Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9894163
ci: refactor wc-build-push for better re-use
rjaegers Oct 10, 2025
1e86543
Merge branch 'main' into ci/refactor-reusable-workflows-for-better-re…
rjaegers Oct 10, 2025
b5a80b6
chore: make runner labels configurable
rjaegers Oct 10, 2025
69de73f
Merge branch 'ci/refactor-reusable-workflows-for-better-re-use' of ht…
rjaegers Oct 10, 2025
316371d
chore: decouple flavor from wc-build-push-test
rjaegers Oct 13, 2025
090dc78
chore: fix issues with image name
rjaegers Oct 13, 2025
0527700
chore: fix duplicate env
rjaegers Oct 13, 2025
5eddcc2
chore: fix more duplicate env's
rjaegers Oct 13, 2025
c651326
chore: fix findings
rjaegers Oct 13, 2025
00e94b1
chore: start with fixing integration tests
rjaegers Oct 13, 2025
fdab30a
chore: split-off image name sanitization
rjaegers Oct 13, 2025
97e0792
chore: fix path to integration tests
rjaegers Oct 13, 2025
66593ba
chore: enable docker credentials for integration test
rjaegers Oct 13, 2025
be55306
ci: change secret names
rjaegers Oct 13, 2025
b6012d6
ci: enable multiple labels for single runner selection
rjaegers Oct 13, 2025
9826dc7
ci: try to fix syntax for fromJson
rjaegers Oct 13, 2025
7e8d786
ci: try to fix runner selection
rjaegers Oct 13, 2025
2f98b13
ci: handle devcontainer metadata properly
rjaegers Oct 13, 2025
8763b1e
ci: don't escape '='
rjaegers Oct 13, 2025
c27b4e9
ci: fix linter feedback
rjaegers Oct 13, 2025
8daad34
ci: fix more runs-on
rjaegers Oct 13, 2025
37b3db0
chore: retry merge-image in bash
rjaegers Oct 13, 2025
ea92892
ci: various fixes and improvements
rjaegers Oct 13, 2025
706a1bb
ci: fix document upload for release
rjaegers Oct 13, 2025
4252f83
Merge branch 'main' into ci/refactor-reusable-workflows-for-better-re…
rjaegers Oct 14, 2025
b319c05
ci: integrate changes from main
rjaegers Oct 14, 2025
b6a1ffe
ci: more refactoring
rjaegers Oct 14, 2025
7bd23e7
ci: fix cancellation and metadata label
rjaegers Oct 14, 2025
9db4d23
ci: fix cancellation
rjaegers Oct 14, 2025
fa94ecc
ci: refactor to multiple re-usable layers
rjaegers Oct 14, 2025
6137048
ci: fix internal scenario
rjaegers Oct 14, 2025
b8cab65
ci: fix permissions
rjaegers Oct 14, 2025
9288a03
ci: fix issues with secrets and refactor further
rjaegers Oct 15, 2025
6c39b6c
ci: fix permissions
rjaegers Oct 15, 2025
c84a3f1
ci: suppress invalid linter finding
rjaegers Oct 15, 2025
275b8af
ci: make acceptance test secrest optional
rjaegers Oct 15, 2025
09677d0
chore: refactor copilot instructions
rjaegers Oct 15, 2025
0e40b83
ci: block network access when not required
rjaegers Oct 15, 2025
5351af7
ci: add acceptance tests and reduce duplication
rjaegers Oct 15, 2025
7db655e
ci: fix release build
rjaegers Oct 15, 2025
96efdac
ci: add requirement and change some naming
rjaegers Oct 15, 2025
343f511
ci: refactor naming
rjaegers Oct 15, 2025
921e1df
ci: fix zizmore findings
rjaegers Oct 15, 2025
e827c6e
ci: more name refactoring
rjaegers Oct 15, 2025
8639486
ci: less emoji is sometimes better
rjaegers Oct 15, 2025
b02bb58
ci: more name juggling
rjaegers Oct 15, 2025
f4c7346
ci: silence step-security warning
rjaegers Oct 15, 2025
827e859
ci: get rid of defaults
rjaegers Oct 15, 2025
27dc924
ci: disable errors for lychee
rjaegers Oct 16, 2025
d79c316
ci: remove xwin cache
rjaegers Oct 16, 2025
9bd6daf
ci: don't use bash-isms to convert to lowercase
rjaegers Oct 16, 2025
69f65d0
ci: make sure we do interpolation
rjaegers Oct 16, 2025
e192a30
ci: heredoc syntax "<<<" also seems to be a bash-ism
rjaegers Oct 16, 2025
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
4 changes: 3 additions & 1 deletion .github/workflows/wc-build-push-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ jobs:
packages: write
pull-requests: write
with:
flavor: ${{ matrix.flavor }}
devcontainer-metadata: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json
dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile
image-name: ${{ github.repository }}-${{ matrix.flavor }}

dependency-review:
runs-on: ubuntu-latest
Expand Down
140 changes: 104 additions & 36 deletions .github/workflows/wc-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,87 @@
on:
workflow_call:
inputs:
flavor:
dockerfile:
description: "Path to the Dockerfile to build"
required: true
type: string
image-name:
description: "Name of the Docker image to build, without registry or tag. E.g. 'my-image' or 'my-org/my-image'"
required: true
type: string
devcontainer-metadata:
description: "Path to a JSON file containing devcontainer metadata to add as a label to the built image"
required: false
type: string
registry:
description: "Docker registry to push built containers to, DOCKER_USERNAME and DOCKER_PASSWORD secrets must be set if not using GitHub Container Registry"
required: false
type: string
default: "ghcr.io"
build-matrix:
description: >-
JSON object passed to fromJson to become the build matrix. Example:
{"runner": ["ubuntu-latest", "ubuntu-24.04-arm"]}
Must include at least a key 'runner' listing GitHub runner labels.
required: false
type: string
default: '{"runner": ["ubuntu-latest", "ubuntu-24.04-arm"]}'
default-runner:
description: "Runner label for the non-build jobs"
required: false
type: string
default: ubuntu-latest
secrets:
DOCKER_USERNAME:
description: "User name for Docker login, if not provided the GitHub actor will be used"
required: false
DOCKER_PASSWORD:
description: "Password or token for Docker login, if not provided the GITHUB_TOKEN will be used"
required: false

permissions:
contents: read

env:
CONTAINER_FLAVOR: ${{ inputs.flavor }}
REGISTRY: ghcr.io
permissions: {}

jobs:
sanitize-inputs:
runs-on: ${{ inputs.default-runner }}
outputs:
image-name: ${{ steps.sanitize-image-name.outputs.sanitized-image-name }}
fully-qualified-image-name: ${{ inputs.registry }}/${{ steps.sanitize-image-name.outputs.sanitized-image-name }}
image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }}
steps:
- name: Sanitize image name
id: sanitize-image-name
env:
IMAGE_NAME: ${{ inputs.image-name }}
run: |
set -Eeuo pipefail
# Split all image name components (on '/') and sanitize each component independently.
# Rules: lowercase; allowed chars a-z0-9._- ; collapse invalid sequences to single '-'; trim leading/trailing '-'.
IFS='/' read -r -a PARTS <<< "$IMAGE_NAME"
SANITIZED_PARTS=()
for PART in "${PARTS[@]}"; do
SANITIZED_PART=$(echo "$PART" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9._-]+/-/g' | sed -E 's/^-+|-+$//g')
if [ -z "$SANITIZED_PART" ]; then
echo "Invalid or empty component after sanitization in image component: '$PART', please correct your image name: '$IMAGE_NAME'" >&2
exit 1
fi
SANITIZED_PARTS+=("$SANITIZED_PART")
done
SANITIZED_IMAGE_NAME=$(IFS='/'; echo "${SANITIZED_PARTS[*]}")
SANITIZED_BASENAME=${SANITIZED_PARTS[-1]}
echo "sanitized-image-name=$SANITIZED_IMAGE_NAME" >> "$GITHUB_OUTPUT"
echo "sanitized-basename=$SANITIZED_BASENAME" >> "$GITHUB_OUTPUT"
build-push:
strategy:
matrix:
runner: ["ubuntu-latest", "ubuntu-24.04-arm"]
matrix: ${{ fromJson(inputs.build-matrix) }}
runs-on: ${{ matrix.runner }}
needs: sanitize-inputs
permissions:
contents: read
packages: write
steps:
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
Expand All @@ -33,16 +96,19 @@
persist-credentials: false
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
env:
USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD || secrets.GITHUB_TOKEN }}
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: ${{ inputs.registry }}
username: ${{ env.USERNAME }}
password: ${{ env.PASSWORD }}
- uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
env:
DOCKER_METADATA_SET_OUTPUT_ENV: false
id: metadata
with:
images: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}
images: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}
# Generate image LABEL for devcontainer.metadata
# the sed expression is a workaround for quotes being eaten in arrays (e.g. ["x", "y", "z"] -> ["x",y,"z"])
- run: echo "metadata=$(jq -cj '[.]' ".devcontainer/${CONTAINER_FLAVOR}/devcontainer-metadata-vscode.json" | sed 's/,"/, "/g')" >> "$GITHUB_OUTPUT"
Expand All @@ -56,9 +122,9 @@
env:
SOURCE_DATE_EPOCH: ${{ steps.devcontainer-epoch.outputs.git-commit-epoch }}
with:
file: .devcontainer/${{ inputs.flavor }}/Dockerfile
file: ${{ inputs.dockerfile }}
push: true
tags: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}
tags: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}
labels: |
${{ steps.metadata.outputs.labels }}
devcontainer.metadata=${{ steps.devcontainer-metadata.outputs.metadata }}
Expand All @@ -75,13 +141,13 @@
RUNNER_TEMP: ${{ runner.temp }}
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: digests-${{ inputs.flavor }}-${{ steps.devcontainer-arch.outputs.arch }}
name: digests-${{ needs.sanitize-inputs.outputs.image-basename }}-${{ steps.devcontainer-arch.outputs.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1

merge-image:
runs-on: ubuntu-latest
runs-on: ${{ inputs.default-runner }}
needs: build-push
permissions:
actions: read
Expand All @@ -103,21 +169,24 @@
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
path: ${{ runner.temp }}/digests
pattern: digests-${{ inputs.flavor }}-*
pattern: digests-${{ needs.sanitize-inputs.outputs.image-basename }}-*
merge-multiple: true
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
env:
USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD || secrets.GITHUB_TOKEN }}
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: ${{ inputs.registry }}
username: ${{ env.USERNAME }}
password: ${{ env.PASSWORD }}
- uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
id: metadata
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
DOCKER_METADATA_SET_OUTPUT_ENV: false
with:
images: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}
images: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}
# Generate Docker tags based on the following events/attributes.
# To prevent unnecessary image builds we simulate the `type=edge` tag
# with `type=raw,value=edge,enable=...` which only enables the tag
Expand All @@ -135,7 +204,7 @@
import json
import subprocess
CONTAINER = f"{os.getenv('REGISTRY')}/{os.getenv('GH_REPO')}-{os.getenv('CONTAINER_FLAVOR')}"
CONTAINER = f"{os.getenv('FULLY_QUALIFIED_IMAGE_NAME')}"
METADATA = json.loads(os.getenv('METADATA_JSON'))
digests = [f for f in os.listdir('.') if f.startswith('sha256:') or len(f) == 64]
Expand All @@ -149,54 +218,53 @@
print(' '.join(command))
subprocess.run(command, check=True)
env:
FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}
METADATA_JSON: ${{ steps.metadata.outputs.json }}
GH_REPO: ${{ github.repository }}
shell: python
working-directory: ${{ runner.temp }}/digests
- name: Inspect manifest and extract digest
id: inspect-manifest
run: |
set -Eeuo pipefail
output=$(docker buildx imagetools inspect "${REGISTRY}/${GH_REPO}-${CONTAINER_FLAVOR}:${CONTAINER_VERSION}" --format '{{json .}}')
output=$(docker buildx imagetools inspect "${CONTAINER}" --format '{{json .}}')
echo "digest=$(echo "$output" | jq -r '.manifest.digest // .manifests[0].digest')" >> "$GITHUB_OUTPUT"
env:
CONTAINER_VERSION: ${{ steps.metadata.outputs.version }}
GH_REPO: ${{ github.repository }}
CONTAINER: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}:${{ steps.metadata.outputs.version }}
- run: |
set -Eeuo pipefail
wget -O diffoci https://github.com/reproducible-containers/diffoci/releases/download/v0.1.7/diffoci-v0.1.7.linux-amd64
chmod +x diffoci
./diffoci diff --semantic --report-file=container-diff.json "${FROM_CONTAINER}" "${TO_CONTAINER}" || true
env:
FROM_CONTAINER: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}:edge
TO_CONTAINER: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}:${{ steps.metadata.outputs.version }}
FROM_CONTAINER: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}:edge
TO_CONTAINER: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}:${{ steps.metadata.outputs.version }}
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: container-diff-${{ inputs.flavor }}
name: container-diff-${{ needs.sanitize-inputs.outputs.image-basedname }}
path: container-diff.json
retention-days: 10
- uses: ./.github/actions/container-size-diff
id: container-size-diff
with:
from-container: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}:edge
to-container: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}:${{ steps.metadata.outputs.version }}
from-container: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}:edge
to-container: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}:${{ steps.metadata.outputs.version }}
- uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4
with:
header: container-size-diff-${{ inputs.flavor }}
header: container-size-diff-${{ needs.sanitize-inputs.outputs.image-basename }}
message: |
${{ steps.container-size-diff.outputs.size-diff-markdown }}
- uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0.20.6
with:
image: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}@${{ steps.inspect-manifest.outputs.digest }}
image: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}@${{ steps.inspect-manifest.outputs.digest }}
dependency-snapshot: true
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with:
subject-name: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }}
subject-name: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}
subject-digest: ${{ steps.inspect-manifest.outputs.digest }}
show-summary: false
push-to-registry: true
- name: Verify attestation
run: gh attestation verify --repo "${GH_REPO}" "oci://${REGISTRY}/${GH_REPO}-${CONTAINER_FLAVOR}@${DIGEST}"
run: gh attestation verify --repo "${GH_REPO}" "oci://${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}@${DIGEST}"
env:
DIGEST: ${{ steps.inspect-manifest.outputs.digest }}
GH_REPO: ${{ github.repository }}
Expand Down
Loading