How VPC Atlas is built and released¶
Everything that builds, tests, scans and publishes VPC Atlas runs in public GitHub Actions workflows, in the repository. The runs are on the Actions page. This page lists what each workflow guards, the choices that keep the supply chain small, how to verify every artifact you download, and how to run the same checks on your own machine. For the contributor side (setup, pull requests, releasing) see Contributing.
The workflows¶
There are five workflows. Each one has a narrow job, the least permissions it needs, and a timeout.
ci¶
Runs on every pull request, on every push to main and by hand. A newer push to the same branch cancels the older
run. These jobs are the required checks for merging.
| Job | What it guards | When |
|---|---|---|
| Lint | ruff check, ruff format --check, actionlint and zizmor over the workflow files. |
Every run |
| Type check | mypy over src/vpcatlas. A type error fails the run. |
Every run |
| Tests | The full suite on Linux (Python 3.12 and 3.13), macOS and Windows. The Linux 3.12 leg also enforces 88% coverage and writes the least covered files to the job summary. | Every run |
| Docs | mkdocs build --strict: a broken link or a page missing from the nav fails. |
Every run |
| Python package | The sdist and wheel build from the hash-pinned build lock, pass twine check and carry the dashboard. |
Every run |
| Images | The dashboard and Lambda images build; the dashboard answers /healthz; the Lambda image answers an HTTP API event through the Runtime Interface Emulator; Trivy fails on a fixable critical CVE and reports the rest to the Security tab. |
Every run |
| Helm chart | helm lint --strict and helm template with the default values and with every option on; then the chart is installed in a kind cluster with the image built from the commit, passes helm test and answers /healthz and an authenticated page through a port-forward, and is upgraded with every option on. |
Every run |
| Installer | install.sh (Linux, macOS) and install.ps1 (Windows) install a freshly built archive, run vpcatlas --version, install into another directory, and refuse a wrong checksum without leaving files behind. |
Every run |
| Dependency review | A change that adds a dependency with a known high-severity advisory is blocked, with a comment on the pull request. | Pull requests |
release¶
Runs when a tag vX.Y.Z is pushed. Started by hand it only tests and builds, as a rehearsal: nothing is published.
| Job | What it guards or does | When |
|---|---|---|
| tests | Lint and test again on the tagged commit, and check that the tag, pyproject.toml, src/vpcatlas/__init__.py and deploy/helm/vpcatlas/Chart.yaml agree on the version. |
Tag |
| binaries | Builds the program for Linux, macOS (x86_64 and arm64) and Windows, smoke-tests each one and measures its startup. The Linux x86_64 leg also writes the SBOM. | Tag |
| package | Builds the sdist and the wheel. | Tag |
| release | Writes SHA256SUMS, attests every archive, the checksum file and the SBOM, attests the SBOM against the Linux archive, and creates the GitHub Release with a verify section in its notes. |
Tag |
| docker | Builds the dashboard image for amd64 and arm64, smoke-tests it, fails on a fixable critical CVE before anything is pushed, then pushes, signs with cosign and attests. | Tag |
| lambda | The same for the Lambda image, one per architecture. | Tag |
| chart | Packages the Helm chart, pushes it to ghcr.io/karina-pp/charts/vpcatlas, signs it with cosign and attests it, pulls it back to verify the signature and the attestation, and attaches the .tgz to the GitHub Release. |
Tag |
| pypi | Publishes the sdist and wheel through trusted publishing. Off until the project is registered on PyPI. | Tag |
| verify | Installs the published release the way a user does (the live install.sh and install.ps1, with the version pinned) on Linux, macOS and Windows, then checks the image attestation and signature. |
Tag |
codeql¶
CodeQL analysis of the Python source with the security-extended queries. Runs on pull requests, on main and every
Monday. Results are in the Security tab.
scorecard¶
OpenSSF Scorecard grades the repository on pinned dependencies, token permissions, branch protection, signed
releases and similar practices. Runs on main and every Monday, and publishes the result, which feeds the badge in
the README.
nightly¶
Checks that can fail without a code change. Runs every day at 05:00 UTC and by hand.
| Job | What it watches |
|---|---|
| Dependency audit | pip-audit over the installed environment: a new advisory for a dependency fails the job. |
| Links | lychee over the README, the docs and the changelog: a dead link fails the job. |
| Layout benchmark | scripts/bench_layout.py on 77 and 5 APIs, full and simple detail. A report in the job summary and an artifact; it does not fail. |
| Published image | Trivy over vpcatlas/vpcatlas:latest from Docker Hub. Fixable high and critical findings go to the Security tab; it does not fail. |
| Open or update the issue | If anything above failed, opens one issue titled "Nightly checks failed" (label nightly), or comments on the open one. |
Dependabot also opens weekly pull requests: one for all GitHub Actions, one for the Python minor and patch updates, and one per Docker base image.
Supply chain decisions¶
- Pinned by content. Every action is pinned to a full commit SHA with its version in a comment, and the base
images and Python requirements are pinned by digest or hash (
requirements.lockis installed with--require-hashes). A tag can be moved after the fact; a SHA cannot. Dependabot proposes the bumps. - Least privilege. Every workflow starts with
permissions: contents: read; a job asks for more only for what it does (security-events: writeto upload findings,id-token: writeto sign,attestations: writeto attest). Checkouts do not keep the token in the working copy. - No long-lived tokens where an identity will do. Signing and attestations use the short-lived OIDC identity of the workflow run, and PyPI uses trusted publishing. The one stored secret is the Docker Hub token, because Docker Hub does not accept OIDC from GitHub.
- No caches in publishing jobs. The jobs that build what you download do not restore build caches, so a poisoned cache cannot reach a release.
- Workflows are linted.
actionlintandzizmorrun on every change; a finding fails the build, and the zizmor findings also go to the Security tab. - Provenance for everything published. Archives,
SHA256SUMS, the SBOM and both image families carry a signed SLSA build provenance attestation, and the images are also signed with cosign. - An SBOM per release. A CycloneDX file lists what is inside the Linux x86_64 build, and is attested against that archive.
- Scanned before and after. Trivy gates the image before it is pushed; CodeQL, Scorecard,
pip-auditand the nightly scan of the published image watch what is already out. - The release is tested as a user meets it. After publishing, the
verifyjob installs from the live installers and checks the signatures.
Verify what you install¶
Images and SBOMs are signed and attested from 0.4.1 on, the first release made by this pipeline; earlier releases
carry only the attestations of the archives and SHA256SUMS. Verifying the image signature needs cosign 3 or newer.
You need the GitHub CLI for the attestations and cosign
for the image signatures. Replace X.Y.Z with the version.
An archive (use .zip on Windows, and your own OS and architecture):
gh attestation verify vpcatlas-X.Y.Z-linux-x86_64.tar.gz --owner karina-pp
The checksum file, and the archive against it:
gh attestation verify SHA256SUMS --owner karina-pp
sha256sum --check --ignore-missing SHA256SUMS # macOS: shasum -a 256 --check --ignore-missing SHA256SUMS
The dashboard image, by attestation and by signature:
gh attestation verify oci://index.docker.io/vpcatlas/vpcatlas:X.Y.Z --owner karina-pp
cosign verify index.docker.io/vpcatlas/vpcatlas:X.Y.Z \
--certificate-identity-regexp '^https://github.com/karina-pp/vpcatlas/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
The Lambda image is verified the same way, with the name vpcatlas/vpcatlas-lambda and the tag X.Y.Z-<arch>.
The SBOM, which is vpcatlas-X.Y.Z-sbom.cdx.json in the release assets: its own provenance, and the SBOM attestation
of the Linux x86_64 archive.
gh attestation verify vpcatlas-X.Y.Z-sbom.cdx.json --owner karina-pp
gh attestation verify vpcatlas-X.Y.Z-linux-x86_64.tar.gz --owner karina-pp --predicate-type https://cyclonedx.org/bom
The installers run the archive checks for you; VPCATLAS_SKIP_ATTESTATION=1 turns the attestation check off. See
Install.
Reproduce the checks locally¶
python -m pip install -e ".[dev,docs]"
python -m ruff check . && python -m ruff format --check . # Lint
python -m mypy # Type check
python -m pytest --cov=vpcatlas --cov-fail-under=88 # Tests with the coverage gate
python -m mkdocs build --strict # Docs
python -m pip install --require-hashes -r requirements-release.lock # build and twine, pinned as in CI
python -m build && python -m twine check dist/* # Python package
docker build -f deploy/Dockerfile -t vpcatlas:ci . # Images
docker build -f deploy/Dockerfile.lambda -t vpcatlas-lambda:ci .
bash scripts/build-binary.sh # Binary and archive
python scripts/bench_layout.py --apis 77 --detail simple # Layout benchmark
The workflow linters run from their own tools: actionlint and zizmor .github/workflows (install them with your
package manager or pip install zizmor). The Lambda smoke test with the emulator is written out in
Contributing.
Demo assets¶
The recordings and the diagram images in the README and on the home page (docs/assets/demo.gif,
demo-inventory.gif, demo-diagram.gif, region-eu-west-1.png, vpc-prod.png) are produced by scripts/demo.sh. It starts a simulated AWS account with
moto, behind a small shim in demo/, and runs the real CLI against it, so nothing touches a real account. Then:
- VHS plays every tape in
demo/and writes one GIF per tape; - draw.io exports the region and VPC pages to PNG.
The dashboard image (dashboard.png) is not made by the script. A maintainer captures it by hand, in a browser,
from vpcatlas serve running against the same simulated account.
Regenerate them when the output of vpcatlas discover or the dashboard changes, and commit the new files with
that change. The ids in the images are examples from the simulated account.