From 9894163ff1a88224440927c5a0b5005aad7d5659 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:26:32 +0200 Subject: [PATCH 01/50] ci: refactor wc-build-push for better re-use --- .github/workflows/wc-build-push.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 2f0af7c4..1e0aafc1 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -7,13 +7,25 @@ on: flavor: required: true type: string + registry: + description: "Docker registry to push built containers to" + required: false + type: string + default: "ghcr.io" + 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 + REGISTRY: ${{ inputs.registry }} jobs: build-push: @@ -33,10 +45,13 @@ jobs: 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 }} + username: ${{ env.USERNAME }} + password: ${{ env.PASSWORD }} - uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 env: DOCKER_METADATA_SET_OUTPUT_ENV: false From b5a80b6aa054c7bd5d5bf63ef8a31ea653967208 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:59:01 +0000 Subject: [PATCH 02/50] chore: make runner labels configurable --- .github/workflows/wc-build-push.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 1e0aafc1..3fab278e 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -12,6 +12,19 @@ on: 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"]}' + merge-runner: + description: "Runner label for the merge-image job" + 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" @@ -30,8 +43,7 @@ env: jobs: build-push: strategy: - matrix: - runner: ["ubuntu-latest", "ubuntu-24.04-arm"] + matrix: ${{ fromJson(inputs.build-matrix) }} runs-on: ${{ matrix.runner }} permissions: packages: write @@ -96,7 +108,7 @@ jobs: retention-days: 1 merge-image: - runs-on: ubuntu-latest + runs-on: ${{ inputs.merge-runner }} needs: build-push permissions: actions: read From 316371ddf624ff3fcd4e6f65bab8d7433e7532a9 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 06:58:00 +0000 Subject: [PATCH 03/50] chore: decouple flavor from wc-build-push-test --- .github/workflows/wc-build-push-test.yml | 3 +- .github/workflows/wc-build-push.yml | 50 +++++++++++++----------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 654881e6..4fd0a888 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -30,7 +30,8 @@ jobs: packages: write pull-requests: write with: - flavor: ${{ matrix.flavor }} + dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile + image-name: ${{ github.repository }}-${{ matrix.flavor }} dependency-review: runs-on: ubuntu-latest diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 3fab278e..e8138c12 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -4,7 +4,12 @@ name: Build & Push 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" required: true type: string registry: @@ -37,8 +42,8 @@ permissions: contents: read env: - CONTAINER_FLAVOR: ${{ inputs.flavor }} REGISTRY: ${{ inputs.registry }} + FULLY_QUALIFIED_IMAGE_NAME: ${{ inputs.registry }}/${{ inputs.image-name }} jobs: build-push: @@ -69,7 +74,7 @@ jobs: DOCKER_METADATA_SET_OUTPUT_ENV: false id: metadata with: - images: ${{ env.REGISTRY }}/${{ github.repository }}-${{ inputs.flavor }} + images: ${{ env.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" @@ -83,9 +88,9 @@ jobs: 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: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }} labels: | ${{ steps.metadata.outputs.labels }} devcontainer.metadata=${{ steps.devcontainer-metadata.outputs.metadata }} @@ -102,7 +107,7 @@ jobs: RUNNER_TEMP: ${{ runner.temp }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: digests-${{ inputs.flavor }}-${{ steps.devcontainer-arch.outputs.arch }} + name: digests-${{ inputs.image-name }}-${{ steps.devcontainer-arch.outputs.arch }} path: ${{ runner.temp }}/digests/* if-no-files-found: error retention-days: 1 @@ -130,21 +135,24 @@ jobs: - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: path: ${{ runner.temp }}/digests - pattern: digests-${{ inputs.flavor }}-* + pattern: digests-${{ inputs.image-name }}-* 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 }} + 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: ${{ env.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 @@ -162,7 +170,7 @@ jobs: 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] @@ -177,36 +185,34 @@ jobs: subprocess.run(command, check=True) env: 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 "${FULLY_QUALIFIED_IMAGE_NAME}:${CONTAINER_VERSION}" --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 }} - 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: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }}:edge + TO_CONTAINER: ${{ env.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-${{ inputs.image-name }} 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: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }}:edge + to-container: ${{ env.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 }} @@ -214,16 +220,16 @@ jobs: ${{ 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: ${{ env.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: ${{ env.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://${FULLY_QUALIFIED_IMAGE_NAME}@${DIGEST}" env: DIGEST: ${{ steps.inspect-manifest.outputs.digest }} GH_REPO: ${{ github.repository }} From 090dc7850a64f7cf29ce6334cd07c58851b8dc78 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 08:27:07 +0000 Subject: [PATCH 04/50] chore: fix issues with image name --- ...vscode.json => devcontainer-metadata.json} | 0 ...vscode.json => devcontainer-metadata.json} | 0 .github/workflows/wc-build-push-test.yml | 1 + .github/workflows/wc-build-push.yml | 92 +++++++++++++------ 4 files changed, 66 insertions(+), 27 deletions(-) rename .devcontainer/cpp/{devcontainer-metadata-vscode.json => devcontainer-metadata.json} (100%) rename .devcontainer/rust/{devcontainer-metadata-vscode.json => devcontainer-metadata.json} (100%) diff --git a/.devcontainer/cpp/devcontainer-metadata-vscode.json b/.devcontainer/cpp/devcontainer-metadata.json similarity index 100% rename from .devcontainer/cpp/devcontainer-metadata-vscode.json rename to .devcontainer/cpp/devcontainer-metadata.json diff --git a/.devcontainer/rust/devcontainer-metadata-vscode.json b/.devcontainer/rust/devcontainer-metadata.json similarity index 100% rename from .devcontainer/rust/devcontainer-metadata-vscode.json rename to .devcontainer/rust/devcontainer-metadata.json diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 4fd0a888..8f3f029a 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -30,6 +30,7 @@ jobs: packages: write pull-requests: write with: + devcontainer-metadata: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile image-name: ${{ github.repository }}-${{ matrix.flavor }} diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index e8138c12..488eeb2d 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -9,11 +9,15 @@ on: required: true type: string image-name: - description: "Name of the Docker image to build" + 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" + 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" @@ -25,8 +29,8 @@ on: required: false type: string default: '{"runner": ["ubuntu-latest", "ubuntu-24.04-arm"]}' - merge-runner: - description: "Runner label for the merge-image job" + default-runner: + description: "Runner label for the non-build jobs" required: false type: string default: ubuntu-latest @@ -38,19 +42,49 @@ on: description: "Password or token for Docker login, if not provided the GITHUB_TOKEN will be used" required: false -permissions: - contents: read - -env: - REGISTRY: ${{ inputs.registry }} - FULLY_QUALIFIED_IMAGE_NAME: ${{ inputs.registry }}/${{ inputs.image-name }} +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: ${{ 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 @@ -66,7 +100,7 @@ jobs: USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }} PASSWORD: ${{ secrets.DOCKER_PASSWORD || secrets.GITHUB_TOKEN }} with: - registry: ${{ env.REGISTRY }} + registry: ${{ inputs.registry }} username: ${{ env.USERNAME }} password: ${{ env.PASSWORD }} - uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 @@ -74,7 +108,7 @@ jobs: DOCKER_METADATA_SET_OUTPUT_ENV: false id: metadata with: - images: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }} + 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" @@ -90,7 +124,7 @@ jobs: with: file: ${{ inputs.dockerfile }} push: true - tags: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }} + tags: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} labels: | ${{ steps.metadata.outputs.labels }} devcontainer.metadata=${{ steps.devcontainer-metadata.outputs.metadata }} @@ -107,13 +141,13 @@ jobs: RUNNER_TEMP: ${{ runner.temp }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: digests-${{ inputs.image-name }}-${{ 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: ${{ inputs.merge-runner }} + runs-on: ${{ inputs.default-runner }} needs: build-push permissions: actions: read @@ -135,7 +169,7 @@ jobs: - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: path: ${{ runner.temp }}/digests - pattern: digests-${{ inputs.image-name }}-* + 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 @@ -143,7 +177,7 @@ jobs: USERNAME: ${{ secrets.DOCKER_USERNAME || github.actor }} PASSWORD: ${{ secrets.DOCKER_PASSWORD || secrets.GITHUB_TOKEN }} with: - registry: ${{ env.REGISTRY }} + registry: ${{ inputs.registry }} username: ${{ env.USERNAME }} password: ${{ env.PASSWORD }} - uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 @@ -152,7 +186,7 @@ jobs: DOCKER_METADATA_ANNOTATIONS_LEVELS: index DOCKER_METADATA_SET_OUTPUT_ENV: false with: - images: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }} + 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 @@ -165,6 +199,8 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - name: Create manifest list and push + env: + FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} run: | import os import json @@ -189,6 +225,8 @@ jobs: working-directory: ${{ runner.temp }}/digests - name: Inspect manifest and extract digest id: inspect-manifest + env: + FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} run: | set -Eeuo pipefail output=$(docker buildx imagetools inspect "${FULLY_QUALIFIED_IMAGE_NAME}:${CONTAINER_VERSION}" --format '{{json .}}') @@ -201,35 +239,35 @@ jobs: chmod +x diffoci ./diffoci diff --semantic --report-file=container-diff.json "${FROM_CONTAINER}" "${TO_CONTAINER}" || true env: - FROM_CONTAINER: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }}:edge - TO_CONTAINER: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }}:${{ 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.image-name }} + 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.FULLY_QUALIFIED_IMAGE_NAME }}:edge - to-container: ${{ env.FULLY_QUALIFIED_IMAGE_NAME }}:${{ 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.FULLY_QUALIFIED_IMAGE_NAME }}@${{ 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.FULLY_QUALIFIED_IMAGE_NAME }} + 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://${FULLY_QUALIFIED_IMAGE_NAME}@${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 }} From 05277004805264596b233b06e22f585f1a535644 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 08:30:52 +0000 Subject: [PATCH 05/50] chore: fix duplicate env --- .github/workflows/wc-build-push.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 488eeb2d..bdf49e42 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -225,14 +225,12 @@ jobs: working-directory: ${{ runner.temp }}/digests - name: Inspect manifest and extract digest id: inspect-manifest - env: - FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} run: | set -Eeuo pipefail - output=$(docker buildx imagetools inspect "${FULLY_QUALIFIED_IMAGE_NAME}:${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 }} + 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 From 5eddcc2d5f7d68a4e2b371bb2dfed8777b0e48a1 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 08:34:28 +0000 Subject: [PATCH 06/50] chore: fix more duplicate env's --- .github/workflows/wc-build-push.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index bdf49e42..752227dc 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -199,8 +199,6 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - name: Create manifest list and push - env: - FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} run: | import os import json @@ -220,6 +218,7 @@ jobs: 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 }} shell: python working-directory: ${{ runner.temp }}/digests From c65132683ae2067163cae9317b4befa78e5c0fe9 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 08:46:10 +0000 Subject: [PATCH 07/50] chore: fix findings --- .github/workflows/wc-build-push.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 752227dc..470b6f36 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -148,7 +148,9 @@ jobs: merge-image: runs-on: ${{ inputs.default-runner }} - needs: build-push + needs: + - build-push + - sanitize-inputs permissions: actions: read attestations: write @@ -240,7 +242,7 @@ jobs: 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-${{ needs.sanitize-inputs.outputs.image-basedname }} + name: container-diff-${{ needs.sanitize-inputs.outputs.image-basename }} path: container-diff.json retention-days: 10 - uses: ./.github/actions/container-size-diff @@ -264,8 +266,9 @@ jobs: show-summary: false push-to-registry: true - name: Verify attestation - run: gh attestation verify --repo "${GH_REPO}" "oci://${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}@${DIGEST}" + run: gh attestation verify --repo "${GH_REPO}" "oci://${FULLY_QUALIFIED_IMAGE_NAME}@${DIGEST}" env: DIGEST: ${{ steps.inspect-manifest.outputs.digest }} + FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ github.token }} From 00e94b1aaeabc7d5666c6c745740d3828ea61359 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 10:09:45 +0000 Subject: [PATCH 08/50] chore: start with fixing integration tests --- .github/workflows/continuous-integration.yml | 8 ++++ .github/workflows/wc-build-push-test.yml | 26 ++--------- .github/workflows/wc-build-push.yml | 12 ++++- .github/workflows/wc-dependency-review.yml | 32 +++++++++++++ .github/workflows/wc-integration-test.yml | 48 ++++++++++++-------- 5 files changed, 84 insertions(+), 42 deletions(-) create mode 100644 .github/workflows/wc-dependency-review.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 3de63165..5b75ed81 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -28,3 +28,11 @@ jobs: id-token: write packages: write pull-requests: write + + dependency-review: + uses: ./.github/workflows/wc-dependency-review.yml + with: + runner-labels: ubuntu-latest + permissions: + contents: read + pull-requests: write diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 8f3f029a..55691536 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -34,26 +34,6 @@ jobs: dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile image-name: ${{ github.repository }}-${{ matrix.flavor }} - dependency-review: - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - needs: build-push - if: github.event_name == 'pull_request' - steps: - - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - disable-sudo-and-containers: true - egress-policy: audit - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - persist-credentials: false - - uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4.8.0 - with: - comment-summary-in-pr: on-failure - fail-on-severity: critical - integration-test: strategy: matrix: @@ -62,8 +42,10 @@ jobs: needs: build-push uses: ./.github/workflows/wc-integration-test.yml with: - flavor: ${{ matrix.flavor }} - runner: ${{ matrix.runner }} + fully-qualified-image-name: ${{ needs.build-push.outputs.fully-qualified-image-name }} + image-basename: ${{ needs.build-push.outputs.image-basename }} + test-file: test/${{ matrix.flavor }}/integration.bats + runner-labels: ${{ matrix.runner }} acceptance-test: strategy: diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 470b6f36..8fd80f77 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -34,6 +34,16 @@ on: required: false type: string default: ubuntu-latest + outputs: + image-basename: + description: "The sanitized base name of the image (without registry or tag)" + value: ${{ jobs.sanitize-inputs.outputs.image-basename }} + image-name: + description: "The sanitized name of the image (without registry or tag)" + value: ${{ jobs.sanitize-inputs.outputs.image-name }} + fully-qualified-image-name: + description: "The fully qualified name of the image including registry (but without tag)" + value: ${{ jobs.sanitize-inputs.outputs.fully-qualified-image-name }} secrets: DOCKER_USERNAME: description: "User name for Docker login, if not provided the GitHub actor will be used" @@ -48,9 +58,9 @@ jobs: sanitize-inputs: runs-on: ${{ inputs.default-runner }} outputs: + image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} 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 diff --git a/.github/workflows/wc-dependency-review.yml b/.github/workflows/wc-dependency-review.yml new file mode 100644 index 00000000..3faf5d11 --- /dev/null +++ b/.github/workflows/wc-dependency-review.yml @@ -0,0 +1,32 @@ +--- +name: Dependency Review + +on: + workflow_call: + inputs: + runner-labels: + description: "Runner to use for the job, will be passed to `runs-on`" + required: false + type: string + default: ubuntu-latest + +permissions: {} + +jobs: + dependency-review: + runs-on: ${{ inputs.runner-labels }} + permissions: + contents: read + pull-requests: write + steps: + - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + disable-sudo-and-containers: true + egress-policy: audit + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + - uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4.8.0 + with: + comment-summary-in-pr: on-failure + fail-on-severity: critical diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 26b899d8..9f18da6f 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -4,25 +4,28 @@ name: Integration Test on: workflow_call: inputs: - flavor: + fully-qualified-image-name: + required: false + type: string + image-basename: required: true type: string - runner: + test-file: + required: true + type: string + runner-labels: + description: "Runner to use for the job, will be passed to `runs-on`" required: true type: string -permissions: - contents: read - -env: - CONTAINER_FLAVOR: ${{ inputs.flavor }} - RUNNER: ${{ inputs.runner }} +permissions: {} jobs: determine-container: - runs-on: ${{ inputs.runner }} + runs-on: ${{ inputs.runner-labels }} outputs: container: ${{ steps.set-container.outputs.container }} + runner-arch: ${{ steps.runner-arch.outputs.arch }} steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: @@ -32,17 +35,21 @@ jobs: id: runner-arch - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: - path: ${{ runner.temp }}/digests-${{ inputs.flavor }}-${{ steps.runner-arch.outputs.arch }} - pattern: digests-${{ inputs.flavor }}-${{ steps.runner-arch.outputs.arch }} - - run: echo "container=$(printf "ghcr.io/${GH_REPO}-${CONTAINER_FLAVOR}@sha256:%s " *)" >> "$GITHUB_OUTPUT" - working-directory: ${{ runner.temp }}/digests-${{ inputs.flavor }}-${{ steps.runner-arch.outputs.arch }} + path: ${{ runner.temp }}/digests-${{ inputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} + pattern: digests-${{ inputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} + - run: echo "container=$(printf "${FULLY_QUALIFIED_IMAGE_NAME}@sha256:%s " *)" >> "$GITHUB_OUTPUT" + working-directory: ${{ runner.temp }}/digests-${{ inputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} env: + FULLY_QUALIFIED_IMAGE_NAME: ${{ inputs.fully-qualified-image-name }} GH_REPO: ${{ github.repository }} id: set-container + run-test: needs: determine-container - runs-on: ${{ inputs.runner }} + runs-on: ${{ inputs.runner-labels }} container: ${{ needs.determine-container.outputs.container }} + permissions: + contents: read steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: @@ -52,15 +59,18 @@ jobs: with: persist-credentials: false - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - if: inputs.flavor == 'cpp' with: path: test/cpp/.xwin-cache - key: xwin-cache-${{ inputs.runner }} + key: xwin-cache-${{ needs.determine-container.outputs.runner-arch }} restore-keys: | - xwin-cache - - run: bats --formatter junit "test/${CONTAINER_FLAVOR}/integration-tests.bats" | tee "test-report-${CONTAINER_FLAVOR}-${RUNNER}.xml" + xwin-cache-${{ needs.determine-container.outputs.runner-arch }} + - run: bats --formatter junit "${TEST_FILE}" | tee "test-report-${IMAGE_BASENAME}-${RUNNER_ARCH}.xml" + env: + IMAGE_BASENAME: ${{ inputs.image-basename }} + TEST_FILE: ${{ inputs.test-file }} + RUNNER_ARCH: ${{ needs.determine-container.outputs.runner-arch }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: always() with: - name: test-results-integration-${{ inputs.flavor }}-${{ inputs.runner }} + name: test-results-integration-${{ inputs.image-basename }}-${{ needs.determine-container.outputs.runner-arch }} path: test-report-*.xml From fdab30a45ae7bef222c03a014364b50de72ad805 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 10:33:54 +0000 Subject: [PATCH 09/50] chore: split-off image name sanitization --- .github/workflows/wc-build-push-test.yml | 3 +- .github/workflows/wc-build-push.yml | 84 ++++++-------------- .github/workflows/wc-integration-test.yml | 34 +++++--- .github/workflows/wc-sanitize-image-name.yml | 70 ++++++++++++++++ 4 files changed, 118 insertions(+), 73 deletions(-) create mode 100644 .github/workflows/wc-sanitize-image-name.yml diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 55691536..85517caa 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -42,8 +42,7 @@ jobs: needs: build-push uses: ./.github/workflows/wc-integration-test.yml with: - fully-qualified-image-name: ${{ needs.build-push.outputs.fully-qualified-image-name }} - image-basename: ${{ needs.build-push.outputs.image-basename }} + image-name: ${{ github.repository }}-${{ matrix.flavor }} test-file: test/${{ matrix.flavor }}/integration.bats runner-labels: ${{ matrix.runner }} diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 8fd80f77..a2ee5471 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -34,16 +34,6 @@ on: required: false type: string default: ubuntu-latest - outputs: - image-basename: - description: "The sanitized base name of the image (without registry or tag)" - value: ${{ jobs.sanitize-inputs.outputs.image-basename }} - image-name: - description: "The sanitized name of the image (without registry or tag)" - value: ${{ jobs.sanitize-inputs.outputs.image-name }} - fully-qualified-image-name: - description: "The fully qualified name of the image including registry (but without tag)" - value: ${{ jobs.sanitize-inputs.outputs.fully-qualified-image-name }} secrets: DOCKER_USERNAME: description: "User name for Docker login, if not provided the GitHub actor will be used" @@ -55,44 +45,18 @@ on: permissions: {} jobs: - sanitize-inputs: - runs-on: ${{ inputs.default-runner }} - outputs: - image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} - image-name: ${{ steps.sanitize-image-name.outputs.sanitized-image-name }} - fully-qualified-image-name: ${{ inputs.registry }}/${{ steps.sanitize-image-name.outputs.sanitized-image-name }} - 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" + sanitize-image-name: + uses: ./.github/workflows/wc-sanitize-image-name.yml + with: + image-name: ${{ inputs.image-name }} + registry: ${{ inputs.registry }} + runner-labels: ${{ inputs.default-runner }} build-push: strategy: matrix: ${{ fromJson(inputs.build-matrix) }} runs-on: ${{ matrix.runner }} - needs: sanitize-inputs + needs: sanitize-image-name permissions: contents: read packages: write @@ -118,7 +82,7 @@ jobs: DOCKER_METADATA_SET_OUTPUT_ENV: false id: metadata with: - images: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} + images: ${{ needs.sanitize-image-name.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" @@ -134,7 +98,7 @@ jobs: with: file: ${{ inputs.dockerfile }} push: true - tags: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} + tags: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} labels: | ${{ steps.metadata.outputs.labels }} devcontainer.metadata=${{ steps.devcontainer-metadata.outputs.metadata }} @@ -151,7 +115,7 @@ jobs: RUNNER_TEMP: ${{ runner.temp }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: digests-${{ needs.sanitize-inputs.outputs.image-basename }}-${{ steps.devcontainer-arch.outputs.arch }} + name: digests-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ steps.devcontainer-arch.outputs.arch }} path: ${{ runner.temp }}/digests/* if-no-files-found: error retention-days: 1 @@ -160,7 +124,7 @@ jobs: runs-on: ${{ inputs.default-runner }} needs: - build-push - - sanitize-inputs + - sanitize-image-name permissions: actions: read attestations: write @@ -181,7 +145,7 @@ jobs: - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: path: ${{ runner.temp }}/digests - pattern: digests-${{ needs.sanitize-inputs.outputs.image-basename }}-* + pattern: digests-${{ needs.sanitize-image-name.outputs.image-basename }}-* merge-multiple: true - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 @@ -198,7 +162,7 @@ jobs: DOCKER_METADATA_ANNOTATIONS_LEVELS: index DOCKER_METADATA_SET_OUTPUT_ENV: false with: - images: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} + images: ${{ needs.sanitize-image-name.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 @@ -230,7 +194,7 @@ jobs: print(' '.join(command)) subprocess.run(command, check=True) env: - FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} + FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} METADATA_JSON: ${{ steps.metadata.outputs.json }} shell: python working-directory: ${{ runner.temp }}/digests @@ -241,37 +205,37 @@ jobs: output=$(docker buildx imagetools inspect "${CONTAINER}" --format '{{json .}}') echo "digest=$(echo "$output" | jq -r '.manifest.digest // .manifests[0].digest')" >> "$GITHUB_OUTPUT" env: - CONTAINER: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}:${{ steps.metadata.outputs.version }} + CONTAINER: ${{ needs.sanitize-image-name.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: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}:edge - TO_CONTAINER: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}:${{ steps.metadata.outputs.version }} + FROM_CONTAINER: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }}:edge + TO_CONTAINER: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }}:${{ steps.metadata.outputs.version }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: container-diff-${{ needs.sanitize-inputs.outputs.image-basename }} + name: container-diff-${{ needs.sanitize-image-name.outputs.image-basename }} path: container-diff.json retention-days: 10 - uses: ./.github/actions/container-size-diff id: container-size-diff with: - 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 }} + from-container: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }}:edge + to-container: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }}:${{ steps.metadata.outputs.version }} - uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 with: - header: container-size-diff-${{ needs.sanitize-inputs.outputs.image-basename }} + header: container-size-diff-${{ needs.sanitize-image-name.outputs.image-basename }} message: | ${{ steps.container-size-diff.outputs.size-diff-markdown }} - uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0.20.6 with: - image: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }}@${{ steps.inspect-manifest.outputs.digest }} + image: ${{ needs.sanitize-image-name.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: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} + subject-name: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} subject-digest: ${{ steps.inspect-manifest.outputs.digest }} show-summary: false push-to-registry: true @@ -279,6 +243,6 @@ jobs: run: gh attestation verify --repo "${GH_REPO}" "oci://${FULLY_QUALIFIED_IMAGE_NAME}@${DIGEST}" env: DIGEST: ${{ steps.inspect-manifest.outputs.digest }} - FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-inputs.outputs.fully-qualified-image-name }} + FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 9f18da6f..293b2dc1 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -4,10 +4,7 @@ name: Integration Test on: workflow_call: inputs: - fully-qualified-image-name: - required: false - type: string - image-basename: + image-name: required: true type: string test-file: @@ -17,12 +14,25 @@ on: description: "Runner to use for the job, will be passed to `runs-on`" required: true 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" permissions: {} jobs: + sanitize-image-name: + uses: ./.github/workflows/wc-sanitize-image-name.yml + with: + image-name: ${{ inputs.image-name }} + registry: ${{ inputs.registry }} + runner-labels: ${{ inputs.runner-labels }} + determine-container: runs-on: ${{ inputs.runner-labels }} + needs: sanitize-image-name outputs: container: ${{ steps.set-container.outputs.container }} runner-arch: ${{ steps.runner-arch.outputs.arch }} @@ -35,17 +45,19 @@ jobs: id: runner-arch - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: - path: ${{ runner.temp }}/digests-${{ inputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} - pattern: digests-${{ inputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} + path: ${{ runner.temp }}/digests-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} + pattern: digests-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} - run: echo "container=$(printf "${FULLY_QUALIFIED_IMAGE_NAME}@sha256:%s " *)" >> "$GITHUB_OUTPUT" - working-directory: ${{ runner.temp }}/digests-${{ inputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} + working-directory: ${{ runner.temp }}/digests-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} env: - FULLY_QUALIFIED_IMAGE_NAME: ${{ inputs.fully-qualified-image-name }} + FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} GH_REPO: ${{ github.repository }} id: set-container run-test: - needs: determine-container + needs: + - determine-container + - sanitize-image-name runs-on: ${{ inputs.runner-labels }} container: ${{ needs.determine-container.outputs.container }} permissions: @@ -66,11 +78,11 @@ jobs: xwin-cache-${{ needs.determine-container.outputs.runner-arch }} - run: bats --formatter junit "${TEST_FILE}" | tee "test-report-${IMAGE_BASENAME}-${RUNNER_ARCH}.xml" env: - IMAGE_BASENAME: ${{ inputs.image-basename }} + IMAGE_BASENAME: ${{ needs.sanitize-image-name.outputs.image-basename }} TEST_FILE: ${{ inputs.test-file }} RUNNER_ARCH: ${{ needs.determine-container.outputs.runner-arch }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: always() with: - name: test-results-integration-${{ inputs.image-basename }}-${{ needs.determine-container.outputs.runner-arch }} + name: test-results-integration-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ needs.determine-container.outputs.runner-arch }} path: test-report-*.xml diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml new file mode 100644 index 00000000..b2a1f3a1 --- /dev/null +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -0,0 +1,70 @@ +--- +name: Sanitize Image Name + +on: + workflow_call: + inputs: + 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 + registry: + description: "Container registry to push the image to, e.g. ghcr.io" + required: false + type: string + default: "ghcr.io" + runner-labels: + description: "Runner to use for the job, will be passed to `runs-on`" + required: false + type: string + default: "ubuntu-latest" + outputs: + image-basename: + description: "The sanitized base name of the image (without registry or tag)" + value: ${{ jobs.sanitize-inputs.outputs.image-basename }} + image-name: + description: "The sanitized name of the image (without registry or tag)" + value: ${{ jobs.sanitize-inputs.outputs.image-name }} + fully-qualified-image-name: + description: "The fully qualified name of the image including registry (but without tag)" + value: ${{ jobs.sanitize-inputs.outputs.fully-qualified-image-name }} + +permissions: {} + +jobs: + sanitize-inputs: + runs-on: ${{ inputs.runner-labels }} + outputs: + image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} + image-name: ${{ steps.sanitize-image-name.outputs.sanitized-image-name }} + fully-qualified-image-name: ${{ inputs.registry }}/${{ steps.sanitize-image-name.outputs.sanitized-image-name }} + steps: + - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + disable-sudo: true + egress-policy: audit + - 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" From 97e079233b41ddb9832c8fe2fb3c40a8af8bbf99 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 10:48:36 +0000 Subject: [PATCH 10/50] chore: fix path to integration tests --- .github/workflows/continuous-integration.yml | 8 -------- .github/workflows/wc-build-push-test.yml | 9 ++++++++- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 5b75ed81..3de63165 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -28,11 +28,3 @@ jobs: id-token: write packages: write pull-requests: write - - dependency-review: - uses: ./.github/workflows/wc-dependency-review.yml - with: - runner-labels: ubuntu-latest - permissions: - contents: read - pull-requests: write diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 85517caa..099c292c 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -34,6 +34,13 @@ jobs: dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile image-name: ${{ github.repository }}-${{ matrix.flavor }} + dependency-review: + needs: build-push + uses: ./.github/workflows/wc-dependency-review.yml + permissions: + contents: read + pull-requests: write + integration-test: strategy: matrix: @@ -43,7 +50,7 @@ jobs: uses: ./.github/workflows/wc-integration-test.yml with: image-name: ${{ github.repository }}-${{ matrix.flavor }} - test-file: test/${{ matrix.flavor }}/integration.bats + test-file: test/${{ matrix.flavor }}/integration-tests.bats runner-labels: ${{ matrix.runner }} acceptance-test: From 66593baff20468433a5b5feeca67c8862c340569 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 11:11:51 +0000 Subject: [PATCH 11/50] chore: enable docker credentials for integration test --- .github/workflows/wc-integration-test.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 293b2dc1..d49cf46c 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -19,6 +19,11 @@ on: required: false type: string default: "ghcr.io" + secrets: + DOCKER_REGISTRY_USERNAME: + required: false + DOCKER_REGISTRY_PASSWORD: + required: false permissions: {} @@ -59,7 +64,11 @@ jobs: - determine-container - sanitize-image-name runs-on: ${{ inputs.runner-labels }} - container: ${{ needs.determine-container.outputs.container }} + container: + image: ${{ needs.determine-container.outputs.container }} + credentials: + username: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} permissions: contents: read steps: From be55306e0197e7816a3245ee10940c199380e11d Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 11:38:04 +0000 Subject: [PATCH 12/50] ci: change secret names --- .github/workflows/wc-build-push.yml | 14 +++++++------- .github/workflows/wc-integration-test.yml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index a2ee5471..4960d1a1 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -17,7 +17,7 @@ on: 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" + description: "Docker registry to push built containers to, DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD secrets must be set if not using GitHub Container Registry" required: false type: string default: "ghcr.io" @@ -35,10 +35,10 @@ on: type: string default: ubuntu-latest secrets: - DOCKER_USERNAME: + DOCKER_REGISTRY_USERNAME: description: "User name for Docker login, if not provided the GitHub actor will be used" required: false - DOCKER_PASSWORD: + DOCKER_REGISTRY_PASSWORD: description: "Password or token for Docker login, if not provided the GITHUB_TOKEN will be used" required: false @@ -71,8 +71,8 @@ jobs: - 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 }} + USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} + PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} with: registry: ${{ inputs.registry }} username: ${{ env.USERNAME }} @@ -150,8 +150,8 @@ jobs: - 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 }} + USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} + PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} with: registry: ${{ inputs.registry }} username: ${{ env.USERNAME }} diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index d49cf46c..2ceab982 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -15,7 +15,7 @@ on: required: true 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" + description: "Docker registry to push built containers to, DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD secrets must be set if not using GitHub Container Registry" required: false type: string default: "ghcr.io" From b6012d649bbafb4c2ce513a971d4672f1502e48b Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:01:36 +0000 Subject: [PATCH 13/50] ci: enable multiple labels for single runner selection --- .github/workflows/wc-build-push.yml | 12 +++++++----- .github/workflows/wc-integration-test.yml | 4 ++-- .github/workflows/wc-sanitize-image-name.yml | 8 +++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 4960d1a1..79d6b54a 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -29,11 +29,13 @@ on: required: false type: string default: '{"runner": ["ubuntu-latest", "ubuntu-24.04-arm"]}' - default-runner: - description: "Runner label for the non-build jobs" + default-runner-labels: + description: >- + String or JSON array of runner labels for non-build jobs. + Example: '["self-hosted", "linux", "x86_64"]' or 'ubuntu-latest'. required: false type: string - default: ubuntu-latest + default: "ubuntu-latest" secrets: DOCKER_REGISTRY_USERNAME: description: "User name for Docker login, if not provided the GitHub actor will be used" @@ -50,7 +52,7 @@ jobs: with: image-name: ${{ inputs.image-name }} registry: ${{ inputs.registry }} - runner-labels: ${{ inputs.default-runner }} + runner-labels: ${{ inputs.default-runner-labels }} build-push: strategy: @@ -121,7 +123,7 @@ jobs: retention-days: 1 merge-image: - runs-on: ${{ inputs.default-runner }} + runs-on: ${{ fromJson(inputs.default-runner-labels) }} needs: - build-push - sanitize-image-name diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 2ceab982..bf26a48a 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -36,7 +36,7 @@ jobs: runner-labels: ${{ inputs.runner-labels }} determine-container: - runs-on: ${{ inputs.runner-labels }} + runs-on: ${{ fromJson(inputs.runner-labels) }} needs: sanitize-image-name outputs: container: ${{ steps.set-container.outputs.container }} @@ -63,7 +63,7 @@ jobs: needs: - determine-container - sanitize-image-name - runs-on: ${{ inputs.runner-labels }} + runs-on: ${{ fromJson(inputs.runner-labels) }} container: image: ${{ needs.determine-container.outputs.container }} credentials: diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index b2a1f3a1..c8c4348b 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -10,14 +10,12 @@ on: type: string registry: description: "Container registry to push the image to, e.g. ghcr.io" - required: false + required: true type: string - default: "ghcr.io" runner-labels: description: "Runner to use for the job, will be passed to `runs-on`" - required: false + required: true type: string - default: "ubuntu-latest" outputs: image-basename: description: "The sanitized base name of the image (without registry or tag)" @@ -33,7 +31,7 @@ permissions: {} jobs: sanitize-inputs: - runs-on: ${{ inputs.runner-labels }} + runs-on: ${{ fromJson(inputs.runner-labels) }} outputs: image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} image-name: ${{ steps.sanitize-image-name.outputs.sanitized-image-name }} From 9826dc7df6eef0609d49b62ebeb37b675b333a6d Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:06:29 +0000 Subject: [PATCH 14/50] ci: try to fix syntax for fromJson --- .github/workflows/wc-build-push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 79d6b54a..e7d19631 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -32,10 +32,10 @@ on: default-runner-labels: description: >- String or JSON array of runner labels for non-build jobs. - Example: '["self-hosted", "linux", "x86_64"]' or 'ubuntu-latest'. + Example: ["self-hosted", "linux", "x86_64"] or ubuntu-latest. required: false type: string - default: "ubuntu-latest" + default: ubuntu-latest secrets: DOCKER_REGISTRY_USERNAME: description: "User name for Docker login, if not provided the GitHub actor will be used" From 7e8d786333da8c7cded055232556c7ff58fef217 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:13:32 +0000 Subject: [PATCH 15/50] ci: try to fix runner selection --- .github/workflows/wc-build-push.yml | 12 +++++++++--- .github/workflows/wc-sanitize-image-name.yml | 8 ++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index e7d19631..163d0633 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -31,8 +31,12 @@ on: default: '{"runner": ["ubuntu-latest", "ubuntu-24.04-arm"]}' default-runner-labels: description: >- - String or JSON array of runner labels for non-build jobs. - Example: ["self-hosted", "linux", "x86_64"] or ubuntu-latest. + Single runner label OR JSON array of runner labels for non-build jobs. + Examples: + ubuntu-latest + ["ubuntu-latest"] + ["self-hosted", "linux", "x86_64"] + Provide a valid JSON array (starting with '[') to use multiple labels; any other value is treated as a single label string. required: false type: string default: ubuntu-latest @@ -123,7 +127,9 @@ jobs: retention-days: 1 merge-image: - runs-on: ${{ fromJson(inputs.default-runner-labels) }} + # Support either a plain single label (e.g. ubuntu-latest) OR a JSON array of labels. + # If the input starts & ends with brackets we attempt JSON parsing; otherwise we pass the raw string. + runs-on: ${{ (startsWith(inputs.default-runner-labels, '[') && endsWith(inputs.default-runner-labels, ']')) && fromJson(inputs.default-runner-labels) || inputs.default-runner-labels }} needs: - build-push - sanitize-image-name diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index c8c4348b..5c2b7b18 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -13,7 +13,10 @@ on: required: true type: string runner-labels: - description: "Runner to use for the job, will be passed to `runs-on`" + description: >- + Runner selection for this job. Accepts either a single label (e.g. ubuntu-latest) + or a JSON array of labels (e.g. ["self-hosted", "linux", "x86_64"]). + If the value begins with '[' it will be parsed as JSON. required: true type: string outputs: @@ -31,7 +34,8 @@ permissions: {} jobs: sanitize-inputs: - runs-on: ${{ fromJson(inputs.runner-labels) }} + # Allow either raw single label or JSON array of labels + runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} outputs: image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} image-name: ${{ steps.sanitize-image-name.outputs.sanitized-image-name }} From 2f98b13f0b5fbb76620108a25a2abbe590c8c95a Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:41:08 +0000 Subject: [PATCH 16/50] ci: handle devcontainer metadata properly --- .github/workflows/wc-build-push.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 163d0633..1f6f10e1 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -89,9 +89,20 @@ jobs: id: metadata with: images: ${{ needs.sanitize-image-name.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" + - name: Generate image LABEL for devcontainer.metadata + run: | + set -Eeuo pipefail + + if ([ -z "${DEVCONTAINER_METADATA:-}" ] || [ ! -f "${DEVCONTAINER_METADATA}" ]); then + echo "devcontainer-metadata input not set or file does not exist, skipping devcontainer.metadata label" + echo "metadata=" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # the sed expression is a workaround for quotes being eaten in arrays (e.g. ["x", "y", "z"] -> ["x",y,"z"]) + echo "metadata=devcontainer.metadata\=$(jq -cj '[.]' "${DEVCONTAINER_METADATA}" | sed 's/,"/, "/g')" >> "$GITHUB_OUTPUT" + env: + DEVCONTAINER_METADATA: ${{ inputs.devcontainer-metadata }} id: devcontainer-metadata - run: echo "git-commit-epoch=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT" id: devcontainer-epoch @@ -107,7 +118,7 @@ jobs: tags: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} labels: | ${{ steps.metadata.outputs.labels }} - devcontainer.metadata=${{ steps.devcontainer-metadata.outputs.metadata }} + ${{ steps.devcontainer-metadata.outputs.metadata }} annotations: ${{ steps.metadata.outputs.annotations }} sbom: true outputs: type=image,push-by-digest=true,name-canonical=true From 8763b1ec05073142c8dd4da31b164096c77382d8 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:49:21 +0000 Subject: [PATCH 17/50] ci: don't escape '=' --- .github/workflows/wc-build-push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 1f6f10e1..54084be6 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -89,7 +89,7 @@ jobs: id: metadata with: images: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} - - name: Generate image LABEL for devcontainer.metadata + - name: Generate image label for devcontainer.metadata run: | set -Eeuo pipefail @@ -100,7 +100,7 @@ jobs: fi # the sed expression is a workaround for quotes being eaten in arrays (e.g. ["x", "y", "z"] -> ["x",y,"z"]) - echo "metadata=devcontainer.metadata\=$(jq -cj '[.]' "${DEVCONTAINER_METADATA}" | sed 's/,"/, "/g')" >> "$GITHUB_OUTPUT" + echo "metadata=devcontainer.metadata=$(jq -cj '[.]' "${DEVCONTAINER_METADATA}" | sed 's/,"/, "/g')" >> "$GITHUB_OUTPUT" env: DEVCONTAINER_METADATA: ${{ inputs.devcontainer-metadata }} id: devcontainer-metadata From c27b4e999558da7f1f7da4504bedbb5e126e72dc Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:51:25 +0000 Subject: [PATCH 18/50] ci: fix linter feedback --- .github/workflows/wc-build-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 54084be6..23fbd6c6 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -93,7 +93,7 @@ jobs: run: | set -Eeuo pipefail - if ([ -z "${DEVCONTAINER_METADATA:-}" ] || [ ! -f "${DEVCONTAINER_METADATA}" ]); then + if [ -z "${DEVCONTAINER_METADATA:-}" ] || [ ! -f "${DEVCONTAINER_METADATA}" ]; then echo "devcontainer-metadata input not set or file does not exist, skipping devcontainer.metadata label" echo "metadata=" >> "$GITHUB_OUTPUT" exit 0 From 8daad34fb9b3200e571482a971cb666d9434e949 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:20:33 +0000 Subject: [PATCH 19/50] ci: fix more runs-on --- .github/workflows/wc-build-push.yml | 16 ++++++++-------- .github/workflows/wc-dependency-review.yml | 2 +- .github/workflows/wc-integration-test.yml | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 23fbd6c6..2c7338ff 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -29,13 +29,13 @@ on: required: false type: string default: '{"runner": ["ubuntu-latest", "ubuntu-24.04-arm"]}' - default-runner-labels: + runner-labels: description: >- Single runner label OR JSON array of runner labels for non-build jobs. Examples: ubuntu-latest - ["ubuntu-latest"] - ["self-hosted", "linux", "x86_64"] + '["ubuntu-latest"]' + '["self-hosted", "linux", "x86_64"]' Provide a valid JSON array (starting with '[') to use multiple labels; any other value is treated as a single label string. required: false type: string @@ -45,7 +45,7 @@ on: description: "User name for Docker login, if not provided the GitHub actor will be used" required: false DOCKER_REGISTRY_PASSWORD: - description: "Password or token for Docker login, if not provided the GITHUB_TOKEN will be used" + description: "Password or token for Docker login, if not provided the GitHub token will be used" required: false permissions: {} @@ -56,7 +56,7 @@ jobs: with: image-name: ${{ inputs.image-name }} registry: ${{ inputs.registry }} - runner-labels: ${{ inputs.default-runner-labels }} + runner-labels: ${{ inputs.runner-labels }} build-push: strategy: @@ -78,7 +78,7 @@ jobs: - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 env: USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} - PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} + PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} with: registry: ${{ inputs.registry }} username: ${{ env.USERNAME }} @@ -140,7 +140,7 @@ jobs: merge-image: # Support either a plain single label (e.g. ubuntu-latest) OR a JSON array of labels. # If the input starts & ends with brackets we attempt JSON parsing; otherwise we pass the raw string. - runs-on: ${{ (startsWith(inputs.default-runner-labels, '[') && endsWith(inputs.default-runner-labels, ']')) && fromJson(inputs.default-runner-labels) || inputs.default-runner-labels }} + runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} needs: - build-push - sanitize-image-name @@ -170,7 +170,7 @@ jobs: - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 env: USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} - PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }} + PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} with: registry: ${{ inputs.registry }} username: ${{ env.USERNAME }} diff --git a/.github/workflows/wc-dependency-review.yml b/.github/workflows/wc-dependency-review.yml index 3faf5d11..94579167 100644 --- a/.github/workflows/wc-dependency-review.yml +++ b/.github/workflows/wc-dependency-review.yml @@ -14,7 +14,7 @@ permissions: {} jobs: dependency-review: - runs-on: ${{ inputs.runner-labels }} + runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} permissions: contents: read pull-requests: write diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index bf26a48a..1c230828 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -36,7 +36,7 @@ jobs: runner-labels: ${{ inputs.runner-labels }} determine-container: - runs-on: ${{ fromJson(inputs.runner-labels) }} + runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} needs: sanitize-image-name outputs: container: ${{ steps.set-container.outputs.container }} @@ -63,7 +63,7 @@ jobs: needs: - determine-container - sanitize-image-name - runs-on: ${{ fromJson(inputs.runner-labels) }} + runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} container: image: ${{ needs.determine-container.outputs.container }} credentials: From 37b3db0afda2db86a16ea0ed384ec0c0c628d8f3 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:36:05 +0000 Subject: [PATCH 20/50] chore: retry merge-image in bash --- .github/workflows/wc-build-push.yml | 47 ++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 2c7338ff..1f00c7ae 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -195,27 +195,44 @@ jobs: type=semver,pattern={{major}} - name: Create manifest list and push run: | - import os - import json - import subprocess + set -Eeuo pipefail + + CMD=(docker buildx imagetools create) - CONTAINER = f"{os.getenv('FULLY_QUALIFIED_IMAGE_NAME')}" - METADATA = json.loads(os.getenv('METADATA_JSON')) + # Build tag and annotation lists from metadata action output + mapfile -t TAGS < <(jq -r '.tags[]? // empty' <<<"${METADATA_JSON}") || true + mapfile -t ANNOTATIONS < <(jq -r '.annotations[]? // empty' <<<"${METADATA_JSON}") || true - digests = [f for f in os.listdir('.') if f.startswith('sha256:') or len(f) == 64] + for ann in "${ANNOTATIONS[@]:-}"; do + [ -n "${ann}" ] && CMD+=( --annotation "${ann}" ) + done + for tag in "${TAGS[@]:-}"; do + [ -n "${tag}" ] && CMD+=( --tag "${tag}" ) + done - command = ['docker', 'buildx', 'imagetools', 'create', - *[annotation for annotation in METADATA.get('annotations', []) for annotation in ('--annotation', annotation)], - *[tag for tag in METADATA.get('tags', []) for tag in ('--tag', tag)], - *[f"{CONTAINER}@sha256:{digest}" for digest in digests] - ] + # Each file in the working directory represents a digest (either named sha256: or the 64-char digest itself) + for f in *; do + [ -f "$f" ] || continue + digest="" + if [[ "$f" == sha256:* ]]; then + digest="${f#sha256:}" + elif [[ ${#f} -eq 64 && "$f" =~ ^[a-f0-9]{64}$ ]]; then + digest="$f" + else + continue + fi + CMD+=( "${CONTAINER}@sha256:${digest}" ) + done - print(' '.join(command)) - subprocess.run(command, check=True) + echo "Creating manifest list with command:" >&2 + printf ' %q' "${CMD[@]}" >&2; echo >&2 + + # Execute the command + "${CMD[@]}" env: - FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} + CONTAINER: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} METADATA_JSON: ${{ steps.metadata.outputs.json }} - shell: python + shell: bash working-directory: ${{ runner.temp }}/digests - name: Inspect manifest and extract digest id: inspect-manifest From ea928923292a422db249bde2d303829d76a3cf94 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 14:40:56 +0000 Subject: [PATCH 21/50] ci: various fixes and improvements --- .github/workflows/update-dependencies.yml | 2 +- .github/workflows/wc-build-push.yml | 2 +- .github/workflows/wc-document-generation.yml | 5 +++-- .github/workflows/wc-integration-test.yml | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 82078c3c..4a6da0b8 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -49,7 +49,7 @@ jobs: strategy: matrix: flavor: ["cpp", "rust"] - file: ["devcontainer-metadata-vscode.json", "devcontainer.json"] + file: ["devcontainer-metadata.json", "devcontainer.json"] permissions: contents: write pull-requests: write diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 1f00c7ae..231511e5 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -255,7 +255,7 @@ jobs: name: container-diff-${{ needs.sanitize-image-name.outputs.image-basename }} path: container-diff.json retention-days: 10 - - uses: ./.github/actions/container-size-diff + - uses: philips-software/amp-devcontainer/.github/actions/container-size-diff@ab0940b1e92f3ccee257d5984166c63c8cfe6a9d # v6.5.0 id: container-size-diff with: from-container: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }}:edge diff --git a/.github/workflows/wc-document-generation.yml b/.github/workflows/wc-document-generation.yml index 95ec7e24..48e38b93 100644 --- a/.github/workflows/wc-document-generation.yml +++ b/.github/workflows/wc-document-generation.yml @@ -4,12 +4,13 @@ name: Document Generation on: workflow_call: -permissions: - contents: read +permissions: {} jobs: generate-documents: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 1c230828..83cd17ae 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -65,7 +65,7 @@ jobs: - sanitize-image-name runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} container: - image: ${{ needs.determine-container.outputs.container }} + image: ${{ needs.determine-container.outputs.container }} # zizmor: ignore[unpinned-images] This image is actually pinned by sha256 digest credentials: username: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} password: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} From 706a1bbfa65e5b23b54401d7af742a8b24284e37 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:00:20 +0000 Subject: [PATCH 22/50] ci: fix document upload for release --- .github/workflows/release-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 1bdc5de6..3f7da266 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -126,3 +126,4 @@ jobs: env: GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ github.token }} + REF_NAME: ${{ github.ref_name }} From b319c05974e504a881e8f004505fc2c42d89d0fe Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:14:14 +0000 Subject: [PATCH 23/50] ci: integrate changes from main --- .github/workflows/wc-dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wc-dependency-review.yml b/.github/workflows/wc-dependency-review.yml index 94579167..30c8d8df 100644 --- a/.github/workflows/wc-dependency-review.yml +++ b/.github/workflows/wc-dependency-review.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - - uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4.8.0 + - uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 with: comment-summary-in-pr: on-failure fail-on-severity: critical From b6a1ffe3e842dc08f0259647b64dcc7e0d5293bb Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:57:32 +0000 Subject: [PATCH 24/50] ci: more refactoring --- .github/workflows/wc-build-push-test.yml | 2 +- .github/workflows/wc-build-push.yml | 56 ++++++++---------------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 099c292c..1a3c9861 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -30,7 +30,7 @@ jobs: packages: write pull-requests: write with: - devcontainer-metadata: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json + devcontainer-metadata-file: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile image-name: ${{ github.repository }}-${{ matrix.flavor }} diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 231511e5..843ca20e 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -12,7 +12,7 @@ on: 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: + devcontainer-metadata-file: description: "Path to a JSON file containing devcontainer metadata to add as a label to the built image" required: false type: string @@ -75,6 +75,8 @@ jobs: with: persist-credentials: false - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + with: + cache-binary: false - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 env: USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} @@ -93,16 +95,16 @@ jobs: run: | set -Eeuo pipefail - if [ -z "${DEVCONTAINER_METADATA:-}" ] || [ ! -f "${DEVCONTAINER_METADATA}" ]; then - echo "devcontainer-metadata input not set or file does not exist, skipping devcontainer.metadata label" - echo "metadata=" >> "$GITHUB_OUTPUT" + if [ -z "${DEVCONTAINER_METADATA_FILE:-}" ] || [ ! -f "${DEVCONTAINER_METADATA_FILE}" ]; then + echo "devcontainer-metadata-file input not set or file does not exist, skipping devcontainer.metadata label" + echo "label=" >> "$GITHUB_OUTPUT" exit 0 fi # the sed expression is a workaround for quotes being eaten in arrays (e.g. ["x", "y", "z"] -> ["x",y,"z"]) - echo "metadata=devcontainer.metadata=$(jq -cj '[.]' "${DEVCONTAINER_METADATA}" | sed 's/,"/, "/g')" >> "$GITHUB_OUTPUT" + echo "label=devcontainer.metadata=$(jq -cj '[.]' "${DEVCONTAINER_METADATA_FILE}" | sed 's/,"/, "/g')" >> "$GITHUB_OUTPUT" env: - DEVCONTAINER_METADATA: ${{ inputs.devcontainer-metadata }} + DEVCONTAINER_METADATA_FILE: ${{ inputs.devcontainer-metadata-file }} id: devcontainer-metadata - run: echo "git-commit-epoch=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT" id: devcontainer-epoch @@ -129,7 +131,6 @@ jobs: touch "${RUNNER_TEMP}/digests/${DIGEST#sha256:}" env: DIGEST: ${{ steps.build-and-push.outputs.digest }} - RUNNER_TEMP: ${{ runner.temp }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: digests-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ steps.devcontainer-arch.outputs.arch }} @@ -167,6 +168,8 @@ jobs: pattern: digests-${{ needs.sanitize-image-name.outputs.image-basename }}-* merge-multiple: true - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + with: + cache-binary: false - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 env: USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} @@ -179,7 +182,6 @@ jobs: id: metadata env: DOCKER_METADATA_ANNOTATIONS_LEVELS: index - DOCKER_METADATA_SET_OUTPUT_ENV: false with: images: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} # Generate Docker tags based on the following events/attributes. @@ -197,38 +199,16 @@ jobs: run: | set -Eeuo pipefail - CMD=(docker buildx imagetools create) - - # Build tag and annotation lists from metadata action output - mapfile -t TAGS < <(jq -r '.tags[]? // empty' <<<"${METADATA_JSON}") || true - mapfile -t ANNOTATIONS < <(jq -r '.annotations[]? // empty' <<<"${METADATA_JSON}") || true - - for ann in "${ANNOTATIONS[@]:-}"; do - [ -n "${ann}" ] && CMD+=( --annotation "${ann}" ) + readarray -t lines <<< "$DOCKER_METADATA_OUTPUT_ANNOTATIONS" + annotations=() + for line in "${lines[@]}"; do + annotations+=(--annotation "$line") done - for tag in "${TAGS[@]:-}"; do - [ -n "${tag}" ] && CMD+=( --tag "${tag}" ) - done - - # Each file in the working directory represents a digest (either named sha256: or the 64-char digest itself) - for f in *; do - [ -f "$f" ] || continue - digest="" - if [[ "$f" == sha256:* ]]; then - digest="${f#sha256:}" - elif [[ ${#f} -eq 64 && "$f" =~ ^[a-f0-9]{64}$ ]]; then - digest="$f" - else - continue - fi - CMD+=( "${CONTAINER}@sha256:${digest}" ) - done - - echo "Creating manifest list with command:" >&2 - printf ' %q' "${CMD[@]}" >&2; echo >&2 - # Execute the command - "${CMD[@]}" + docker buildx imagetools create \ + "${annotations[@]}" \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "${METADATA_JSON}") \ + $(printf "${CONTAINER}@sha256:%s " *) env: CONTAINER: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} METADATA_JSON: ${{ steps.metadata.outputs.json }} From 7bd23e7d45ce55466a9b00b0b716d72b457f6cd0 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:27:15 +0000 Subject: [PATCH 25/50] ci: fix cancellation and metadata label --- .github/workflows/pr-conventional-title.yml | 2 +- .github/workflows/wc-acceptance-test.yml | 4 ++-- .github/workflows/wc-build-push-test.yml | 2 +- .github/workflows/wc-build-push.yml | 2 +- .github/workflows/wc-integration-test.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr-conventional-title.yml b/.github/workflows/pr-conventional-title.yml index 22ad16ab..14740507 100644 --- a/.github/workflows/pr-conventional-title.yml +++ b/.github/workflows/pr-conventional-title.yml @@ -33,7 +33,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 - if: always() && steps.pr-title.outputs.error_message != null + if: !cancelled() && steps.pr-title.outputs.error_message != null with: header: pr-title-lint-error message: | diff --git a/.github/workflows/wc-acceptance-test.yml b/.github/workflows/wc-acceptance-test.yml index 6390a304..59fb0d3e 100644 --- a/.github/workflows/wc-acceptance-test.yml +++ b/.github/workflows/wc-acceptance-test.yml @@ -30,7 +30,7 @@ jobs: steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: - # Playwright requires root privileges to install browsers + disable-sudo: false # Playwright requires root privileges to install browsers egress-policy: audit - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: @@ -90,7 +90,7 @@ jobs: GITHUB_TOTP_SECRET: ${{ secrets.TEST_GITHUB_TOTP_SECRET }} PLAYWRIGHT_JUNIT_OUTPUT_NAME: ${{ github.workspace }}/test-report-acceptance-${{ inputs.flavor }}.xml - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() + if: !cancelled() with: name: test-results-acceptance-${{ inputs.flavor }} path: | diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 1a3c9861..b617f0b0 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -73,7 +73,7 @@ jobs: checks: write pull-requests: write needs: [acceptance-test, integration-test] - if: always() + if: !cancelled() steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 843ca20e..36fae5ba 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -120,7 +120,7 @@ jobs: tags: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} labels: | ${{ steps.metadata.outputs.labels }} - ${{ steps.devcontainer-metadata.outputs.metadata }} + ${{ steps.devcontainer-metadata.outputs.label }} annotations: ${{ steps.metadata.outputs.annotations }} sbom: true outputs: type=image,push-by-digest=true,name-canonical=true diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 83cd17ae..dfbfd259 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -91,7 +91,7 @@ jobs: TEST_FILE: ${{ inputs.test-file }} RUNNER_ARCH: ${{ needs.determine-container.outputs.runner-arch }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() + if: !cancelled() with: name: test-results-integration-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ needs.determine-container.outputs.runner-arch }} path: test-report-*.xml From 9db4d23bfa115df40e230838e62ad984f2fa7179 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:34:02 +0000 Subject: [PATCH 26/50] ci: fix cancellation --- .github/workflows/pr-conventional-title.yml | 2 +- .github/workflows/wc-acceptance-test.yml | 2 +- .github/workflows/wc-build-push-test.yml | 2 +- .github/workflows/wc-integration-test.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr-conventional-title.yml b/.github/workflows/pr-conventional-title.yml index 14740507..7bcc116e 100644 --- a/.github/workflows/pr-conventional-title.yml +++ b/.github/workflows/pr-conventional-title.yml @@ -33,7 +33,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 - if: !cancelled() && steps.pr-title.outputs.error_message != null + if: ${{ !cancelled() && steps.pr-title.outputs.error_message != null }} with: header: pr-title-lint-error message: | diff --git a/.github/workflows/wc-acceptance-test.yml b/.github/workflows/wc-acceptance-test.yml index 59fb0d3e..d652e8ce 100644 --- a/.github/workflows/wc-acceptance-test.yml +++ b/.github/workflows/wc-acceptance-test.yml @@ -90,7 +90,7 @@ jobs: GITHUB_TOTP_SECRET: ${{ secrets.TEST_GITHUB_TOTP_SECRET }} PLAYWRIGHT_JUNIT_OUTPUT_NAME: ${{ github.workspace }}/test-report-acceptance-${{ inputs.flavor }}.xml - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: !cancelled() + if: ${{ !cancelled() }} with: name: test-results-acceptance-${{ inputs.flavor }} path: | diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index b617f0b0..5ff72dba 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -73,7 +73,7 @@ jobs: checks: write pull-requests: write needs: [acceptance-test, integration-test] - if: !cancelled() + if: ${{ !cancelled() }} steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index dfbfd259..49e9a263 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -91,7 +91,7 @@ jobs: TEST_FILE: ${{ inputs.test-file }} RUNNER_ARCH: ${{ needs.determine-container.outputs.runner-arch }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: !cancelled() + if: ${{ !cancelled() }} with: name: test-results-integration-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ needs.determine-container.outputs.runner-arch }} path: test-report-*.xml From fa94ecc7f06237ad84e8cd06ba3480cdb0e96bf3 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:45:18 +0000 Subject: [PATCH 27/50] ci: refactor to multiple re-usable layers --- .github/workflows/continuous-integration.yml | 2 +- .github/workflows/release-build.yml | 2 +- .../workflows/wc-build-push-test-flavor.yml | 92 +++++++++++++++++ .github/workflows/wc-build-push-test.yml | 99 +++++++++++-------- .github/workflows/wc-build-push.yml | 61 +++++------- .github/workflows/wc-sanitize-image-name.yml | 7 -- 6 files changed, 177 insertions(+), 86 deletions(-) create mode 100644 .github/workflows/wc-build-push-test-flavor.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 3de63165..14f2908a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -14,7 +14,7 @@ permissions: {} jobs: build-push-test: - uses: ./.github/workflows/wc-build-push-test.yml + uses: ./.github/workflows/wc-build-push-test-flavor.yml secrets: TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 3f7da266..c80cfd3c 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -16,7 +16,7 @@ permissions: {} jobs: build-push-test: - uses: ./.github/workflows/wc-build-push-test.yml + uses: ./.github/workflows/wc-build-push-test-flavor.yml secrets: TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} diff --git a/.github/workflows/wc-build-push-test-flavor.yml b/.github/workflows/wc-build-push-test-flavor.yml new file mode 100644 index 00000000..0c460bb4 --- /dev/null +++ b/.github/workflows/wc-build-push-test-flavor.yml @@ -0,0 +1,92 @@ +--- +name: Build, Push & Test + +on: + workflow_call: + secrets: + TEST_GITHUB_TOKEN: + required: true + TEST_GITHUB_USER: + required: true + TEST_GITHUB_PASSWORD: + required: true + TEST_GITHUB_TOTP_SECRET: + required: true + +permissions: {} + +jobs: + build-push: + strategy: + matrix: + flavor: [cpp, rust] + uses: ./.github/workflows/wc-build-push.yml + permissions: + actions: read + attestations: write + contents: write + id-token: write + packages: write + pull-requests: write + with: + devcontainer-metadata-file: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json + dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile + image-name: ${{ github.repository }}-${{ matrix.flavor }} + + dependency-review: + needs: build-push + uses: ./.github/workflows/wc-dependency-review.yml + permissions: + contents: read + pull-requests: write + + integration-test: + strategy: + matrix: + flavor: [cpp, rust] + runner: ["ubuntu-latest", "ubuntu-24.04-arm"] + needs: build-push + uses: ./.github/workflows/wc-integration-test.yml + with: + image-name: ${{ github.repository }}-${{ matrix.flavor }} + test-file: test/${{ matrix.flavor }}/integration-tests.bats + runner-labels: ${{ matrix.runner }} + + acceptance-test: + strategy: + matrix: + flavor: [cpp] + needs: build-push + uses: ./.github/workflows/wc-acceptance-test.yml + secrets: + TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} + TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} + TEST_GITHUB_PASSWORD: ${{ secrets.TEST_GITHUB_PASSWORD }} + TEST_GITHUB_TOTP_SECRET: ${{ secrets.TEST_GITHUB_TOTP_SECRET }} + with: + flavor: ${{ matrix.flavor }} + + publish-test-results: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + needs: [acceptance-test, integration-test] + if: ${{ !cancelled() }} + steps: + - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + disable-sudo: true + egress-policy: audit + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + merge-multiple: true + pattern: test-results-* + - uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0 + with: + files: test-report-*.xml + + generate-documents: + uses: ./.github/workflows/wc-document-generation.yml + permissions: + contents: read diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 5ff72dba..805e6257 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -3,6 +3,46 @@ name: Build, Push & Test on: workflow_call: + inputs: + 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-file: + 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_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD secrets must be set if not using GitHub Container Registry" + required: false + type: string + default: "ghcr.io" + build-test-runner-labels: + description: >- + JSON object passed to fromJson to become the build matrix. Example: + '["ubuntu-latest", "ubuntu-24.04-arm"]' + required: false + type: string + default: '["ubuntu-latest", "ubuntu-24.04-arm"]' + runner-labels: + description: >- + Single runner label OR JSON array of runner labels for non-build jobs. + Examples: + ubuntu-latest + '["ubuntu-latest"]' + '["self-hosted", "linux", "x86_64"]' + Provide a valid JSON array (starting with '[') to use multiple labels; any other value is treated as a single label string. + required: false + type: string + default: ubuntu-latest + integration-test-file: + description: "Path to the BATS test file to run for integration tests" + required: false + type: string secrets: TEST_GITHUB_TOKEN: required: true @@ -12,15 +52,17 @@ on: required: true TEST_GITHUB_TOTP_SECRET: required: true + DOCKER_REGISTRY_USERNAME: + description: "User name for Docker login, if not provided the GitHub actor will be used" + required: false + DOCKER_REGISTRY_PASSWORD: + description: "Password or token for Docker login, if not provided the GitHub token will be used" + required: false -permissions: - contents: read +permissions: {} jobs: build-push: - strategy: - matrix: - flavor: [cpp, rust] uses: ./.github/workflows/wc-build-push.yml permissions: actions: read @@ -29,50 +71,34 @@ jobs: id-token: write packages: write pull-requests: write + secrets: + DOCKER_REGISTRY_USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} + DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} with: - devcontainer-metadata-file: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json - dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile - image-name: ${{ github.repository }}-${{ matrix.flavor }} - - dependency-review: - needs: build-push - uses: ./.github/workflows/wc-dependency-review.yml - permissions: - contents: read - pull-requests: write + dockerfile: ${{ inputs.dockerfile }} + registry: ${{ inputs.registry }} + image-name: ${{ inputs.image-name }} + devcontainer-metadata-file: ${{ inputs.devcontainer-metadata-file }} + runner-labels: ${{ inputs.runner-labels }} + build-test-runner-labels: ${{ inputs.build-test-runner-labels }} integration-test: strategy: matrix: - flavor: [cpp, rust] - runner: ["ubuntu-latest", "ubuntu-24.04-arm"] + runner: ${{ (startsWith(inputs.build-test-runner-labels, '[') && endsWith(inputs.build-test-runner-labels, ']')) && fromJson(inputs.build-test-runner-labels) || inputs.build-test-runner-labels }} needs: build-push uses: ./.github/workflows/wc-integration-test.yml with: - image-name: ${{ github.repository }}-${{ matrix.flavor }} - test-file: test/${{ matrix.flavor }}/integration-tests.bats + image-name: ${{ inputs.image-name }} + test-file: ${{ inputs.integration-test-file }} runner-labels: ${{ matrix.runner }} - acceptance-test: - strategy: - matrix: - flavor: [cpp] - needs: build-push - uses: ./.github/workflows/wc-acceptance-test.yml - secrets: - TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} - TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} - TEST_GITHUB_PASSWORD: ${{ secrets.TEST_GITHUB_PASSWORD }} - TEST_GITHUB_TOTP_SECRET: ${{ secrets.TEST_GITHUB_TOTP_SECRET }} - with: - flavor: ${{ matrix.flavor }} - publish-test-results: runs-on: ubuntu-latest permissions: checks: write pull-requests: write - needs: [acceptance-test, integration-test] + needs: [integration-test] if: ${{ !cancelled() }} steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 @@ -86,8 +112,3 @@ jobs: - uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0 with: files: test-report-*.xml - - generate-documents: - uses: ./.github/workflows/wc-document-generation.yml - permissions: - contents: read diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 36fae5ba..e8b4b2ba 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -1,3 +1,13 @@ +# This is a lower-level re-usable workflow that builds and pushes +# a multi-architecture devcontainer image to a container registry. +# +# It is intended to be called by a higher-level workflow that provides +# the necessary inputs and secrets. To prevent duplication, the inputs +# and secrets don't contain any defaults or descriptions and all of them +# are required. +# +# See the top-level workflow `wc-build-push-test.yml` for an example. + --- name: Build & Push @@ -5,48 +15,28 @@ on: workflow_call: inputs: 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-file: - description: "Path to a JSON file containing devcontainer metadata to add as a label to the built image" - required: false + required: true type: string registry: - description: "Docker registry to push built containers to, DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD secrets must be set if not using GitHub Container Registry" - required: false + required: true 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 + build-test-runner-labels: + required: true type: string - default: '{"runner": ["ubuntu-latest", "ubuntu-24.04-arm"]}' runner-labels: - description: >- - Single runner label OR JSON array of runner labels for non-build jobs. - Examples: - ubuntu-latest - '["ubuntu-latest"]' - '["self-hosted", "linux", "x86_64"]' - Provide a valid JSON array (starting with '[') to use multiple labels; any other value is treated as a single label string. - required: false + required: true type: string - default: ubuntu-latest secrets: DOCKER_REGISTRY_USERNAME: - description: "User name for Docker login, if not provided the GitHub actor will be used" - required: false + required: true DOCKER_REGISTRY_PASSWORD: - description: "Password or token for Docker login, if not provided the GitHub token will be used" - required: false + required: true permissions: {} @@ -60,7 +50,8 @@ jobs: build-push: strategy: - matrix: ${{ fromJson(inputs.build-matrix) }} + matrix: + runner: ${{ (startsWith(inputs.build-test-runner-labels, '[') && endsWith(inputs.build-test-runner-labels, ']')) && fromJson(inputs.build-test-runner-labels) || inputs.build-test-runner-labels }} runs-on: ${{ matrix.runner }} needs: sanitize-image-name permissions: @@ -78,13 +69,10 @@ jobs: with: cache-binary: false - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 - env: - USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} - PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} with: registry: ${{ inputs.registry }} - username: ${{ env.USERNAME }} - password: ${{ env.PASSWORD }} + username: ${{ secrets.DOCKER_REGISTRY_USERNAME }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} - uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 env: DOCKER_METADATA_SET_OUTPUT_ENV: false @@ -171,13 +159,10 @@ jobs: with: cache-binary: false - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 - env: - USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} - PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} with: registry: ${{ inputs.registry }} - username: ${{ env.USERNAME }} - password: ${{ env.PASSWORD }} + username: ${{ secrets.DOCKER_REGISTRY_USERNAME }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} - uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 id: metadata env: diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index 5c2b7b18..17e8aecd 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -5,18 +5,12 @@ on: workflow_call: inputs: 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 registry: - description: "Container registry to push the image to, e.g. ghcr.io" required: true type: string runner-labels: - description: >- - Runner selection for this job. Accepts either a single label (e.g. ubuntu-latest) - or a JSON array of labels (e.g. ["self-hosted", "linux", "x86_64"]). - If the value begins with '[' it will be parsed as JSON. required: true type: string outputs: @@ -34,7 +28,6 @@ permissions: {} jobs: sanitize-inputs: - # Allow either raw single label or JSON array of labels runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} outputs: image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} From 613704804b845ed29483588b4993be087a56856b Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:49:12 +0000 Subject: [PATCH 28/50] ci: fix internal scenario --- .github/workflows/wc-build-push-test-flavor.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/wc-build-push-test-flavor.yml b/.github/workflows/wc-build-push-test-flavor.yml index 0c460bb4..a40e37cb 100644 --- a/.github/workflows/wc-build-push-test-flavor.yml +++ b/.github/workflows/wc-build-push-test-flavor.yml @@ -28,10 +28,16 @@ jobs: id-token: write packages: write pull-requests: write + secrets: + DOCKER_REGISTRY_USERNAME: ${{ github.actor }} + DOCKER_REGISTRY_PASSWORD: ${{ github.token }} with: devcontainer-metadata-file: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile image-name: ${{ github.repository }}-${{ matrix.flavor }} + registry: ghcr.io + runner-labels: ubuntu-latest + build-test-runner-labels: '["ubuntu-latest", "ubuntu-24.04-arm"]' dependency-review: needs: build-push From b8cab6586bff267ec8780215be4ad61a9a5c5afb Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:52:11 +0000 Subject: [PATCH 29/50] ci: fix permissions --- .github/workflows/wc-build-push-test-flavor.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/wc-build-push-test-flavor.yml b/.github/workflows/wc-build-push-test-flavor.yml index a40e37cb..690e4bf4 100644 --- a/.github/workflows/wc-build-push-test-flavor.yml +++ b/.github/workflows/wc-build-push-test-flavor.yml @@ -53,6 +53,8 @@ jobs: runner: ["ubuntu-latest", "ubuntu-24.04-arm"] needs: build-push uses: ./.github/workflows/wc-integration-test.yml + permissions: + contents: read with: image-name: ${{ github.repository }}-${{ matrix.flavor }} test-file: test/${{ matrix.flavor }}/integration-tests.bats @@ -64,6 +66,8 @@ jobs: flavor: [cpp] needs: build-push uses: ./.github/workflows/wc-acceptance-test.yml + permissions: + contents: read secrets: TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} From 9288a03aadfd29d234007c4e4899536e60a93e0b Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:09:06 +0000 Subject: [PATCH 30/50] ci: fix issues with secrets and refactor further --- .github/copilot-instructions.md | 7 +++ .github/workflows/continuous-integration.yml | 45 +++++++++++++++++++- .github/workflows/wc-build-push-test.yml | 25 ++--------- .github/workflows/wc-build-push.yml | 8 ++-- 4 files changed, 57 insertions(+), 28 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b2614955..70288fba 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -24,3 +24,10 @@ The folder structure of amp-devcontainer is described below, adhere to the exist - `/.devcontainer/[flavor]-test`: Contains a devcontainer.json file for testing the container flavor. - `/.github`: Contains the GitHub workflows for CI/CD, linter configuration, issue templates and re-usable actions. - `/test/[flavor]`: Contains [Bats](https://bats-core.readthedocs.io/en/stable/) integration- and Playwright verification tests for the containers. + +## File Specific Instructions + +When reviewing GitHub Action workflows, ensure that: + +- Workflows that have a workflow_call trigger have the file name prefixed with `wc-`. +- For all re-usable workflows, only the top-level workflow has defaults and descriptions for inputs to avoid duplication. Top-level workflows are not called themselves by other workflows with workflow_call. diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 14f2908a..aee83903 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -13,8 +13,12 @@ concurrency: permissions: {} jobs: - build-push-test: - uses: ./.github/workflows/wc-build-push-test-flavor.yml + build-push-test-flavors: + name: Build, Push and Test (๐Ÿจ ${{ matrix.flavor }}) + strategy: + matrix: + flavor: [cpp, rust] + uses: ./.github/workflows/wc-build-push-test.yml secrets: TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} @@ -28,3 +32,40 @@ jobs: id-token: write packages: write pull-requests: write + with: + devcontainer-metadata-file: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json + dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile + image-name: ${{ github.repository }}-${{ matrix.flavor }} + integration-test-file: test/${{ matrix.flavor }}/integration-tests.bats + + dependency-review: + needs: build-push-test-flavors + uses: ./.github/workflows/wc-dependency-review.yml + permissions: + contents: read + pull-requests: write + + publish-test-results: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + needs: build-push-test-flavors + if: ${{ !cancelled() }} + steps: + - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + disable-sudo: true + egress-policy: audit + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + merge-multiple: true + pattern: test-results-* + - uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0 + with: + files: test-report-*.xml + + generate-documents: + uses: ./.github/workflows/wc-document-generation.yml + permissions: + contents: read diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 805e6257..eece9b07 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -72,8 +72,8 @@ jobs: packages: write pull-requests: write secrets: - DOCKER_REGISTRY_USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} - DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} + DOCKER_REGISTRY_USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME }} + DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} with: dockerfile: ${{ inputs.dockerfile }} registry: ${{ inputs.registry }} @@ -83,6 +83,7 @@ jobs: build-test-runner-labels: ${{ inputs.build-test-runner-labels }} integration-test: + if: ${{ inputs.integration-test-file }} strategy: matrix: runner: ${{ (startsWith(inputs.build-test-runner-labels, '[') && endsWith(inputs.build-test-runner-labels, ']')) && fromJson(inputs.build-test-runner-labels) || inputs.build-test-runner-labels }} @@ -92,23 +93,3 @@ jobs: image-name: ${{ inputs.image-name }} test-file: ${{ inputs.integration-test-file }} runner-labels: ${{ matrix.runner }} - - publish-test-results: - runs-on: ubuntu-latest - permissions: - checks: write - pull-requests: write - needs: [integration-test] - if: ${{ !cancelled() }} - steps: - - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - disable-sudo: true - egress-policy: audit - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - with: - merge-multiple: true - pattern: test-results-* - - uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0 - with: - files: test-report-*.xml diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index e8b4b2ba..21f2fc65 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -71,8 +71,8 @@ jobs: - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ${{ inputs.registry }} - username: ${{ secrets.DOCKER_REGISTRY_USERNAME }} - password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + username: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} - uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 env: DOCKER_METADATA_SET_OUTPUT_ENV: false @@ -161,8 +161,8 @@ jobs: - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ${{ inputs.registry }} - username: ${{ secrets.DOCKER_REGISTRY_USERNAME }} - password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + username: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} - uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 id: metadata env: From 6c39b6c5d545c7efb32d4b10725e2db2a0c79471 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:10:48 +0000 Subject: [PATCH 31/50] ci: fix permissions --- .github/workflows/wc-build-push-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index eece9b07..9392fa98 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -89,6 +89,8 @@ jobs: runner: ${{ (startsWith(inputs.build-test-runner-labels, '[') && endsWith(inputs.build-test-runner-labels, ']')) && fromJson(inputs.build-test-runner-labels) || inputs.build-test-runner-labels }} needs: build-push uses: ./.github/workflows/wc-integration-test.yml + permissions: + contents: read with: image-name: ${{ inputs.image-name }} test-file: ${{ inputs.integration-test-file }} From c84a3f107cc73a620f7883fe3f22e66d4abc292a Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:41:44 +0000 Subject: [PATCH 32/50] ci: suppress invalid linter finding --- .github/workflows/wc-build-push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 21f2fc65..64590fe1 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -190,6 +190,7 @@ jobs: annotations+=(--annotation "$line") done + # shellcheck disable=SC2046 docker buildx imagetools create \ "${annotations[@]}" \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "${METADATA_JSON}") \ From 275b8af72bdb82d08fda62b9d85dfe05a80e7d89 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 08:01:18 +0000 Subject: [PATCH 33/50] ci: make acceptance test secrest optional --- .github/workflows/wc-build-push-test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 9392fa98..b3b7f16d 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -45,13 +45,13 @@ on: type: string secrets: TEST_GITHUB_TOKEN: - required: true + required: false TEST_GITHUB_USER: - required: true + required: false TEST_GITHUB_PASSWORD: - required: true + required: false TEST_GITHUB_TOTP_SECRET: - required: true + required: false DOCKER_REGISTRY_USERNAME: description: "User name for Docker login, if not provided the GitHub actor will be used" required: false From 09677d005907d6c42264e89bef3b8bb8e8195c80 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 08:17:08 +0000 Subject: [PATCH 34/50] chore: refactor copilot instructions --- .github/copilot-instructions.md | 7 ------- .github/instructions/actions.instructions.md | 12 ++++++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 .github/instructions/actions.instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 70288fba..b2614955 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -24,10 +24,3 @@ The folder structure of amp-devcontainer is described below, adhere to the exist - `/.devcontainer/[flavor]-test`: Contains a devcontainer.json file for testing the container flavor. - `/.github`: Contains the GitHub workflows for CI/CD, linter configuration, issue templates and re-usable actions. - `/test/[flavor]`: Contains [Bats](https://bats-core.readthedocs.io/en/stable/) integration- and Playwright verification tests for the containers. - -## File Specific Instructions - -When reviewing GitHub Action workflows, ensure that: - -- Workflows that have a workflow_call trigger have the file name prefixed with `wc-`. -- For all re-usable workflows, only the top-level workflow has defaults and descriptions for inputs to avoid duplication. Top-level workflows are not called themselves by other workflows with workflow_call. diff --git a/.github/instructions/actions.instructions.md b/.github/instructions/actions.instructions.md new file mode 100644 index 00000000..f366a496 --- /dev/null +++ b/.github/instructions/actions.instructions.md @@ -0,0 +1,12 @@ +--- +applyTo: ".github/workflows/*.{yml,yaml}" +--- + +# GitHub Actions Guidelines + +When writing GitHub Action workflows, ensure that: + +- Workflows that have a workflow_call trigger have the file name prefixed with `wc-`. +- For all re-usable workflows, only the top-level workflow (workflows that are not called themselves by other workflows with workflow_call) has defaults and descriptions for inputs to avoid duplication. +- The sorting order for inputs, secrets, and outputs is alphabetical. +- The sorting order of other keys is consistent across the repository. From 0e40b8373cf0c975994e91453a49aed903212246 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 08:41:32 +0000 Subject: [PATCH 35/50] ci: block network access when not required --- .github/workflows/wc-sanitize-image-name.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index 17e8aecd..d79bd1fc 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -36,8 +36,8 @@ jobs: steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: - disable-sudo: true - egress-policy: audit + disable-sudo-and-containers: true + egress-policy: block - name: Sanitize image name id: sanitize-image-name env: From 5351af7d50c3d73bb283ee75c2d94df38f3331f2 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:13:04 +0000 Subject: [PATCH 36/50] ci: add acceptance tests and reduce duplication --- .github/workflows/continuous-integration.yml | 2 + .github/workflows/wc-acceptance-test.yml | 20 ++-- .../workflows/wc-build-push-test-flavor.yml | 102 ------------------ .github/workflows/wc-build-push-test.yml | 28 ++++- .github/workflows/wc-build-push.yml | 9 ++ .github/workflows/wc-integration-test.yml | 54 +++------- 6 files changed, 64 insertions(+), 151 deletions(-) delete mode 100644 .github/workflows/wc-build-push-test-flavor.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index aee83903..aa43a899 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -37,6 +37,8 @@ jobs: dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile image-name: ${{ github.repository }}-${{ matrix.flavor }} integration-test-file: test/${{ matrix.flavor }}/integration-tests.bats + acceptance-test-path: ${{ matrix.flavor == 'cpp' && 'test/cpp/features' || '' }} + test-devcontainer-file: ${{ matrix.flavor == 'cpp' && '.devcontainer/cpp-test/devcontainer.json' || '' }} dependency-review: needs: build-push-test-flavors diff --git a/.github/workflows/wc-acceptance-test.yml b/.github/workflows/wc-acceptance-test.yml index d652e8ce..7532b7f5 100644 --- a/.github/workflows/wc-acceptance-test.yml +++ b/.github/workflows/wc-acceptance-test.yml @@ -4,7 +4,13 @@ name: Acceptance Test on: workflow_call: inputs: - flavor: + image-basename: + required: true + type: string + devcontainer-file: + required: true + type: string + acceptance-test-path: required: true type: string secrets: @@ -49,9 +55,9 @@ jobs: gh secret set -a codespaces IMAGE_VERSION --body "edge" fi - echo CODESPACE_NAME="$(gh codespace create -R "${{ github.repository }}" -b "$HEAD_REF" -m basicLinux32gb --devcontainer-path ".devcontainer/${CONTAINER_FLAVOR}-test/devcontainer.json" --idle-timeout 10m --retention-period 1h)" >> "$GITHUB_ENV" + echo CODESPACE_NAME="$(gh codespace create -R "${{ github.repository }}" -b "$HEAD_REF" -m basicLinux32gb --devcontainer-path "${DEVCONTAINER_FILE}" --idle-timeout 10m --retention-period 1h)" >> "$GITHUB_ENV" env: - CONTAINER_FLAVOR: ${{ inputs.flavor }} + DEVCONTAINER_FILE: ${{ inputs.devcontainer-file }} GH_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} HEAD_REF: ${{ github.head_ref }} - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 @@ -82,17 +88,17 @@ jobs: done env: GH_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} - - run: cd "test/${CONTAINER_FLAVOR}/features" && npm test + - run: cd "${ACCEPTANCE_TEST_PATH}" && npm test env: - CONTAINER_FLAVOR: ${{ inputs.flavor }} + ACCEPTANCE_TEST_PATH: ${{ inputs.acceptance-test-path }} GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} GITHUB_PASSWORD: ${{ secrets.TEST_GITHUB_PASSWORD }} GITHUB_TOTP_SECRET: ${{ secrets.TEST_GITHUB_TOTP_SECRET }} - PLAYWRIGHT_JUNIT_OUTPUT_NAME: ${{ github.workspace }}/test-report-acceptance-${{ inputs.flavor }}.xml + PLAYWRIGHT_JUNIT_OUTPUT_NAME: ${{ github.workspace }}/test-report-acceptance-${{ inputs.image-basename }}.xml - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: ${{ !cancelled() }} with: - name: test-results-acceptance-${{ inputs.flavor }} + name: test-results-acceptance-${{ inputs.image-basename }} path: | test-report-*.xml test-results/ diff --git a/.github/workflows/wc-build-push-test-flavor.yml b/.github/workflows/wc-build-push-test-flavor.yml deleted file mode 100644 index 690e4bf4..00000000 --- a/.github/workflows/wc-build-push-test-flavor.yml +++ /dev/null @@ -1,102 +0,0 @@ ---- -name: Build, Push & Test - -on: - workflow_call: - secrets: - TEST_GITHUB_TOKEN: - required: true - TEST_GITHUB_USER: - required: true - TEST_GITHUB_PASSWORD: - required: true - TEST_GITHUB_TOTP_SECRET: - required: true - -permissions: {} - -jobs: - build-push: - strategy: - matrix: - flavor: [cpp, rust] - uses: ./.github/workflows/wc-build-push.yml - permissions: - actions: read - attestations: write - contents: write - id-token: write - packages: write - pull-requests: write - secrets: - DOCKER_REGISTRY_USERNAME: ${{ github.actor }} - DOCKER_REGISTRY_PASSWORD: ${{ github.token }} - with: - devcontainer-metadata-file: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json - dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile - image-name: ${{ github.repository }}-${{ matrix.flavor }} - registry: ghcr.io - runner-labels: ubuntu-latest - build-test-runner-labels: '["ubuntu-latest", "ubuntu-24.04-arm"]' - - dependency-review: - needs: build-push - uses: ./.github/workflows/wc-dependency-review.yml - permissions: - contents: read - pull-requests: write - - integration-test: - strategy: - matrix: - flavor: [cpp, rust] - runner: ["ubuntu-latest", "ubuntu-24.04-arm"] - needs: build-push - uses: ./.github/workflows/wc-integration-test.yml - permissions: - contents: read - with: - image-name: ${{ github.repository }}-${{ matrix.flavor }} - test-file: test/${{ matrix.flavor }}/integration-tests.bats - runner-labels: ${{ matrix.runner }} - - acceptance-test: - strategy: - matrix: - flavor: [cpp] - needs: build-push - uses: ./.github/workflows/wc-acceptance-test.yml - permissions: - contents: read - secrets: - TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} - TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} - TEST_GITHUB_PASSWORD: ${{ secrets.TEST_GITHUB_PASSWORD }} - TEST_GITHUB_TOTP_SECRET: ${{ secrets.TEST_GITHUB_TOTP_SECRET }} - with: - flavor: ${{ matrix.flavor }} - - publish-test-results: - runs-on: ubuntu-latest - permissions: - checks: write - pull-requests: write - needs: [acceptance-test, integration-test] - if: ${{ !cancelled() }} - steps: - - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - disable-sudo: true - egress-policy: audit - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - with: - merge-multiple: true - pattern: test-results-* - - uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0 - with: - files: test-report-*.xml - - generate-documents: - uses: ./.github/workflows/wc-document-generation.yml - permissions: - contents: read diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index b3b7f16d..3a0e433d 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -43,6 +43,14 @@ on: description: "Path to the BATS test file to run for integration tests" required: false type: string + test-devcontainer-file: + description: "Path to the devcontainer.json file to use for acceptance tests" + required: false + type: string + acceptance-test-path: + description: "Path to the Playwright acceptance tests (directory that contains playwright.config.ts)" + required: false + type: string secrets: TEST_GITHUB_TOKEN: required: false @@ -92,6 +100,24 @@ jobs: permissions: contents: read with: - image-name: ${{ inputs.image-name }} + fully-qualified-image-name: ${{ needs.build-push.outputs.fully-qualified-image-name }} + image-basename: ${{ needs.build-push.outputs.image-basename }} + image-digest: ${{ needs.build-push.outputs.digest }} test-file: ${{ inputs.integration-test-file }} runner-labels: ${{ matrix.runner }} + + acceptance-test: + if: ${{ inputs.test-devcontainer-file && inputs.acceptance-test-path }} + needs: build-push + uses: ./.github/workflows/wc-acceptance-test.yml + permissions: + contents: read + secrets: + TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} + TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} + TEST_GITHUB_PASSWORD: ${{ secrets.TEST_GITHUB_PASSWORD }} + TEST_GITHUB_TOTP_SECRET: ${{ secrets.TEST_GITHUB_TOTP_SECRET }} + with: + image-basename: ${{ needs.build-push.outputs.image-basename }} + devcontainer-file: ${{ inputs.test-devcontainer-file }} + acceptance-test-path: ${{ inputs.acceptance-test-path }} diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 64590fe1..c951c37f 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -32,6 +32,13 @@ on: runner-labels: required: true type: string + outputs: + fully-qualified-image-name: + value: ${{ jobs.sanitize-image-name.outputs.fully-qualified-image-name }} + image-basename: + value: ${{ jobs.sanitize-image-name.outputs.image-basename }} + digest: + value: ${{ jobs.merge-image.outputs.digest }} secrets: DOCKER_REGISTRY_USERNAME: required: true @@ -142,6 +149,8 @@ jobs: id-token: write packages: write pull-requests: write + outputs: + digest: ${{ steps.inspect-manifest.outputs.digest }} steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 49e9a263..7bf920ad 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -4,7 +4,13 @@ name: Integration Test on: workflow_call: inputs: - image-name: + fully-qualified-image-name: + required: true + type: string + image-basename: + required: true + type: string + image-digest: required: true type: string test-file: @@ -28,44 +34,10 @@ on: permissions: {} jobs: - sanitize-image-name: - uses: ./.github/workflows/wc-sanitize-image-name.yml - with: - image-name: ${{ inputs.image-name }} - registry: ${{ inputs.registry }} - runner-labels: ${{ inputs.runner-labels }} - - determine-container: - runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} - needs: sanitize-image-name - outputs: - container: ${{ steps.set-container.outputs.container }} - runner-arch: ${{ steps.runner-arch.outputs.arch }} - steps: - - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - disable-sudo: true - egress-policy: audit - - run: echo "arch=${RUNNER_ARCH@L}" >> "$GITHUB_OUTPUT" - id: runner-arch - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - with: - path: ${{ runner.temp }}/digests-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} - pattern: digests-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} - - run: echo "container=$(printf "${FULLY_QUALIFIED_IMAGE_NAME}@sha256:%s " *)" >> "$GITHUB_OUTPUT" - working-directory: ${{ runner.temp }}/digests-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} - env: - FULLY_QUALIFIED_IMAGE_NAME: ${{ needs.sanitize-image-name.outputs.fully-qualified-image-name }} - GH_REPO: ${{ github.repository }} - id: set-container - run-test: - needs: - - determine-container - - sanitize-image-name runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} container: - image: ${{ needs.determine-container.outputs.container }} # zizmor: ignore[unpinned-images] This image is actually pinned by sha256 digest + image: ${{ inputs.fully-qualified-image-name }}@${{ inputs.image-digest }} credentials: username: ${{ secrets.DOCKER_REGISTRY_USERNAME || github.actor }} password: ${{ secrets.DOCKER_REGISTRY_PASSWORD || github.token }} @@ -82,16 +54,16 @@ jobs: - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: test/cpp/.xwin-cache - key: xwin-cache-${{ needs.determine-container.outputs.runner-arch }} + key: xwin-cache-${{ runner.arch }} restore-keys: | - xwin-cache-${{ needs.determine-container.outputs.runner-arch }} + xwin-cache-${{ runner.arch }} - run: bats --formatter junit "${TEST_FILE}" | tee "test-report-${IMAGE_BASENAME}-${RUNNER_ARCH}.xml" env: - IMAGE_BASENAME: ${{ needs.sanitize-image-name.outputs.image-basename }} + IMAGE_BASENAME: ${{ inputs.image-basename }} TEST_FILE: ${{ inputs.test-file }} - RUNNER_ARCH: ${{ needs.determine-container.outputs.runner-arch }} + RUNNER_ARCH: ${{ runner.arch }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: ${{ !cancelled() }} with: - name: test-results-integration-${{ needs.sanitize-image-name.outputs.image-basename }}-${{ needs.determine-container.outputs.runner-arch }} + name: test-results-integration-${{ inputs.image-basename }}-${{ runner.arch }} path: test-report-*.xml From 7db655eab172d821272618f1b795374fdf2cda41 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:23:10 +0000 Subject: [PATCH 37/50] ci: fix release build --- ...ns.instructions.md => workflows.instructions.md} | 6 +++--- .github/workflows/release-build.yml | 13 ++++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) rename .github/instructions/{actions.instructions.md => workflows.instructions.md} (72%) diff --git a/.github/instructions/actions.instructions.md b/.github/instructions/workflows.instructions.md similarity index 72% rename from .github/instructions/actions.instructions.md rename to .github/instructions/workflows.instructions.md index f366a496..6be8426c 100644 --- a/.github/instructions/actions.instructions.md +++ b/.github/instructions/workflows.instructions.md @@ -1,12 +1,12 @@ --- -applyTo: ".github/workflows/*.{yml,yaml}" +applyTo: ".github/workflows/*.yml" --- -# GitHub Actions Guidelines +# GitHub Workflows Guidelines When writing GitHub Action workflows, ensure that: -- Workflows that have a workflow_call trigger have the file name prefixed with `wc-`. +- Workflows that have a workflow_call trigger have their filename prefixed with `wc-`. - For all re-usable workflows, only the top-level workflow (workflows that are not called themselves by other workflows with workflow_call) has defaults and descriptions for inputs to avoid duplication. - The sorting order for inputs, secrets, and outputs is alphabetical. - The sorting order of other keys is consistent across the repository. diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index c80cfd3c..42e55c07 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -16,7 +16,11 @@ permissions: {} jobs: build-push-test: - uses: ./.github/workflows/wc-build-push-test-flavor.yml + name: Build, Push and Test (๐Ÿจ ${{ matrix.flavor }}) + strategy: + matrix: + flavor: [cpp, rust] + uses: ./.github/workflows/wc-build-push-test.yml secrets: TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }} TEST_GITHUB_USER: ${{ secrets.TEST_GITHUB_USER }} @@ -30,6 +34,13 @@ jobs: id-token: write packages: write pull-requests: write + with: + devcontainer-metadata-file: .devcontainer/${{ matrix.flavor }}/devcontainer-metadata.json + dockerfile: .devcontainer/${{ matrix.flavor }}/Dockerfile + image-name: ${{ github.repository }}-${{ matrix.flavor }} + integration-test-file: test/${{ matrix.flavor }}/integration-tests.bats + acceptance-test-path: ${{ matrix.flavor == 'cpp' && 'test/cpp/features' || '' }} + test-devcontainer-file: ${{ matrix.flavor == 'cpp' && '.devcontainer/cpp-test/devcontainer.json' || '' }} apply-release-notes-template: runs-on: ubuntu-latest permissions: From 96efdaccdfeea6eab03fccb9ed68a4223a36d3be Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 13:32:35 +0000 Subject: [PATCH 38/50] ci: add requirement and change some naming --- .github/workflows/continuous-integration.yml | 9 ++++++--- .github/workflows/wc-sanitize-image-name.yml | 8 ++++---- test/cpp/features/maintainability.feature | 6 ++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index aa43a899..23e44e92 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -13,7 +13,7 @@ concurrency: permissions: {} jobs: - build-push-test-flavors: + build-push-test: name: Build, Push and Test (๐Ÿจ ${{ matrix.flavor }}) strategy: matrix: @@ -41,18 +41,20 @@ jobs: test-devcontainer-file: ${{ matrix.flavor == 'cpp' && '.devcontainer/cpp-test/devcontainer.json' || '' }} dependency-review: - needs: build-push-test-flavors + name: Review Dependencies + needs: build-push-test uses: ./.github/workflows/wc-dependency-review.yml permissions: contents: read pull-requests: write publish-test-results: + name: Publish Test Results runs-on: ubuntu-latest permissions: checks: write pull-requests: write - needs: build-push-test-flavors + needs: build-push-test if: ${{ !cancelled() }} steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 @@ -68,6 +70,7 @@ jobs: files: test-report-*.xml generate-documents: + name: Generate Documents uses: ./.github/workflows/wc-document-generation.yml permissions: contents: read diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index d79bd1fc..02b5de12 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -16,18 +16,18 @@ on: outputs: image-basename: description: "The sanitized base name of the image (without registry or tag)" - value: ${{ jobs.sanitize-inputs.outputs.image-basename }} + value: ${{ jobs.sanitize.outputs.image-basename }} image-name: description: "The sanitized name of the image (without registry or tag)" - value: ${{ jobs.sanitize-inputs.outputs.image-name }} + value: ${{ jobs.sanitize.outputs.image-name }} fully-qualified-image-name: description: "The fully qualified name of the image including registry (but without tag)" - value: ${{ jobs.sanitize-inputs.outputs.fully-qualified-image-name }} + value: ${{ jobs.sanitize.outputs.fully-qualified-image-name }} permissions: {} jobs: - sanitize-inputs: + sanitize: runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} outputs: image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} diff --git a/test/cpp/features/maintainability.feature b/test/cpp/features/maintainability.feature index 730ce325..aa384582 100644 --- a/test/cpp/features/maintainability.feature +++ b/test/cpp/features/maintainability.feature @@ -18,6 +18,12 @@ Feature: Maintainability This reduces the maintenance burden on users, as they do not need to manually track and apply updates. Automatic updates can also help ensure compatibility with other dependencies and tools, improving the overall stability and reliability of the development environment. + Rule: Re-usable build system + amp-devcontainer *SHOULD* provide re-usable building blocks to enable building, publishing and testing derived containers. + + Providing re-usable building blocks for building, publishing and testing derived containers reduces duplication, and ensures consistent application of practices. + Derived containers (i.e. containers using amp-devcontainer as a base for further extension) should be able to build, push and test in the same way that amp-devcontainer does, without the need to duplicate the build system. + Rule: Architectural decisions amp-devcontainer *SHOULD* document its architectural decisions. From 343f5117bf93d016519264bd49d14d8ccdcf1c59 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 14:39:29 +0000 Subject: [PATCH 39/50] ci: refactor naming --- .github/workflows/continuous-integration.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 23e44e92..0e91d5a1 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -1,5 +1,5 @@ --- -name: Continuous Integration +name: ๐Ÿงช CI on: merge_group: @@ -14,7 +14,7 @@ permissions: {} jobs: build-push-test: - name: Build, Push and Test (๐Ÿจ ${{ matrix.flavor }}) + name: ๐Ÿ› ๏ธ Build โ†’ Push โ†’ Test (๐Ÿจ ${{ matrix.flavor }}) strategy: matrix: flavor: [cpp, rust] @@ -41,7 +41,7 @@ jobs: test-devcontainer-file: ${{ matrix.flavor == 'cpp' && '.devcontainer/cpp-test/devcontainer.json' || '' }} dependency-review: - name: Review Dependencies + name: ๐Ÿ” Dependencies needs: build-push-test uses: ./.github/workflows/wc-dependency-review.yml permissions: @@ -49,7 +49,7 @@ jobs: pull-requests: write publish-test-results: - name: Publish Test Results + name: ๐Ÿ“Š Test Results runs-on: ubuntu-latest permissions: checks: write @@ -70,7 +70,7 @@ jobs: files: test-report-*.xml generate-documents: - name: Generate Documents + name: ๐Ÿ“„ Documentation uses: ./.github/workflows/wc-document-generation.yml permissions: contents: read From 921e1df0a023f42c454e27af003261ef09286060 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 14:54:04 +0000 Subject: [PATCH 40/50] ci: fix zizmore findings --- .github/instructions/workflows.instructions.md | 1 + .github/workflows/image-cleanup.yml | 3 ++- .github/workflows/issue-cleanup.yml | 1 + .github/workflows/linting-formatting.yml | 1 + .github/workflows/ossf-scorecard.yml | 1 + .github/workflows/pr-conventional-title.yml | 1 + .github/workflows/pr-image-cleanup.yml | 2 ++ .github/workflows/pr-report.yml | 1 + .github/workflows/release-build.yml | 3 +++ .github/workflows/release-please.yml | 1 + .github/workflows/social-interaction.yml | 1 + .github/workflows/update-dependencies.yml | 2 ++ .github/workflows/vulnerability-scan.yml | 1 + .github/workflows/wc-acceptance-test.yml | 1 + .github/workflows/wc-build-push-test.yml | 3 +++ .github/workflows/wc-build-push.yml | 3 +++ .github/workflows/wc-dependency-review.yml | 1 + .github/workflows/wc-document-generation.yml | 1 + .github/workflows/wc-integration-test.yml | 1 + .github/workflows/wc-sanitize-image-name.yml | 1 + 20 files changed, 29 insertions(+), 1 deletion(-) diff --git a/.github/instructions/workflows.instructions.md b/.github/instructions/workflows.instructions.md index 6be8426c..2c254630 100644 --- a/.github/instructions/workflows.instructions.md +++ b/.github/instructions/workflows.instructions.md @@ -8,5 +8,6 @@ When writing GitHub Action workflows, ensure that: - Workflows that have a workflow_call trigger have their filename prefixed with `wc-`. - For all re-usable workflows, only the top-level workflow (workflows that are not called themselves by other workflows with workflow_call) has defaults and descriptions for inputs to avoid duplication. +- All workflows and action definitions have a name that is descriptive and concise, using emoji where appropriate. - The sorting order for inputs, secrets, and outputs is alphabetical. - The sorting order of other keys is consistent across the repository. diff --git a/.github/workflows/image-cleanup.yml b/.github/workflows/image-cleanup.yml index 8c519166..13f21fde 100644 --- a/.github/workflows/image-cleanup.yml +++ b/.github/workflows/image-cleanup.yml @@ -9,7 +9,8 @@ on: permissions: {} jobs: - delete-images: + cleanup-images: + name: ๐Ÿงน Clean Images runs-on: ubuntu-latest permissions: # dataaxiom/ghcr-cleanup-action needs packages write permission diff --git a/.github/workflows/issue-cleanup.yml b/.github/workflows/issue-cleanup.yml index eb938768..dbebf1c6 100644 --- a/.github/workflows/issue-cleanup.yml +++ b/.github/workflows/issue-cleanup.yml @@ -9,6 +9,7 @@ permissions: {} jobs: close-issues: + name: โ™ป๏ธ Close Stale Issues & PRs runs-on: ubuntu-latest permissions: issues: write diff --git a/.github/workflows/linting-formatting.yml b/.github/workflows/linting-formatting.yml index 2202898d..314de526 100644 --- a/.github/workflows/linting-formatting.yml +++ b/.github/workflows/linting-formatting.yml @@ -19,6 +19,7 @@ permissions: jobs: linter: + name: ๐Ÿงน Lint & Format runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 46c598be..467f2d8f 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -13,6 +13,7 @@ permissions: read-all jobs: ossf-scorecard: + name: ๐Ÿ›ก๏ธ OpenSSF Scorecard runs-on: ubuntu-latest permissions: security-events: write diff --git a/.github/workflows/pr-conventional-title.yml b/.github/workflows/pr-conventional-title.yml index 7bcc116e..bac8deed 100644 --- a/.github/workflows/pr-conventional-title.yml +++ b/.github/workflows/pr-conventional-title.yml @@ -12,6 +12,7 @@ permissions: {} jobs: validate-pr-title: + name: โœ… Validate PR Title runs-on: ubuntu-latest permissions: pull-requests: write diff --git a/.github/workflows/pr-image-cleanup.yml b/.github/workflows/pr-image-cleanup.yml index 1c770cb4..822618b1 100644 --- a/.github/workflows/pr-image-cleanup.yml +++ b/.github/workflows/pr-image-cleanup.yml @@ -9,6 +9,7 @@ permissions: {} jobs: delete-images: + name: ๐Ÿ—‘๏ธ Delete PR Images runs-on: ubuntu-latest permissions: packages: write @@ -22,6 +23,7 @@ jobs: delete-tags: pr-${{ github.event.pull_request.number }} packages: amp-devcontainer,amp-devcontainer-cpp,amp-devcontainer-rust cleanup-cache: + name: ๐Ÿงน Cleanup Cache runs-on: ubuntu-latest permissions: # actions: write permission is required to delete the cache diff --git a/.github/workflows/pr-report.yml b/.github/workflows/pr-report.yml index 839b6b53..966b17fc 100644 --- a/.github/workflows/pr-report.yml +++ b/.github/workflows/pr-report.yml @@ -9,6 +9,7 @@ permissions: {} jobs: add-pr-report: + name: ๐Ÿ“Š Add PR Report permissions: contents: read checks: read diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 42e55c07..c6e83388 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -42,6 +42,7 @@ jobs: acceptance-test-path: ${{ matrix.flavor == 'cpp' && 'test/cpp/features' || '' }} test-devcontainer-file: ${{ matrix.flavor == 'cpp' && '.devcontainer/cpp-test/devcontainer.json' || '' }} apply-release-notes-template: + name: ๐Ÿ“ Apply Release Template runs-on: ubuntu-latest permissions: # `contents: write` is needed to modify a release. @@ -68,6 +69,7 @@ jobs: GH_TOKEN: ${{ github.token }} REF_NAME: ${{ github.ref_name }} update-release-notes: + name: Update Release Notes (๐Ÿจ ${{ matrix.flavor }}) strategy: matrix: flavor: [cpp, rust] @@ -119,6 +121,7 @@ jobs: GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ github.token }} upload-documents: + name: ๐Ÿ“„ Upload Documents runs-on: ubuntu-latest permissions: # `contents: write` is needed to modify a release. diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 30c23ec8..7417d318 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -14,6 +14,7 @@ permissions: jobs: create-release: + name: ๐Ÿš€ Create Release runs-on: ubuntu-latest steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 diff --git a/.github/workflows/social-interaction.yml b/.github/workflows/social-interaction.yml index 64e07ffb..b4f6dd96 100644 --- a/.github/workflows/social-interaction.yml +++ b/.github/workflows/social-interaction.yml @@ -11,6 +11,7 @@ permissions: {} jobs: greeting: + name: ๐Ÿ‘‹ First Interaction Greeting runs-on: ubuntu-latest permissions: issues: write diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 4a6da0b8..75b36424 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -10,6 +10,7 @@ permissions: {} jobs: update-apt-dependencies: + name: Update APT Dependencies (๐Ÿจ ${{ matrix.flavor }}) runs-on: ubuntu-latest strategy: matrix: @@ -45,6 +46,7 @@ jobs: token: ${{ steps.token.outputs.token }} sign-commits: true update-vscode-extensions: + name: Update VS Code Extensions (๐Ÿจ ${{ matrix.flavor }}, ${{ matrix.file }}) runs-on: ubuntu-latest strategy: matrix: diff --git a/.github/workflows/vulnerability-scan.yml b/.github/workflows/vulnerability-scan.yml index 47ac40b9..1223eabf 100644 --- a/.github/workflows/vulnerability-scan.yml +++ b/.github/workflows/vulnerability-scan.yml @@ -10,6 +10,7 @@ permissions: {} jobs: vulnerability-scan: + name: ๐Ÿ›ก๏ธ Vulnerability Scan (๐Ÿจ ${{ matrix.flavor }}) runs-on: ubuntu-latest strategy: matrix: diff --git a/.github/workflows/wc-acceptance-test.yml b/.github/workflows/wc-acceptance-test.yml index 7532b7f5..38415da9 100644 --- a/.github/workflows/wc-acceptance-test.yml +++ b/.github/workflows/wc-acceptance-test.yml @@ -32,6 +32,7 @@ permissions: jobs: test: + name: ๐Ÿงช Acceptance Test runs-on: ubuntu-latest steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 3a0e433d..ab24157b 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -71,6 +71,7 @@ permissions: {} jobs: build-push: + name: ๐Ÿ› ๏ธ Build โ†’ Push uses: ./.github/workflows/wc-build-push.yml permissions: actions: read @@ -91,6 +92,7 @@ jobs: build-test-runner-labels: ${{ inputs.build-test-runner-labels }} integration-test: + name: ๐Ÿงช Integration Test (${{ matrix.runner }}) if: ${{ inputs.integration-test-file }} strategy: matrix: @@ -107,6 +109,7 @@ jobs: runner-labels: ${{ matrix.runner }} acceptance-test: + name: ๐Ÿงช Acceptance Test if: ${{ inputs.test-devcontainer-file && inputs.acceptance-test-path }} needs: build-push uses: ./.github/workflows/wc-acceptance-test.yml diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index c951c37f..5d8072b2 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -49,6 +49,7 @@ permissions: {} jobs: sanitize-image-name: + name: ๐Ÿงผ Sanitize Image Name uses: ./.github/workflows/wc-sanitize-image-name.yml with: image-name: ${{ inputs.image-name }} @@ -56,6 +57,7 @@ jobs: runner-labels: ${{ inputs.runner-labels }} build-push: + name: ๐Ÿ› ๏ธ Build โ†’ Push (${ { matrix.runner }}) strategy: matrix: runner: ${{ (startsWith(inputs.build-test-runner-labels, '[') && endsWith(inputs.build-test-runner-labels, ']')) && fromJson(inputs.build-test-runner-labels) || inputs.build-test-runner-labels }} @@ -134,6 +136,7 @@ jobs: retention-days: 1 merge-image: + name: ๐Ÿ”— Merge Image & Publish Digest # Support either a plain single label (e.g. ubuntu-latest) OR a JSON array of labels. # If the input starts & ends with brackets we attempt JSON parsing; otherwise we pass the raw string. runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} diff --git a/.github/workflows/wc-dependency-review.yml b/.github/workflows/wc-dependency-review.yml index 30c8d8df..a0a7eb25 100644 --- a/.github/workflows/wc-dependency-review.yml +++ b/.github/workflows/wc-dependency-review.yml @@ -14,6 +14,7 @@ permissions: {} jobs: dependency-review: + name: ๐Ÿ” Dependency Review runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} permissions: contents: read diff --git a/.github/workflows/wc-document-generation.yml b/.github/workflows/wc-document-generation.yml index 48e38b93..241eb719 100644 --- a/.github/workflows/wc-document-generation.yml +++ b/.github/workflows/wc-document-generation.yml @@ -8,6 +8,7 @@ permissions: {} jobs: generate-documents: + name: ๐Ÿ“„ Generate Documents runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 7bf920ad..0b035e77 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -35,6 +35,7 @@ permissions: {} jobs: run-test: + name: ๐Ÿงช Integration Test runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} container: image: ${{ inputs.fully-qualified-image-name }}@${{ inputs.image-digest }} diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index 02b5de12..d5023b59 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -28,6 +28,7 @@ permissions: {} jobs: sanitize: + name: ๐Ÿงผ Sanitize Image Name runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} outputs: image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} From e827c6e2ac99a41116cb1dd4cc559fb58fcde9f0 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:14:50 +0000 Subject: [PATCH 41/50] ci: more name refactoring --- .github/workflows/continuous-integration.yml | 2 +- .github/workflows/wc-build-push-test.yml | 6 +++--- .github/workflows/wc-build-push.yml | 6 +++--- .github/workflows/wc-dependency-review.yml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 0e91d5a1..e3b7d809 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -41,7 +41,7 @@ jobs: test-devcontainer-file: ${{ matrix.flavor == 'cpp' && '.devcontainer/cpp-test/devcontainer.json' || '' }} dependency-review: - name: ๐Ÿ” Dependencies + name: ๐Ÿ” Dependency Review needs: build-push-test uses: ./.github/workflows/wc-dependency-review.yml permissions: diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index ab24157b..52b6979d 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -71,7 +71,7 @@ permissions: {} jobs: build-push: - name: ๐Ÿ› ๏ธ Build โ†’ Push + name: ๐Ÿ› ๏ธ uses: ./.github/workflows/wc-build-push.yml permissions: actions: read @@ -92,7 +92,7 @@ jobs: build-test-runner-labels: ${{ inputs.build-test-runner-labels }} integration-test: - name: ๐Ÿงช Integration Test (${{ matrix.runner }}) + name: ๐Ÿงช if: ${{ inputs.integration-test-file }} strategy: matrix: @@ -109,7 +109,7 @@ jobs: runner-labels: ${{ matrix.runner }} acceptance-test: - name: ๐Ÿงช Acceptance Test + name: ๐Ÿงช if: ${{ inputs.test-devcontainer-file && inputs.acceptance-test-path }} needs: build-push uses: ./.github/workflows/wc-acceptance-test.yml diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 5d8072b2..50a096a5 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -49,7 +49,7 @@ permissions: {} jobs: sanitize-image-name: - name: ๐Ÿงผ Sanitize Image Name + name: ๐Ÿงผ uses: ./.github/workflows/wc-sanitize-image-name.yml with: image-name: ${{ inputs.image-name }} @@ -57,7 +57,7 @@ jobs: runner-labels: ${{ inputs.runner-labels }} build-push: - name: ๐Ÿ› ๏ธ Build โ†’ Push (${ { matrix.runner }}) + name: ๐Ÿ› ๏ธ (${{ matrix.runner }}) strategy: matrix: runner: ${{ (startsWith(inputs.build-test-runner-labels, '[') && endsWith(inputs.build-test-runner-labels, ']')) && fromJson(inputs.build-test-runner-labels) || inputs.build-test-runner-labels }} @@ -136,7 +136,7 @@ jobs: retention-days: 1 merge-image: - name: ๐Ÿ”— Merge Image & Publish Digest + name: ๐Ÿ”— Merge Image # Support either a plain single label (e.g. ubuntu-latest) OR a JSON array of labels. # If the input starts & ends with brackets we attempt JSON parsing; otherwise we pass the raw string. runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} diff --git a/.github/workflows/wc-dependency-review.yml b/.github/workflows/wc-dependency-review.yml index a0a7eb25..593c8a3f 100644 --- a/.github/workflows/wc-dependency-review.yml +++ b/.github/workflows/wc-dependency-review.yml @@ -14,7 +14,7 @@ permissions: {} jobs: dependency-review: - name: ๐Ÿ” Dependency Review + name: ๐Ÿ” runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} permissions: contents: read From 8639486aaebf71d91d56cdf0ae3740eadeb78e43 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 17:49:31 +0000 Subject: [PATCH 42/50] ci: less emoji is sometimes better --- .github/workflows/continuous-integration.yml | 4 ++-- .github/workflows/wc-build-push-test.yml | 6 +++--- .github/workflows/wc-build-push.yml | 2 +- .github/workflows/wc-dependency-review.yml | 2 +- .github/workflows/wc-document-generation.yml | 2 +- .github/workflows/wc-sanitize-image-name.yml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index e3b7d809..99d13f86 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -1,5 +1,5 @@ --- -name: ๐Ÿงช CI +name: CI on: merge_group: @@ -49,7 +49,7 @@ jobs: pull-requests: write publish-test-results: - name: ๐Ÿ“Š Test Results + name: ๐Ÿ“Š Publish Test Results runs-on: ubuntu-latest permissions: checks: write diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 52b6979d..23393c25 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -71,7 +71,7 @@ permissions: {} jobs: build-push: - name: ๐Ÿ› ๏ธ + name: 1. ๐Ÿ› ๏ธ uses: ./.github/workflows/wc-build-push.yml permissions: actions: read @@ -92,7 +92,7 @@ jobs: build-test-runner-labels: ${{ inputs.build-test-runner-labels }} integration-test: - name: ๐Ÿงช + name: 2. ๐Ÿงช if: ${{ inputs.integration-test-file }} strategy: matrix: @@ -109,7 +109,7 @@ jobs: runner-labels: ${{ matrix.runner }} acceptance-test: - name: ๐Ÿงช + name: 3. ๐Ÿงช if: ${{ inputs.test-devcontainer-file && inputs.acceptance-test-path }} needs: build-push uses: ./.github/workflows/wc-acceptance-test.yml diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 50a096a5..a8786e5a 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -57,7 +57,7 @@ jobs: runner-labels: ${{ inputs.runner-labels }} build-push: - name: ๐Ÿ› ๏ธ (${{ matrix.runner }}) + name: ${{ matrix.runner }} strategy: matrix: runner: ${{ (startsWith(inputs.build-test-runner-labels, '[') && endsWith(inputs.build-test-runner-labels, ']')) && fromJson(inputs.build-test-runner-labels) || inputs.build-test-runner-labels }} diff --git a/.github/workflows/wc-dependency-review.yml b/.github/workflows/wc-dependency-review.yml index 593c8a3f..1415f8d9 100644 --- a/.github/workflows/wc-dependency-review.yml +++ b/.github/workflows/wc-dependency-review.yml @@ -14,7 +14,7 @@ permissions: {} jobs: dependency-review: - name: ๐Ÿ” + name: Review runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} permissions: contents: read diff --git a/.github/workflows/wc-document-generation.yml b/.github/workflows/wc-document-generation.yml index 241eb719..290bbce5 100644 --- a/.github/workflows/wc-document-generation.yml +++ b/.github/workflows/wc-document-generation.yml @@ -8,7 +8,7 @@ permissions: {} jobs: generate-documents: - name: ๐Ÿ“„ Generate Documents + name: Generate Documents runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index d5023b59..4c01024c 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -28,7 +28,7 @@ permissions: {} jobs: sanitize: - name: ๐Ÿงผ Sanitize Image Name + name: Sanitize Image Name runs-on: ${{ (startsWith(inputs.runner-labels, '[') && endsWith(inputs.runner-labels, ']')) && fromJson(inputs.runner-labels) || inputs.runner-labels }} outputs: image-basename: ${{ steps.sanitize-image-name.outputs.sanitized-basename }} From b02bb589c0ad833ac7682e694ba44c3bb3b80284 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:03:11 +0000 Subject: [PATCH 43/50] ci: more name juggling --- .github/workflows/wc-acceptance-test.yml | 2 +- .github/workflows/wc-build-push-test.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wc-acceptance-test.yml b/.github/workflows/wc-acceptance-test.yml index 38415da9..ceabf080 100644 --- a/.github/workflows/wc-acceptance-test.yml +++ b/.github/workflows/wc-acceptance-test.yml @@ -32,7 +32,7 @@ permissions: jobs: test: - name: ๐Ÿงช Acceptance Test + name: Acceptance Test runs-on: ubuntu-latest steps: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 diff --git a/.github/workflows/wc-build-push-test.yml b/.github/workflows/wc-build-push-test.yml index 23393c25..170a930c 100644 --- a/.github/workflows/wc-build-push-test.yml +++ b/.github/workflows/wc-build-push-test.yml @@ -71,7 +71,7 @@ permissions: {} jobs: build-push: - name: 1. ๐Ÿ› ๏ธ + name: ๐Ÿ› ๏ธ uses: ./.github/workflows/wc-build-push.yml permissions: actions: read @@ -92,7 +92,7 @@ jobs: build-test-runner-labels: ${{ inputs.build-test-runner-labels }} integration-test: - name: 2. ๐Ÿงช + name: ๐Ÿงช if: ${{ inputs.integration-test-file }} strategy: matrix: @@ -109,7 +109,7 @@ jobs: runner-labels: ${{ matrix.runner }} acceptance-test: - name: 3. ๐Ÿงช + name: ๐Ÿ—๏ธ if: ${{ inputs.test-devcontainer-file && inputs.acceptance-test-path }} needs: build-push uses: ./.github/workflows/wc-acceptance-test.yml From f4c734666db59aa2035b293423a52ce555f433dd Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:13:24 +0000 Subject: [PATCH 44/50] ci: silence step-security warning --- .github/workflows/wc-sanitize-image-name.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index 4c01024c..32e9eace 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -38,7 +38,7 @@ jobs: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: disable-sudo-and-containers: true - egress-policy: block + allowed-endpoints: '' - name: Sanitize image name id: sanitize-image-name env: From 827e8590a385f5db91991a4ddf748b99037d00c5 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:29:28 +0000 Subject: [PATCH 45/50] ci: get rid of defaults --- .github/workflows/image-cleanup.yml | 1 - .github/workflows/pr-conventional-title.yml | 1 - .github/workflows/social-interaction.yml | 1 - .github/workflows/wc-sanitize-image-name.yml | 3 ++- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/image-cleanup.yml b/.github/workflows/image-cleanup.yml index 13f21fde..81ecbe23 100644 --- a/.github/workflows/image-cleanup.yml +++ b/.github/workflows/image-cleanup.yml @@ -20,7 +20,6 @@ jobs: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: disable-sudo: true - egress-policy: block allowed-endpoints: > api.github.com:443 ghcr.io:443 diff --git a/.github/workflows/pr-conventional-title.yml b/.github/workflows/pr-conventional-title.yml index bac8deed..21a24e54 100644 --- a/.github/workflows/pr-conventional-title.yml +++ b/.github/workflows/pr-conventional-title.yml @@ -20,7 +20,6 @@ jobs: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: disable-sudo-and-containers: true - egress-policy: block allowed-endpoints: > api.github.com:443 - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 diff --git a/.github/workflows/social-interaction.yml b/.github/workflows/social-interaction.yml index b4f6dd96..992de656 100644 --- a/.github/workflows/social-interaction.yml +++ b/.github/workflows/social-interaction.yml @@ -21,7 +21,6 @@ jobs: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: disable-sudo-and-containers: true - egress-policy: block allowed-endpoints: > api.github.com:443 - uses: actions/first-interaction@1c4688942c71f71d4f5502a26ea67c331730fa4d # v3.1.0 diff --git a/.github/workflows/wc-sanitize-image-name.yml b/.github/workflows/wc-sanitize-image-name.yml index 32e9eace..5629c4a4 100644 --- a/.github/workflows/wc-sanitize-image-name.yml +++ b/.github/workflows/wc-sanitize-image-name.yml @@ -38,7 +38,8 @@ jobs: - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: disable-sudo-and-containers: true - allowed-endpoints: '' + allowed-endpoints: > + api.github.com:443 - name: Sanitize image name id: sanitize-image-name env: From 27dc9249c0ac4ab7975dcbe4251e15c23a4ccae8 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Thu, 16 Oct 2025 05:13:16 +0000 Subject: [PATCH 46/50] ci: disable errors for lychee --- .mega-linter.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mega-linter.yml b/.mega-linter.yml index b2af1df8..eea658e7 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -14,6 +14,8 @@ DISABLE_LINTERS: - REPOSITORY_SEMGREP - JSON_JSONLINT - SPELL_CSPELL +DISABLE_ERRORS_LINTERS: + - SPELL_LYCHEE SARIF_REPORTER: true PRINT_ALPACA: false SHOW_SKIPPED_LINTERS: false From d79c316f28f65d56d89f9f4bf91459f75f4ed044 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Thu, 16 Oct 2025 05:51:32 +0000 Subject: [PATCH 47/50] ci: remove xwin cache --- .github/workflows/wc-integration-test.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 0b035e77..c33e6151 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -52,19 +52,15 @@ jobs: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: test/cpp/.xwin-cache - key: xwin-cache-${{ runner.arch }} - restore-keys: | - xwin-cache-${{ runner.arch }} + - run: echo "arch=${RUNNER_ARCH@L}" >> "$GITHUB_OUTPUT" + id: runner-arch - run: bats --formatter junit "${TEST_FILE}" | tee "test-report-${IMAGE_BASENAME}-${RUNNER_ARCH}.xml" env: IMAGE_BASENAME: ${{ inputs.image-basename }} TEST_FILE: ${{ inputs.test-file }} - RUNNER_ARCH: ${{ runner.arch }} + RUNNER_ARCH: ${{ steps.runner-arch.outputs.arch }} - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: ${{ !cancelled() }} with: - name: test-results-integration-${{ inputs.image-basename }}-${{ runner.arch }} + name: test-results-integration-${{ inputs.image-basename }}-${{ steps.runner-arch.outputs.arch }} path: test-report-*.xml From 9bd6dafd9f21fd71f57dd20fe3b86ae335c6244e Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Thu, 16 Oct 2025 06:02:13 +0000 Subject: [PATCH 48/50] ci: don't use bash-isms to convert to lowercase --- .github/workflows/wc-build-push.yml | 2 +- .github/workflows/wc-integration-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index a8786e5a..74e32ac5 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -105,7 +105,7 @@ jobs: id: devcontainer-metadata - run: echo "git-commit-epoch=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT" id: devcontainer-epoch - - run: echo "arch=${RUNNER_ARCH@L}" >> "$GITHUB_OUTPUT" + - run: echo "arch=$(tr '[:upper:]' '[:lower:]' <<< '${RUNNER_ARCH}')" >> "$GITHUB_OUTPUT" id: devcontainer-arch - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 id: build-and-push diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index c33e6151..cf5903ae 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -52,7 +52,7 @@ jobs: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - - run: echo "arch=${RUNNER_ARCH@L}" >> "$GITHUB_OUTPUT" + - run: echo "arch=$(tr '[:upper:]' '[:lower:]' <<< '${RUNNER_ARCH}')" >> "$GITHUB_OUTPUT" id: runner-arch - run: bats --formatter junit "${TEST_FILE}" | tee "test-report-${IMAGE_BASENAME}-${RUNNER_ARCH}.xml" env: From 69f65d00636f04cb70ca9e0593897dfd3b123643 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Thu, 16 Oct 2025 06:08:45 +0000 Subject: [PATCH 49/50] ci: make sure we do interpolation --- .github/workflows/wc-build-push.yml | 2 +- .github/workflows/wc-integration-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 74e32ac5..6103eae2 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -105,7 +105,7 @@ jobs: id: devcontainer-metadata - run: echo "git-commit-epoch=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT" id: devcontainer-epoch - - run: echo "arch=$(tr '[:upper:]' '[:lower:]' <<< '${RUNNER_ARCH}')" >> "$GITHUB_OUTPUT" + - run: echo "arch=$(tr '[:upper:]' '[:lower:]' <<< "${RUNNER_ARCH}")" >> "$GITHUB_OUTPUT" id: devcontainer-arch - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 id: build-and-push diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index cf5903ae..3699f497 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -52,7 +52,7 @@ jobs: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - - run: echo "arch=$(tr '[:upper:]' '[:lower:]' <<< '${RUNNER_ARCH}')" >> "$GITHUB_OUTPUT" + - run: echo "arch=$(tr '[:upper:]' '[:lower:]' <<< "${RUNNER_ARCH}")" >> "$GITHUB_OUTPUT" id: runner-arch - run: bats --formatter junit "${TEST_FILE}" | tee "test-report-${IMAGE_BASENAME}-${RUNNER_ARCH}.xml" env: From e192a30688fba75353790429624f048f9683c101 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Thu, 16 Oct 2025 06:11:06 +0000 Subject: [PATCH 50/50] ci: heredoc syntax "<<<" also seems to be a bash-ism --- .github/workflows/wc-build-push.yml | 2 +- .github/workflows/wc-integration-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wc-build-push.yml b/.github/workflows/wc-build-push.yml index 6103eae2..f03e791b 100644 --- a/.github/workflows/wc-build-push.yml +++ b/.github/workflows/wc-build-push.yml @@ -105,7 +105,7 @@ jobs: id: devcontainer-metadata - run: echo "git-commit-epoch=$(git log -1 --pretty=%ct)" >> "$GITHUB_OUTPUT" id: devcontainer-epoch - - run: echo "arch=$(tr '[:upper:]' '[:lower:]' <<< "${RUNNER_ARCH}")" >> "$GITHUB_OUTPUT" + - run: echo "arch=$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" id: devcontainer-arch - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 id: build-and-push diff --git a/.github/workflows/wc-integration-test.yml b/.github/workflows/wc-integration-test.yml index 3699f497..cc7479d2 100644 --- a/.github/workflows/wc-integration-test.yml +++ b/.github/workflows/wc-integration-test.yml @@ -52,7 +52,7 @@ jobs: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - - run: echo "arch=$(tr '[:upper:]' '[:lower:]' <<< "${RUNNER_ARCH}")" >> "$GITHUB_OUTPUT" + - run: echo "arch=$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" id: runner-arch - run: bats --formatter junit "${TEST_FILE}" | tee "test-report-${IMAGE_BASENAME}-${RUNNER_ARCH}.xml" env: