Use self-repository syntax for composite actions #138
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| workflow_dispatch: | |
| pull_request: | |
| push: | |
| branches: | |
| - main | |
| permissions: {} | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| # Many color libraries just need this variable to be set to any value. | |
| # Set it to 3 to support 8-bit color graphics (256 colors per channel) | |
| # for libraries that care about the value set. | |
| FORCE_COLOR: 3 | |
| jobs: | |
| lint: | |
| name: Format | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.x" | |
| - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| - uses: j178/prek-action@5337cb91e0fa35a7ff31b9ca345126d8bbbcdf16 # v2.0.6 | |
| - name: Run Pylint | |
| run: uvx nox -s pylint -- --output-format=github | |
| checks: | |
| name: Check Python ${{ matrix.python-version }} on ${{ matrix.runs-on }} | |
| runs-on: ${{ matrix.runs-on }} | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.12", "3.14"] | |
| runs-on: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel] | |
| include: | |
| - python-version: "pypy-3.11" | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| allow-prereleases: true | |
| - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| - name: Install package | |
| run: uv sync | |
| - name: Test package | |
| run: >- | |
| uv run pytest -ra --cov --cov-report=xml --cov-report=term | |
| --junitxml=junit.xml -o junit_family=legacy --durations=20 | |
| - name: Upload coverage report | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| - name: Upload test results to Codecov | |
| if: ${{ !cancelled() }} | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| report_type: test_results | |
| # The e2e-* jobs exercise the reusable notify and bump-images workflows | |
| # end-to-end on every PR: notify runs in --dry-run mode against fixture | |
| # deploy targets with dummy credentials, and bump-images consumes freshly | |
| # generated client payloads against fixture manifests in tests/e2e/. | |
| e2e-payload: | |
| name: E2E build client payloads | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| commit_payload: ${{ steps.payloads.outputs.commit_payload }} | |
| pr_payload: ${{ steps.payloads.outputs.pr_payload }} | |
| tag: ${{ steps.payloads.outputs.tag }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| - name: Install odp-releaser from this checkout | |
| run: uv tool install . | |
| - name: Build client payloads | |
| id: payloads | |
| env: | |
| DIGEST: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | |
| run: | | |
| TAG="e2e-${GITHUB_SHA:0:7}" | |
| COMMIT_PAYLOAD=$(odp-releaser test make-payload \ | |
| ghcr.io/gulfofmaine/odp-releaser-e2e-commit "$TAG" "$DIGEST" \ | |
| --github-event-name workflow_dispatch) | |
| PR_PAYLOAD=$(odp-releaser test make-payload \ | |
| ghcr.io/gulfofmaine/odp-releaser-e2e-pr "$TAG" "$DIGEST" \ | |
| --github-event-name workflow_dispatch) | |
| { | |
| printf 'commit_payload=%s\n' "$COMMIT_PAYLOAD" | |
| printf 'pr_payload=%s\n' "$PR_PAYLOAD" | |
| printf 'tag=%s\n' "$TAG" | |
| } >> "$GITHUB_OUTPUT" | |
| e2e-notify: | |
| name: E2E notify (dry run) | |
| permissions: | |
| contents: read # notify.yml checks out this repo to read the deploy targets | |
| pull-requests: read # match notify.yml's declared job permissions | |
| uses: ./.github/workflows/notify.yml | |
| with: | |
| image_name: ghcr.io/gulfofmaine/odp-releaser-e2e-commit | |
| tag: e2e | |
| digest: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | |
| deploy_targets_path: tests/e2e/deploy_targets.yaml | |
| event_name: workflow_dispatch | |
| dry_run: true | |
| verbosity: 2 | |
| secrets: | |
| # Dummy values: --dry-run only checks credential *presence* per target | |
| # owner (resolve_app_credentials); nothing is minted or sent. | |
| dispatch_app_id: "000000" | |
| dispatch_app_private_key: e2e-dummy-not-a-real-key | |
| dispatch_apps: | |
| '{"odp-e2e-org": {"app_id": "111111", "private_key": | |
| "e2e-dummy-not-a-real-key"}}' | |
| e2e-bump-commit: | |
| name: E2E bump-images (commit mode) | |
| needs: [e2e-payload] | |
| permissions: | |
| contents: write # match bump-images.yml's declared job permissions; dry_run never pushes | |
| pull-requests: write # match bump-images.yml's declared job permissions; dry_run opens no PR | |
| id-token: write # match bump-images.yml's declared job permissions; no sync is configured here | |
| uses: ./.github/workflows/bump-images.yml | |
| with: | |
| config_path: tests/e2e/image_manifest.yaml | |
| client_payload: ${{ needs.e2e-payload.outputs.commit_payload }} | |
| dry_run: true | |
| verbosity: 2 | |
| e2e-bump-pr: | |
| name: E2E bump-images (pull_request mode) | |
| needs: [e2e-payload] | |
| permissions: | |
| contents: write # match bump-images.yml's declared job permissions; dry_run never pushes | |
| pull-requests: write # match bump-images.yml's declared job permissions; dry_run opens no PR | |
| id-token: write # match bump-images.yml's declared job permissions; no sync is configured here | |
| uses: ./.github/workflows/bump-images.yml | |
| with: | |
| config_path: tests/e2e/image_manifest.yaml | |
| client_payload: ${{ needs.e2e-payload.outputs.pr_payload }} | |
| dry_run: true | |
| verbosity: 2 | |
| e2e-action-self-install: | |
| name: E2E composite action installs the CLI itself | |
| # The reusable workflows install the CLI before calling the composite | |
| # actions, so they only ever exercise the no-op branch of `install`. This | |
| # job is the other half of the contract a caller gets: `bump_images` alone, | |
| # with no install step and no uv on the PATH, has to resolve | |
| # `$/.github/actions/install` and install the CLI for itself. | |
| needs: [e2e-payload] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read # dry_run writes nothing and pushes nothing | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Bump images | |
| id: bump | |
| uses: ./.github/actions/bump_images | |
| with: | |
| config_path: tests/e2e/image_manifest.yaml | |
| client_payload: ${{ needs.e2e-payload.outputs.commit_payload }} | |
| dry_run: "true" | |
| verbosity: "2" | |
| - name: Assert the bump ran | |
| env: | |
| CHANGED: ${{ steps.bump.outputs.changed }} | |
| IMAGE_NAME: ${{ steps.bump.outputs.image_name }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$CHANGED" != "true" ]; then | |
| echo "::error::expected changed=true, got '$CHANGED'" | |
| exit 1 | |
| fi | |
| if [ "$IMAGE_NAME" != "ghcr.io/gulfofmaine/odp-releaser-e2e-commit" ]; then | |
| echo "::error::unexpected image_name '$IMAGE_NAME'" | |
| exit 1 | |
| fi | |
| command -v odp-releaser | |
| e2e-assert: | |
| name: E2E assert notify and bump outputs | |
| needs: [e2e-payload, e2e-notify, e2e-bump-commit, e2e-bump-pr] | |
| runs-on: ubuntu-latest | |
| permissions: {} | |
| env: | |
| TAG: ${{ needs.e2e-payload.outputs.tag }} | |
| NOTIFY_RESULTS: ${{ needs.e2e-notify.outputs.results }} | |
| NOTIFY_TARGET_COUNT: ${{ needs.e2e-notify.outputs.target_count }} | |
| COMMIT_CHANGED: ${{ needs.e2e-bump-commit.outputs.changed }} | |
| COMMIT_MODE: ${{ needs.e2e-bump-commit.outputs.update_mode }} | |
| COMMIT_MESSAGE: ${{ needs.e2e-bump-commit.outputs.commit_message }} | |
| COMMIT_IMAGE_NAME: ${{ needs.e2e-bump-commit.outputs.image_name }} | |
| COMMIT_DIGEST: ${{ needs.e2e-bump-commit.outputs.digest }} | |
| PR_CHANGED: ${{ needs.e2e-bump-pr.outputs.changed }} | |
| PR_MODE: ${{ needs.e2e-bump-pr.outputs.update_mode }} | |
| PR_TITLE: ${{ needs.e2e-bump-pr.outputs.pr_title }} | |
| PR_BRANCH: ${{ needs.e2e-bump-pr.outputs.branch_name }} | |
| PR_IMAGE_NAME: ${{ needs.e2e-bump-pr.outputs.image_name }} | |
| PR_DIGEST: ${{ needs.e2e-bump-pr.outputs.digest }} | |
| PR_REVIEWERS: ${{ needs.e2e-bump-pr.outputs.reviewers }} | |
| PR_TEAM_REVIEWERS: ${{ needs.e2e-bump-pr.outputs.team_reviewers }} | |
| COMMIT_REVIEWERS: ${{ needs.e2e-bump-commit.outputs.reviewers }} | |
| PR_COMMENT_ENABLED: ${{ needs.e2e-bump-pr.outputs.comment_enabled }} | |
| PR_COMMENT_PR_NUMBER: ${{ needs.e2e-bump-pr.outputs.comment_pr_number }} | |
| PR_COMMENT_STAGED: | |
| ${{ needs.e2e-bump-pr.outputs.comment_staged_template }} | |
| PR_COMMENT_DEPLOYED: | |
| ${{ needs.e2e-bump-pr.outputs.comment_deployed_template }} | |
| E2E_DIGEST: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | |
| steps: | |
| - name: Assert notify and bump-images outputs | |
| run: | | |
| fail=0 | |
| expect() { # expect <label> <actual> <expected> | |
| if [ "$2" != "$3" ]; then | |
| echo "::error::$1: expected '$3', got '$2'" | |
| fail=1 | |
| fi | |
| } | |
| expect_contains() { # expect_contains <label> <haystack> <needle> | |
| case "$2" in | |
| *"$3"*) ;; | |
| *) echo "::error::$1 does not contain '$3'"; fail=1 ;; | |
| esac | |
| } | |
| expect "notify target_count" "$NOTIFY_TARGET_COUNT" "2" | |
| expect "notify results length" \ | |
| "$(jq 'length' <<<"$NOTIFY_RESULTS")" "2" | |
| expect "notify results all ok" \ | |
| "$(jq 'all(.ok)' <<<"$NOTIFY_RESULTS")" "true" | |
| expect "notify results targets" \ | |
| "$(jq -r '[.[] | "\(.owner)/\(.repo):\(.event_type)"] | join(",")' \ | |
| <<<"$NOTIFY_RESULTS")" \ | |
| "gulfofmaine/odp-releaser-e2e-target:image-published,odp-e2e-org/odp-releaser-e2e-target:image-published-e2e" | |
| expect "notify results all dry run" \ | |
| "$(jq 'all(.detail == "dry run (not sent)")' <<<"$NOTIFY_RESULTS")" \ | |
| "true" | |
| expect "commit-mode changed" "$COMMIT_CHANGED" "true" | |
| expect "commit-mode update_mode" "$COMMIT_MODE" "commit" | |
| expect "commit-mode image_name" "$COMMIT_IMAGE_NAME" \ | |
| "ghcr.io/gulfofmaine/odp-releaser-e2e-commit" | |
| expect "commit-mode digest" "$COMMIT_DIGEST" "$E2E_DIGEST" | |
| expect_contains "commit-mode commit_message" "$COMMIT_MESSAGE" \ | |
| "Update image ghcr.io/gulfofmaine/odp-releaser-e2e-commit to $TAG" | |
| expect_contains "commit-mode commit_message" "$COMMIT_MESSAGE" \ | |
| "commit.kustomization.yaml" | |
| expect "pr-mode changed" "$PR_CHANGED" "true" | |
| expect "pr-mode update_mode" "$PR_MODE" "pull_request" | |
| expect "pr-mode image_name" "$PR_IMAGE_NAME" \ | |
| "ghcr.io/gulfofmaine/odp-releaser-e2e-pr" | |
| expect "pr-mode digest" "$PR_DIGEST" "$E2E_DIGEST" | |
| expect "pr-mode pr_title" "$PR_TITLE" \ | |
| "Update image ghcr.io/gulfofmaine/odp-releaser-e2e-pr to $TAG" | |
| expect "pr-mode branch_name" "$PR_BRANCH" \ | |
| "odp-releaser/bump-ghcr.io-gulfofmaine-odp-releaser-e2e-pr" | |
| expect "pr-mode reviewers" "$PR_REVIEWERS" "odp-releaser-e2e-reviewer" | |
| expect "pr-mode team_reviewers" "$PR_TEAM_REVIEWERS" "" | |
| expect "commit-mode reviewers" "$COMMIT_REVIEWERS" "" | |
| # Commenting is on by default and resolves to the built-in templates, | |
| # but a workflow_dispatch payload carries no source pull request, so | |
| # the empty number is what makes bump-images.yml skip the step. | |
| expect "pr-mode comment_enabled" "$PR_COMMENT_ENABLED" "true" | |
| expect "pr-mode comment_pr_number" "$PR_COMMENT_PR_NUMBER" "" | |
| expect_contains "pr-mode comment_staged_template" \ | |
| "$PR_COMMENT_STAGED" "staged for" | |
| expect_contains "pr-mode comment_deployed_template" \ | |
| "$PR_COMMENT_DEPLOYED" "deployed to" | |
| exit "$fail" | |
| # The only job anywhere in this repo that touches a real registry. The | |
| # e2e-bump-* jobs above run --dry-run, which skips the sync step outright, so | |
| # without this the sync shell -- the destination loop, the skip-if-identical | |
| # check and the digest verification -- would ship untested. | |
| # | |
| # The script under test is *extracted* from bump_images/action.yml rather | |
| # than restated here, so this cannot quietly drift from what actually runs. | |
| # registry:2 speaks plain HTTP, so skopeo is told to treat it as insecure via | |
| # registries.conf -- confined to this job, rather than adding a | |
| # --tls-verify escape hatch to the action's own inputs for the sake of a test. | |
| e2e-sync: | |
| name: E2E image sync (local registry) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| services: | |
| registry: | |
| # registry:2, pinned to its multi-arch index digest | |
| image: registry@sha256:a3d8aaa63ed8681a604f1dea0aa03f100d5895b6a58ace528858a7b332415373 | |
| ports: | |
| - 5000:5000 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| - name: Trust the plain-HTTP test registry | |
| run: | | |
| # Replaces the file rather than appending to it. The ubuntu runner | |
| # ships a v1-format registries.conf (a `[registries.search]` table), | |
| # and containers/image refuses outright to load a file that mixes | |
| # the v1 and v2 formats -- appending a `[[registry]]` block gives | |
| # "mixing sysregistry v1/v2 is not supported" and every skopeo call | |
| # fails. Nothing here needs the v1 search list: every reference in | |
| # this job is fully qualified, and search registries only ever apply | |
| # to unqualified short names. | |
| sudo tee /etc/containers/registries.conf >/dev/null <<'CONF' | |
| [[registry]] | |
| location = "127.0.0.1:5000" | |
| insecure = true | |
| CONF | |
| - name: Extract the sync script from the composite action | |
| run: | | |
| uv run --no-project --with pyyaml python - <<'PY' | |
| import pathlib | |
| import yaml | |
| action = yaml.safe_load( | |
| pathlib.Path(".github/actions/bump_images/action.yml").read_text() | |
| ) | |
| step = next(s for s in action["runs"]["steps"] if s.get("id") == "sync") | |
| pathlib.Path("sync.sh").write_text(step["run"]) | |
| PY | |
| test -s sync.sh | |
| - name: Seed a multi-arch source image into the test registry | |
| run: | | |
| # mirror.gcr.io rather than Docker Hub directly: no auth and no anonymous | |
| # pull limits, while still serving a real multi-arch manifest list. | |
| skopeo copy --all --preserve-digests \ | |
| docker://mirror.gcr.io/library/alpine:3.20 \ | |
| docker://127.0.0.1:5000/odp-releaser-e2e-sync-src:seed | |
| - name: Sync to the test registry and assert | |
| run: | | |
| set -euo pipefail | |
| SRC_REPO=127.0.0.1:5000/odp-releaser-e2e-sync-src | |
| DST_A=127.0.0.1:5000/odp-releaser-e2e-sync-dst-a:v1.2.3 | |
| DST_B=127.0.0.1:5000/odp-releaser-e2e-sync-dst-b:v1.2.3 | |
| digest_of() { | |
| skopeo inspect --raw "docker://$1" | sha256sum | cut -d' ' -f1 | |
| } | |
| platforms_of() { | |
| skopeo inspect --raw "docker://$1" | jq '.manifests | length' | |
| } | |
| SRC_DIGEST="sha256:$(digest_of "$SRC_REPO:seed")" | |
| SRC_PLATFORMS="$(platforms_of "$SRC_REPO:seed")" | |
| echo "source $SRC_DIGEST with $SRC_PLATFORMS manifests" | |
| export SOURCE_REF="$SRC_REPO@$SRC_DIGEST" | |
| # Two destinations, so the loop's fan-out path is exercised and not | |
| # just its single-item case. Built with printf rather than a literal | |
| # two-line string: the newline has to survive YAML block-scalar | |
| # dedenting, and a stray leading space would make the second | |
| # destination an invalid reference. | |
| export DESTINATIONS="$(printf '%s\n%s' "$DST_A" "$DST_B")" | |
| export EXPECTED_DIGEST="$SRC_DIGEST" | |
| export GITHUB_OUTPUT="$RUNNER_TEMP/sync_output" | |
| # GITHUB_OUTPUT is append-only, and a real step gets a fresh one per | |
| # invocation. This job runs the script several times in a single | |
| # step, so truncate between runs or the outputs accumulate and no | |
| # longer look like what a real run produces. | |
| run_sync() { | |
| : > "$GITHUB_OUTPUT" | |
| bash sync.sh 2>&1 | tee "$1" | |
| } | |
| echo "::group::first sync" | |
| run_sync first.log | |
| echo "::endgroup::" | |
| FIRST_OUTPUT="$(cat "$GITHUB_OUTPUT")" | |
| echo "::group::second sync (must skip, not re-push)" | |
| run_sync second.log | |
| echo "::endgroup::" | |
| SECOND_OUTPUT="$(cat "$GITHUB_OUTPUT")" | |
| fail=0 | |
| expect() { | |
| if [ "$2" = "$3" ]; then | |
| echo "ok: $1" | |
| else | |
| echo "::error::$1: expected '$3', got '$2'" | |
| fail=1 | |
| fi | |
| } | |
| # --preserve-digests held and the index was not rewritten in transit. | |
| expect "destination A digest" "sha256:$(digest_of "$DST_A")" \ | |
| "$SRC_DIGEST" | |
| expect "destination B digest" "sha256:$(digest_of "$DST_B")" \ | |
| "$SRC_DIGEST" | |
| DST="$DST_A" | |
| # The --all regression guard. Without --all skopeo copies only the | |
| # runner's own architecture and --preserve-digests does not object, it | |
| # just preserves that single image's digest instead of the index's -- | |
| # so this is the assertion that would catch losing the flag. | |
| expect "destination manifest count" "$(platforms_of "$DST")" \ | |
| "$SRC_PLATFORMS" | |
| if [ "$SRC_PLATFORMS" -lt 2 ]; then | |
| echo "::error::source is not multi-arch, so the --all guard above is vacuous" | |
| fail=1 | |
| fi | |
| # Both runs report synced=true: one that copied, and one where every | |
| # destination was already up to date. | |
| expect "synced output after copying" "$FIRST_OUTPUT" "synced=true" | |
| expect "synced output after skipping" "$SECOND_OUTPUT" "synced=true" | |
| # Both destinations must be reported, not just the first: this is | |
| # what catches the loop silently processing one item. | |
| for dst in "$DST_A" "$DST_B"; do | |
| if ! grep -q "$dst" first.log; then | |
| echo "::error::first run never mentioned $dst" | |
| fail=1 | |
| fi | |
| done | |
| # The second run must take the skip path rather than re-pushing: a | |
| # registry with tag immutability would reject the re-push outright. | |
| if ! grep -q "is already $SRC_DIGEST; skipping" second.log; then | |
| echo "::error::second run did not report skipping an identical digest" | |
| fail=1 | |
| fi | |
| if grep -q "^Copying " second.log; then | |
| echo "::error::second run copied again instead of skipping" | |
| fail=1 | |
| fi | |
| # A destination holding a DIFFERENT digest must be re-copied, not | |
| # skipped. Only the digests-match case is a no-op; this is the path | |
| # that moves a mirror tag onto a new release. | |
| echo "::group::occupy destination B with a different image" | |
| skopeo copy --all --preserve-digests \ | |
| docker://mirror.gcr.io/library/busybox:stable \ | |
| "docker://$DST_B" | |
| echo "::endgroup::" | |
| OTHER_DIGEST="sha256:$(digest_of "$DST_B")" | |
| if [ "$OTHER_DIGEST" = "$SRC_DIGEST" ]; then | |
| echo "::error::the two test images share a digest, so the recopy check is vacuous" | |
| fail=1 | |
| fi | |
| echo "::group::third sync (must re-copy B, still skip A)" | |
| run_sync third.log | |
| echo "::endgroup::" | |
| expect "destination B re-copied" "sha256:$(digest_of "$DST_B")" \ | |
| "$SRC_DIGEST" | |
| if ! grep -q "^Copying .* to $DST_B$" third.log; then | |
| echo "::error::third run did not re-copy the mismatched destination" | |
| fail=1 | |
| fi | |
| if ! grep -q "$DST_A is already $SRC_DIGEST; skipping" third.log; then | |
| echo "::error::third run should still have skipped the matching destination" | |
| fail=1 | |
| fi | |
| # The post-copy verification must fail the step when the destination | |
| # does not end up at the expected digest. Exercised by lying about | |
| # the expected digest, which is the only way to reach it without a | |
| # registry that rewrites manifests. | |
| echo "::group::mismatch must fail the step" | |
| if EXPECTED_DIGEST="sha256:$(printf '0%.0s' $(seq 64))" \ | |
| DESTINATIONS=127.0.0.1:5000/odp-releaser-e2e-sync-dst-c:v1.2.3 \ | |
| bash sync.sh >mismatch.log 2>&1; then | |
| echo "::error::a destination digest mismatch did not fail the step" | |
| fail=1 | |
| else | |
| echo "ok: digest mismatch failed the step" | |
| fi | |
| echo "::endgroup::" | |
| if ! grep -q "but the manifests were bumped to" mismatch.log; then | |
| echo "::error::mismatch failure did not explain itself" | |
| fail=1 | |
| fi | |
| exit "$fail" | |
| pass: | |
| name: All checks passed | |
| if: always() | |
| needs: | |
| [ | |
| lint, | |
| checks, | |
| e2e-payload, | |
| e2e-notify, | |
| e2e-bump-commit, | |
| e2e-bump-pr, | |
| e2e-action-self-install, | |
| e2e-assert, | |
| e2e-sync, | |
| ] | |
| runs-on: ubuntu-slim | |
| timeout-minutes: 2 | |
| steps: | |
| - name: Decide whether the needed jobs succeeded or failed | |
| uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 | |
| with: | |
| jobs: ${{ toJSON(needs) }} |