Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
name: docker-build

# Reusable BuildKit image build whose layer cache outlives the runner.
#
# Disposable runners rebuild every layer from scratch unless the cache lives
# somewhere durable. This workflow puts it in a registry --
# `{image}/buildcache` by default, which on ghcr.io is currently free storage
# -- so hosted and self-hosted runners share one cache across runs and
# repositories. `cache: gha` selects the GitHub Actions cache backend instead
# (needs Docker Engine >= 28 / Buildx >= 0.21, both satisfied by
# ubuntu-latest and the nddev images); `cache: none` disables caching.
#
# Runner contract: any Linux runner with a Docker daemon -- ubuntu-latest or
# a docker-capable nddev class (nddev-linux-integration and friends). Pushing
# to ghcr.io authenticates with the ambient workflow token, which needs
# `packages: write` in the caller when push or a ghcr cache ref is used.

on:
workflow_call:
inputs:
runner:
description: 'Linux runner label with a Docker daemon.'
type: string
default: 'ubuntu-latest'
image:
description: 'Image repository without a tag, e.g. ghcr.io/acme/app.'
type: string
required: true
tags:
description: 'Comma-separated tags; empty tags the image with the commit SHA.'
type: string
default: ''
context:
description: 'Build context path inside the repository.'
type: string
default: '.'
dockerfile:
description: 'Dockerfile path relative to the repository root.'
type: string
default: 'Dockerfile'
platforms:
description: 'Target platforms.'
type: string
default: 'linux/amd64'
push:
description: 'Push the built image and tags to the registry.'
type: boolean
default: false
cache:
description: 'Layer cache backend: registry, gha, or none.'
type: string
default: 'registry'
cache_ref:
description: 'Registry cache ref; empty derives {image}/buildcache.'
type: string
default: ''
build_args:
description: 'Newline-separated KEY=value build arguments.'
type: string
default: ''
timeout_minutes:
type: number
default: 30
outputs:
digest:
description: 'Content digest of the built image.'
value: ${{ jobs.build.outputs.digest }}

permissions: {}

jobs:
build:
runs-on: ${{ inputs.runner }}
timeout-minutes: ${{ inputs.timeout_minutes }}
permissions:
# Checkout of the caller repository only; no writes back to it.
contents: read
# ghcr.io push and the registry layer cache authenticate with the
# ambient token; both write to the caller's own package namespace.
packages: write
Comment thread
rldyourmnd marked this conversation as resolved.
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Validate immutable inputs
env:
IMAGE: ${{ inputs.image }}
TAGS: ${{ inputs.tags }}
CONTEXT: ${{ inputs.context }}
DOCKERFILE: ${{ inputs.dockerfile }}
CACHE: ${{ inputs.cache }}
CACHE_REF: ${{ inputs.cache_ref }}
run: |
set -euo pipefail
[[ "$IMAGE" =~ ^[a-z0-9][a-z0-9._/-]*$ ]]
[[ -z "$TAGS" || "$TAGS" =~ ^[A-Za-z0-9._,-]+$ ]]
for value in "$CONTEXT" "$DOCKERFILE"; do
[[ "$value" =~ ^[A-Za-z0-9._/-]+$ && "$value" != /* && "$value" != *..* ]]
done
[[ "$CACHE" == registry || "$CACHE" == gha || "$CACHE" == none ]]
[[ -z "$CACHE_REF" || "$CACHE_REF" =~ ^[a-z0-9][a-z0-9._/-]*$ ]]

- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Resolve tags and cache references
id: refs
env:
IMAGE: ${{ inputs.image }}
TAGS: ${{ inputs.tags }}
CACHE: ${{ inputs.cache }}
CACHE_REF: ${{ inputs.cache_ref }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
if [ -n "$TAGS" ]; then
full=""
IFS=',' read -ra parts <<< "$TAGS"
for tag in "${parts[@]}"; do full="${full:+$full,}$IMAGE:$tag"; done
else
full="$IMAGE:$SHA"
fi
printf 'tags=%s\n' "$full" >> "$GITHUB_OUTPUT"
case "$CACHE" in
registry)
ref="${CACHE_REF:-$IMAGE/buildcache}"
printf 'cache_from=type=registry,ref=%s\n' "$ref" >> "$GITHUB_OUTPUT"
printf 'cache_to=type=registry,ref=%s,mode=max,image-manifest=true,oci-mediatypes=true\n' "$ref" >> "$GITHUB_OUTPUT"
;;
gha)
printf 'cache_from=type=gha\n' >> "$GITHUB_OUTPUT"
printf 'cache_to=type=gha,mode=max\n' >> "$GITHUB_OUTPUT"
;;
none)
printf 'cache_from=\n' >> "$GITHUB_OUTPUT"
printf 'cache_to=\n' >> "$GITHUB_OUTPUT"
;;
esac

- name: Log in to ghcr.io
if: ${{ inputs.push || (inputs.cache == 'registry' && startsWith(inputs.image, 'ghcr.io/')) }}
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}

- name: Set up BuildKit
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0

- name: Build and push
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: ${{ inputs.context }}
file: ${{ inputs.dockerfile }}
platforms: ${{ inputs.platforms }}
push: ${{ inputs.push }}
tags: ${{ steps.refs.outputs.tags }}
cache-from: ${{ steps.refs.outputs.cache_from }}
cache-to: ${{ steps.refs.outputs.cache_to }}
build-args: ${{ inputs.build_args }}
8 changes: 8 additions & 0 deletions catalog/cache-contract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ producers:
upstream_default: null
default_caches: true
note: Explicit cache; the key is written by the calling workflow.
- action: docker/build-push-action
control: cache-to
upstream_default: null
default_caches: false
note: >-
Caches only when docker-build.yml passes cache-from/cache-to, and the
workflow computes them from its `cache` input: a registry ref
({image}/buildcache by default) or type=gha. No input means no cache.
- action: astral-sh/setup-uv
control: enable-cache
upstream_default: auto
Expand Down
26 changes: 26 additions & 0 deletions catalog/capabilities.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,32 @@ capabilities:
- "https://github.com/aquasecurity/trivy"
- "https://github.com/aquasecurity/trivy-action"

- id: docker-build-registry-cache
name: Docker build with a registry layer cache
cluster: actions-core
status: ga
public_oss: free
private_free: free
private_paid: available
workflow: .github/workflows/docker-build.yml
# build-push-action shells out to the Docker daemon through BuildKit.
runtime_requirements: ["container-runtime"]
example: examples/infra/docker-build.yml
required_permissions:
- "contents: read"
- "packages: write"
required_settings:
- "ghcr.io push and registry cache authenticate with the ambient workflow token"
risks:
- "Workflow is present on disk and validated by generated workflow inventory"
- "Registry cache refs accumulate untagged manifests; clean them with a retention policy"
- "type=gha cache needs Docker Engine >= 28 and Buildx >= 0.21 on the runner"
deprecations: null
last_verified: "2026-09-01"
sources:
- "https://github.com/docker/build-push-action"
- "https://docs.docker.com/build/cache/backends/registry/"

- id: terraform-ci
name: Terraform CI
cluster: actions-core
Expand Down
41 changes: 36 additions & 5 deletions catalog/tools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,14 @@ tools:
- .github/workflows/coverage-gate.yml
- .github/workflows/cpp-ci.yml
- .github/workflows/cross-platform-smoke.yml
- .github/workflows/dependabot-catalog-convergence.yml
- .github/workflows/dart-flutter-ci.yml
- .github/workflows/dependabot-catalog-convergence.yml
- .github/workflows/docker-build.yml
- .github/workflows/docs-ci.yml
- .github/workflows/docs-quality.yml
- .github/workflows/gds-anchor-contract.yml
- .github/workflows/dotnet-ci.yml
- .github/workflows/fuzzing.yml
- .github/workflows/gds-anchor-contract.yml
- .github/workflows/go-ci.yml
- .github/workflows/grype-scan.yml
- .github/workflows/hadolint-ci.yml
Expand All @@ -197,26 +198,26 @@ tools:
- .github/workflows/maintenance.yml
- .github/workflows/monorepo-changed-paths.yml
- .github/workflows/mutation-testing.yml
- .github/workflows/nddev-security-bundle.yml
- .github/workflows/node-ci.yml
- .github/workflows/osv-scan.yml
- .github/workflows/pr-hygiene.yml
- .github/workflows/nddev-security-bundle.yml
- .github/workflows/private-security-bundle-free.yml
- .github/workflows/private-static.yml
- .github/workflows/public-codeql.yml
- .github/workflows/public-dependency-review.yml
- .github/workflows/public-scorecard-json.yml
- .github/workflows/public-scorecard-analysis.yml
- .github/workflows/public-scorecard-json.yml
- .github/workflows/public-scorecard.yml
- .github/workflows/python-ci.yml
- .github/workflows/qt-ci.yml
- .github/workflows/r-ci.yml
- .github/workflows/release-supply-chain-free.yml
- .github/workflows/release-supply-chain.yml
- .github/workflows/release.yml
- .github/workflows/runtime-fixtures.yml
- .github/workflows/runtime-fixtures-event-write.yml
- .github/workflows/runtime-fixtures-languages.yml
- .github/workflows/runtime-fixtures.yml
- .github/workflows/rust-ci.yml
- .github/workflows/rust-supply-chain.yml
- .github/workflows/secret-scan.yml
Expand Down Expand Up @@ -618,6 +619,36 @@ tools:
- .github/workflows/container-ci.yml
last_verified: "2026-07-12"

- id: docker-login-action
name: docker/login-action
homepage: "https://github.com/docker/login-action"
kind: action
current_version: "v4.6.0"
pin: "docker/login-action@dbcb813823bdd20940b903addbd779551569679f"
used_by:
- .github/workflows/docker-build.yml
last_verified: "2026-09-01"

- id: docker-setup-buildx-action
name: docker/setup-buildx-action
homepage: "https://github.com/docker/setup-buildx-action"
kind: action
current_version: "v4.3.0"
pin: "docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e"
used_by:
- .github/workflows/docker-build.yml
last_verified: "2026-09-01"

- id: docker-build-push-action
name: docker/build-push-action
homepage: "https://github.com/docker/build-push-action"
kind: action
current_version: "v7.3.0"
pin: "docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a"
used_by:
- .github/workflows/docker-build.yml
last_verified: "2026-09-01"

# Registered late: these three workflows (rust-supply-chain, clusterfuzzlite)
# shipped without catalog entries because the pin validators check pin format,
# never catalog membership. scripts/check_tool_registry.py now derives the
Expand Down
1 change: 1 addition & 0 deletions catalog/workflow-routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ groups:
runtime_requirements: [container-runtime]
workflows:
- .github/workflows/container-ci.yml
- .github/workflows/docker-build.yml
- .github/workflows/secret-scan.yml
- id: linux-shell
supported_os: [linux]
Expand Down
3 changes: 2 additions & 1 deletion docs/generated/capability-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
| C/C++ CI (`cpp-ci`) | actions-core | ga | free | conditional | available | `.github/workflows/cpp-ci.yml` | `examples/languages/cpp.yml` |
| Cross-platform smoke test (`cross-platform-smoke`) | actions-core | ga | free | free | available | `.github/workflows/cross-platform-smoke.yml` | `examples/infra/cross-platform.yml` |
| Dart/Flutter CI (`dart-flutter-ci`) | actions-core | ga | free | conditional | available | `.github/workflows/dart-flutter-ci.yml` | `examples/languages/dart-flutter.yml` |
| Docker build with a registry layer cache (`docker-build-registry-cache`) | actions-core | ga | free | free | available | `.github/workflows/docker-build.yml` | `examples/infra/docker-build.yml` |
| Docs CI (`docs-ci`) | actions-core | ga | free | conditional | available | `.github/workflows/docs-ci.yml` | `examples/infra/docs.yml` |
| Docs quality (links, spelling, markdown) (`docs-quality`) | actions-core | ga | free | free | available | `.github/workflows/docs-quality.yml` | `examples/quality/docs-quality.yml` |
| .NET CI (`dotnet-ci`) | actions-core | ga | free | conditional | available | `.github/workflows/dotnet-ci.yml` | `examples/languages/dotnet.yml` |
Expand Down Expand Up @@ -83,4 +84,4 @@
| SLSA build provenance (`slsa-build-provenance`) | supply-chain | ga | free | paid | conditional | `.github/workflows/release-supply-chain.yml` | `-` |

---
Source data verified through: 2026-08-24
Source data verified through: 2026-09-01
8 changes: 4 additions & 4 deletions docs/generated/profile-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ catalogs, so prose cannot drift away from it.

| Profile | Visibility | Base plan | C/S/Q | Compute billing | Licence billing | CodeQL | Runner | Provenance | Enforcement | Fixed cost | Programme |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `public-free-standalone` | public | free, pro, team, enterprise-cloud | `000` | public-standard-unmetered | no-paid-addons | default | github-hosted-standard | attestations | active | free | 65 run / 7 conditional / 6 unavailable |
| `private-free-max` | private | free, pro, team | `000` | private-self-hosted | no-paid-addons | none | self-hosted-ephemeral | checksums | active | free | 30 run / 29 conditional / 19 unavailable |
| `public-enterprise-max` | public | enterprise-cloud | `111` | public-standard-unmetered | selected-addons | default | github-hosted-standard | attestations | active | free | 66 run / 7 conditional / 5 unavailable |
| `enterprise-full-private-fixed80` | private, internal | enterprise-cloud | `111` | private-self-hosted | fixed-license-envelope | default | self-hosted-ephemeral | attestations | active | $80/month | 38 run / 35 conditional / 5 unavailable |
| `public-free-standalone` | public | free, pro, team, enterprise-cloud | `000` | public-standard-unmetered | no-paid-addons | default | github-hosted-standard | attestations | active | free | 66 run / 7 conditional / 6 unavailable |
| `private-free-max` | private | free, pro, team | `000` | private-self-hosted | no-paid-addons | none | self-hosted-ephemeral | checksums | active | free | 31 run / 29 conditional / 19 unavailable |
| `public-enterprise-max` | public | enterprise-cloud | `111` | public-standard-unmetered | selected-addons | default | github-hosted-standard | attestations | active | free | 67 run / 7 conditional / 5 unavailable |
| `enterprise-full-private-fixed80` | private, internal | enterprise-cloud | `111` | private-self-hosted | fixed-license-envelope | default | self-hosted-ephemeral | attestations | active | $80/month | 39 run / 35 conditional / 5 unavailable |

## Entitlement combinations

Expand Down
3 changes: 2 additions & 1 deletion docs/generated/workflow-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
| `.github/workflows/dart-flutter-ci.yml` | `dart-flutter-ci` | ga |
| `.github/workflows/dependabot-catalog-convergence.yml` | internal | internal |
| `.github/workflows/dependency-review.yml` | internal | internal |
| `.github/workflows/docker-build.yml` | `docker-build-registry-cache` | ga |
| `.github/workflows/docs-ci.yml` | `docs-ci` | ga |
| `.github/workflows/docs-quality.yml` | `docs-quality` | ga |
| `.github/workflows/dotnet-ci.yml` | `dotnet-ci` | ga |
Expand Down Expand Up @@ -67,4 +68,4 @@
| `.github/workflows/zizmor-sarif.yml` | `zizmor` | ga |

---
Source data verified through: 2026-08-24
Source data verified through: 2026-09-01
1 change: 1 addition & 0 deletions docs/generated/workflow-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ an operating system this table does not list for it.
| `.github/workflows/cpp-ci.yml` | `linux-shell` | linux | shell only |
| `.github/workflows/cross-platform-smoke.yml` | `portable-shell` | linux, macos, windows | shell only |
| `.github/workflows/dart-flutter-ci.yml` | `linux-shell` | linux | shell only |
| `.github/workflows/docker-build.yml` | `linux-container` | linux | container-runtime |
| `.github/workflows/docs-ci.yml` | `linux-shell` | linux | shell only |
| `.github/workflows/docs-quality.yml` | `linux-shell` | linux | shell only |
| `.github/workflows/dotnet-ci.yml` | `portable-shell` | linux, macos, windows | shell only |
Expand Down
24 changes: 24 additions & 0 deletions examples/infra/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Build a container image with a layer cache that survives disposable
# runners. The registry cache defaults to {image}/buildcache -- on ghcr.io
# that storage is currently free -- so every run starts from the layers the
# last one built, on hosted and self-hosted runners alike.
name: docker-build

on:
push:
branches: [main]

permissions: {}

jobs:
image:
permissions:
contents: read
packages: write
uses: NDDev-OpenNetwork/ci-workflows/.github/workflows/docker-build.yml@<sha>
with:
runner: ubuntu-latest
image: ghcr.io/acme/app
push: true
# cache: registry (default) shares {image}/buildcache across runs;
# switch to gha for the Actions cache backend, or none.
Loading