This repository uses GitHub Actions for two distinct purposes that must never be confused with each other: mirroring upstream base images into GitHub Container Registry (GHCR), and building the demo applications on top of those bases. The naming convention below keeps the two categories clearly separated, both in the file system and in the Actions UI.
| Category | Purpose | Workflow file | Display name: |
Concurrency group |
|---|---|---|---|---|
| Mirror | Copy / refresh a base image from an upstream registry into GHCR | mirror-<image>.yml |
mirror / quarantine/<image> |
mirror-quarantine-<image> |
| Promote from quarantine | Scan a quarantined image and promote it into its golden/base repository (golden/<image>, or base/... for base/hardened images) when it passes the vulnerability policy |
promote-from-quarantine-<image>.yml |
promote from quarantine / quarantine/<image> |
promote-from-quarantine-<image> |
| Build | Build an application image on top of a mirrored base | build-<app>.yml |
build / <app> |
build-<app> |
| Promote override | Act on a maintainer's approve/deny of a blocked image (issue-comment or dispatch) | promote-override.yml |
promote override |
promote-override-<issue> |
| Report | Watch other workflows and report CI failures as tracking issues | report-<purpose>.yml |
report / <purpose> |
report-<purpose> |
| Reusable | Shared logic invoked by other workflows; never triggered directly | _<purpose>.yml (leading underscore) |
_reusable / <purpose> |
n/a |
| Composite action | A single reusable step shared across workflows | .github/actions/<verb-noun>/action.yml |
name: <verb-noun> |
n/a |
- Verb prefix. Every workflow filename starts with a category verb:
mirror-,promote-from-quarantine-,promote-override,build-,report-, or a leading underscore (_) for reusable workflows. This groups related workflows together alphabetically and makes intent obvious at a glance. - Leading underscore = internal. Reusable workflows (triggered by
workflow_call) are prefixed with_so they sort to the top of the list and signal "do not run me directly." - Display names use a
category / subjectformat (for examplemirror / quarantine/python) so the Actions sidebar reads cleanly. - Concurrency groups mirror the filename so two runs of the same logical job never overlap, while different images/apps run independently.
- GHCR destination repositories for mirrored base images follow the
quarantine/<image>scheme, e.g.ghcr.io/<owner>/quarantine/python. Images promoted out of quarantine by a promote-from-quarantine workflow follow thegolden/<image>scheme, e.g.ghcr.io/<owner>/golden/python, except for base/hardened images which are promoted into thebase/...namespace, e.g.ghcr.io/<owner>/base/nodeorghcr.io/<owner>/base/hardened/python.
Mirror workflows keep a copy of an upstream base image fresh in GHCR.
- Structure. Logic lives in a single reusable workflow,
_mirror-image.yml, which is assembled from composite actions under.github/actions/(see workflow actions). Each image has a thin caller, e.g.mirror-python.yml, that only declares triggers and the image-specific inputs and calls the reusable workflow viauses:. - Idempotent sync. On every run the workflow compares the digest of the
source tag against the digest already in GHCR and only copies when they
differ.
crane copypreserves multi-architecture manifest lists. - Triggers. A daily
schedule(06:00 UTC) plusworkflow_dispatchwith an optionalforceinput to copy even when digests match. - Auth. GHCR is accessed with the built-in
GITHUB_TOKEN(packages: write). Public Docker Hub sources are pulled anonymously.
- Copy
mirror-python.ymltomirror-<image>.yml. - Update the display
name:, theconcurrency.group, and the four inputs (source_image,source_tag,dest_image,dest_tag). - No logic changes are needed — the reusable workflow does the work.
Promote-from-quarantine workflows gate images out of quarantine. They scan every
tag in a
quarantine/<image> repository with Trivy and promote the images that pass a
configurable severity threshold (plus an optional CVE exception list) into a
golden/<image> repository (or a base/... repository for base/hardened
images).
- Structure. Logic lives in a single reusable workflow,
_promote-from-quarantine.yml, which is assembled from composite actions under.github/actions/(see workflow actions) and fans the work out across a job matrix (one job per tag). Each scanned repository has a thin caller, e.g.promote-from-quarantine-python.yml, that only declares triggers and the repository-specific inputs and calls the reusable workflow viauses:. - Gate + promote. Passing images are copied with
crane copy, get an empty OCI scan-report referrer attached withoras attach, and are then deleted from quarantine. Blocked images stay in quarantine. - Triggers. A daily
schedule(07:00 UTC, after the 06:00 mirror) plusworkflow_dispatchwith overrides for the threshold, exceptions, and adry_runmode. - Auth. GHCR scan/copy/attach use the built-in
GITHUB_TOKEN(packages: write). Deleting quarantine tags needs a separatedelete:packagesPAT (see the promote-from-quarantine architecture).
- Copy
promote-from-quarantine-python.ymltopromote-from-quarantine-<image>.yml. - Update the display
name:, theconcurrency.group, and the inputs (source_repo,dest_repo, and any threshold/exception overrides). - No logic changes are needed — the reusable workflow does the work.
Build workflows (added later) build the demo applications under apps/ on top
of the mirrored base images. They use the build-<app>.yml filename and the
build / <app> display name so they remain clearly separate from mirror
workflows.
Report workflows watch other workflows and turn CI failures into visible,
de-duplicated tracking issues. There is one repository-wide monitor,
report-ci-failure.yml, that
subscribes to the mirror, promote-from-quarantine, and promote-override workflows
via the workflow_run event and opens (or, on recovery, closes) a
CI failure: <workflow> issue. It uses the report-<purpose>.yml filename and
the report / <subject> display name. See the
CI failure notifications design.
The reusable steps shared across workflows live as composite actions under
.github/actions/, one directory per action with an
action.yml inside. They are the single source of truth for each operation
(login, enumerate, copy, scan, gate, attach, delete); the reusable workflows
only orchestrate them.
- One verb-noun per action. Action directory and
name:use the standard terminology verb-noun, e.g.registry-login,enumerate-tags,mirror-image,scan-image,scan-sbom,evaluate-findings,attach-scan-report,delete-image. The canonical glossary lives in workflow actions and terminology. - kebab-case for action directory names, inputs, and outputs.
- Single responsibility. An action does one thing on one image/repository; looping over many tags is the orchestrating workflow's job (via a matrix).
- No tool setup inside actions. Actions assume the calling job has already
checked out the repo and installed the CLIs they need (
crane,oras,trivy); this lets several actions share one setup. - Document every action in workflow actions and terminology when it is added or its interface changes.