Standardized development workflows and continuous integration pipelines using
[TOC]
Ensure you have bash, docker and pre-commit installed:
brew install bash docker pre-commit
Create a .pre-commit-config.yaml in your project:
This is the recommended hook configuration for containerized python apps.
Check out the pre-commit section for details and options.
repos:
- repo: https://gitlab.com/flywheel-io/tools/etc/qa-ci
rev: main
hooks:
- id: eolfix
- id: hadolint
- id: helmcheck
- id: jsonlint
- id: linkcheck
- id: markdownlint
- id: poetry_export
- id: ruff
- id: ruff_format
- id: ruff_tests
- id: shellcheck
- id: yamllint
Create a .gitlab-ci.yml in your project:
This is the recommended CI configuration for containerized python apps.
Check out the GitLab CI section for details and options.
include:
- project: flywheel-io/tools/etc/qa-ci
ref: main
file: ci/app.yml
Update the qa-ci reference in both files from main to a non-mutable rev:
The pre-commit and the gitlab-ci revs should always be pinned and in sync.
pre-commit try-repo https://gitlab.com/flywheel-io/tools/etc/qa-ci autoupdate
Profit! Your project can now be tested using pre-commit and CI is configured to run the same tests and to publish any project artifacts as needed.
pre-commit run -a # run all the hooks
pre-commit install # auto-run hooks before you commit
Hooks help identifying issues before submission to code review. Running hooks before every commit allows pointing out problems like invalid YAML syntax, non-standard code formatting or broken tests. Fixing these before code review allows reviewers to focus on the architecture of the change while not wasting time on style nitpicks or worrying about whether the tests pass.
Using pre-commit provides a single, unified entry point for all building, linting
and testing activities related to a project's development lifecycle, all driven
from .pre-commit-config.yaml. Running the same command to invoke the same or
similar tasks lowers friction when switching between projects and facilitates
code standardization as well as CI automation.
Hooks are configured with .pre-commit-config.yaml.
Projects using the py/docker/helm stack should use the recommended hooks
listed in the Usage section above and documented in detail in the
Supported hooks section below.
Hooks come with a recommended configuration to aid in code standardization across projects. You can override the defaults by passing your own hook args:
hooks:
- id: ruff
args: [--select, "PL"] # override qa-ci's default
You can inspect a CLI tools' help text to discover their available args:
docker run -it --rm flywheel/qa-ci ruff --help
Hooks can be disabled by commenting them out, which is useful for adopting them gradually in existing projects:
hooks:
# - id: shellcheck # disable shellcheck by commenting it out
Hooks can be extended with 3rd party hooks and custom local entries:
# add 3rd party hook after the qa-ci ones:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: trailing-whitespace
Finally, hooks can be removed if they are not relevant to your project.
This section describes the qa-ci hooks' behavior and configuration in detail.
You can inspect the supported hook entries and their default arguments in
.pre-commit-hooks.yaml.
Qa-ci hooks are containerized to minimize dev environment setup time and to
increase reproducibility across developer machines and CI runners.
Hooks may generate new files or reformat existing files in place. Eg.: ruff format
may change whitespace in python code.
When this happens to tracked files, pre-commit will consider that hook failed.
To fix this, simply commit that diff and re-run the hook.
Fix text files (i.e.: any source code) to enforce LF line endings and to ensure
a single LF at EOF (no more, no less).
Links:
Lint Dockerfile to enforce best practices with hadolint. Uses shellcheck under
the hood to lint the RUN instructions as well.
Links:
Document, lint, test and security-scan helm charts with a custom qa-ci script
helmcheck.sh. Runs helm-docs to generate the chart README, then renders the
templates with test values to run yamllint, helm lint, kubeconform and kubesec on
the rendered YAMLs.
Test values files are useful for templating charts that have required values
and/or test-covering the templating logic on various if/else branches.
Custom test values can be placed in the helm/<project>/tests/ dir, eg:
helm/my-app/tests/test_nodeport.yaml
If there are no custom tests, the chart will be tested using defaults.
The overrides of skaffold.yaml (if present) are also used for testing.
Links:
Lint JSON files for syntax, uniform indentation and style with jsonlint.
Formats .json files in-place to consistently use 2 spaces for indentation.
Links:
Check text files for dead links with a custom qa-ci script linkcheck.py.
Looks for HTTP links in any text file using regex and attempts to issue a HEAD request on all the URLs found. Ignores links
localhost)local and testhelm.dev.flywheel.io and local.flywheel.io--ignore patterns passed as argumentsWhen checking links, strip URL fragments and consider:
2xx, 401 and 403 as success (assume unauthorized responses are OK)503 as success for gitlab links (which return 503 on private repos)In addition to HTTP URLs, check markdown header / file references.
Links:
Lint markdown files for syntax, line length, style and more with markdownlint.
Re-formats .md files in-place to consistently use indentation and newlines.
.markdownlint-cli2.yaml88 and allow inline HTML like <br/>.Links:
Export python dependencies from pyproject.toml to requirements.txt with poetry.
Python project dependencies should be managed with poetry. Exporting the required packages with their versions pinned into the simpler requirements.txt format allows installing them pip:
pip install -r requirements.txt
This can be useful for writing a Dockerfile without installing poetry for example,
since it's a large, hard-to-containerize utility.
requirements.txt.requirements-dev.txt.all will also be included in requirements-dev.txt.all is required for this hook to work.Links:
Lint python code with static analyzer ruff. Supersedes isort, pylint and pydocstyle.
Links:
Format python code with ruff to ensure uniform whitespace and style.
Links:
Lint python tests using less strict defaults with static analyzer ruff.
Lint shell scripts with the static analyzer shellcheck.
Links:
Lint YAML files for syntax, uniform indentation and style with yamllint. Formats
.yaml files in-place to consistently use 2 spaces as indentation and to enforce
maximum line length 88 - among many other rules.
Links:
https://gitlab.com/flywheel-io/infrastructure/deployments/flywheel-build/build-cluster
GitLab CI stages allow grouping jobs into different stages where jobs in the same stage are executed in parallell but the stages are run one after the other, making their order important.
All jobs defined in CI templates are assigned to a stage. The complete list of stages currently in use across the qa-ci templates are:
stages:
- test # run pre-commit
- publish # push artifacts (docker/helm/poetry/pages)
- release # automatic release mr and repo tagging using the bot
- update # automatic repo updates and external project triggers
Qa-ci comes with full, off-the-shelf pipelines for application components,
standalone docker images and python libraries.
Projects matching any of these use cases should use an off-the-shelf pipeline.
Standardizing CI pipelines lowers friction when switching between projects and
facilitates more and better automation.
For python applications with a Dockerfile and a Helm chart that integrate into
the Flywheel umbrella
chart as a sub-compontent.
Source: ci/app.yml
include:
- project: flywheel-io/tools/etc/qa-ci
ref: main
file: ci/app.yml
For projects with a Dockerfile that are not tied to an application component,
but contain utilities (or just useful layers) that are distributed as a docker
image on Docker Hub under the flywheel repository.
Source: ci/img.yml
include:
- project: flywheel-io/tools/etc/qa-ci
ref: main
file: ci/img.yml
For open-source python libraries published on PyPI and for
private ones on GitLab Package Registry.
Source: ci/lib.yml
include:
- project: flywheel-io/tools/etc/qa-ci
ref: main
file: ci/lib.yml
CI job templates allow selecting and/or customizing your jobs instead of relying on any set of jobs defined in one of the pipeline templates listed above.
To use a job template, include ci/templates.yml and extend it:
include:
- project: flywheel-io/tools/etc/qa-ci
ref: main
file: ci/templates.yml
build:docker:
extends: .build:docker
Build the project's Docker image and push it to the GitLab container registry
as $CI_REGISTRY_IMAGE:$CI_PIPELINE_ID. The registry feature must be enabled
in the project settings General > Visibility, project features, permissions.
Runs on every MR, main branch commit and tag if a Dockerfile exists.
Run the project's pre-commit hooks as configured in the .pre-commit-config.yaml.
The job is pre-configured to support pytest coverage reports.
Pull the build image from the GitLab registry and tag and push it to Docker Hub.
Runs on every MR, main branch commit and tag if a Dockerfile exists.
The images are tagged using the following patterns based on the commit source:
| Source | Pattern | Example | Type |
|---|---|---|---|
| MR | <REF> | FLYW-123-feat | rolling |
| main | latest | latest | rolling |
| main | <REF>.<SHA> | main.d34db33f | immutable |
| tag | <TAG> | 1.2.3 | immutable |
* <REF> is the branch name and <SHA> is the commit SHA.
Configuration:
| Variable | Default | Description |
|---|---|---|
DOCKER_FILE | Dockerfile | Dockerfile name to build |
DOCKER_IMAGE | flywheel/<project> | Image name to tag with |
Build and push the project's helm chart to the
Flywheel ChartMuseum.
Runs on every MR, main branch commit and tag if a helm/*/Chart.yaml exists.
The chart is versioned using the following patterns based on the commit source:
| Source | Pattern | Example | Type |
|---|---|---|---|
| MR | <VER>-<REF> | 1.2.3-FLYW-123-feat | rolling |
| main | <VER>-latest | 1.2.3-latest | rolling |
| main | <VER>-<REF>.<DATE>.<BUILD>+<SHA> | 1.2.3-main.20220101.456+d34db33f | immutable |
| tag | <TAG> | 1.2.3 | immutable |
* <VER> is the last version and <BUILD> is the $CI_PIPELINE_ID.
Configuration:
| Variable | Default | Description |
|---|---|---|
GIT_DEPTH | 100 | No. of commits to fetch from the history for <VER> |
Publish static HTML project documentation from the public/ folder on
GitLab pages.
Runs on every main branch commit and tag if the public/ dir exists.
Configuration:
TODO ...
Publish a python library to PYPI, the Python Package Index.
Create a release-X.Y.Z branch and MR with the project version bumped in all
files referencing it like pyproject.toml and the helm chart.
Runs on main and hotfix branches when manually triggering a pipeline with a
RELEASE=X.Y.Z variable, where X.Y.Z is the desired version.
The MR description will hold a change-log based on the MRs merged since the last
version and the list of JIRA tickets referenced across those changes. If
FW_COMPONENT is set, it will also reference any open umbrella release MRs.
Edit the change-log in the MR description to adjust the upcoming project release docs and check/uncheck any umbrella release MRs to select which to bump.
Configuration:
| Variable | Default | Description |
|---|---|---|
FW_COMPONENT | "" | Set to true to enable umbrella version bumps |
Create an annotated tag.
Runs on main and hotfix branches when a release-X.Y.Z branch is merged.
The new tag will kick off further pipelines for publishing release artifacts.
Create an update-repo branch and MR with common project files auto-updated.
Runs on any branch if UPDATE=true is set.
It's recommended to run updates on the main branch every 2-4 weeks using a
scheduled pipeline
with a cron string like 0 0 * * sun%4 for every 4th Sunday.
| File | Description |
|---|---|
Dockerfile | Update the base image in FROM |
pyproject.toml | Update the build-system and loosen ^0.X pins |
poetry.lock | Update the lock file from pyproject.toml |
requirements.txt | Update requirements files |
.pre-commit-config.yaml | Update pre-commit repo references |
.gitlab-ci.yml | Update gitlab-ci include references |
Configuration:
| Variable | Default | Description |
|---|---|---|
UPDATE_SKIP | "" | Space-separated list of files not to update |
Create a GitLab project release
with the change-log and JIRA tickets from the release MR description.
If FW_COMPONENT is set and any umbrella releases were selected on the release
MR description, then also bump the component version on those.
Runs on tags only.
Content type
Image
Digest
sha256:37d9e8a20…
Size
809.6 MB
Last updated
about 17 hours ago
docker pull flywheel/qa-ci:3.14-sse.7057c2b2.2898475772