diff --git a/.github/workflows/runner-full-stack-e2e.yml b/.github/workflows/runner-full-stack-e2e.yml index c0eb1dfb44..a4125829d0 100644 --- a/.github/workflows/runner-full-stack-e2e.yml +++ b/.github/workflows/runner-full-stack-e2e.yml @@ -418,31 +418,25 @@ jobs: --certificate-oidc-issuer https://token.actions.githubusercontent.com \ "$IMAGE_TAG@$digest" >/dev/null immutable="${IMAGE_TAG%:*}@$digest" - # The Daytona base image is large. The build cache plus a second full - # anonymous pull can exhaust a standard GitHub-hosted runner before - # Docker creates the tiny metadata-probe container. The pushed digest - # is already immutable, so release the local builder/cache first. - docker buildx prune --all --force >/dev/null - docker system prune --all --force >/dev/null - anonymous_config="$(mktemp -d)" - docker --config "$anonymous_config" pull "$immutable" # The Dockerfile's final two RUN steps execute the runner metadata, # transport-mode, provider-pack JSON, and pinned ACP binary checks as - # root and as the unprivileged Daytona user. Starting another - # container after this full pull can exhaust the hosted runner's thin - # writable layer even after pruning, so assert the published image - # configuration here without creating a redundant container. - image_config="$(docker image inspect "$immutable" \ - --format '{{json .}}')" - published_content_id="$(jq -r '.Config.Labels["io.paperclip.runner.content-id"] // empty' <<< "$image_config")" - source_revision="$(jq -r '.Config.Labels["org.opencontainers.image.revision"] // empty' <<< "$image_config")" + # root and as the unprivileged Daytona user. Buildx reads the signed + # digest's OCI config directly from GHCR, so verification does not + # download the image's large filesystem layers. Logging out first + # preserves the proof that Daytona can retrieve this public image + # without the workflow's package credentials. + docker logout ghcr.io >/dev/null + image_config="$(docker buildx imagetools inspect "$immutable" \ + --format '{{json .Image}}')" + published_content_id="$(jq -r '.config.Labels["io.paperclip.runner.content-id"] // empty' <<< "$image_config")" + source_revision="$(jq -r '.config.Labels["org.opencontainers.image.revision"] // empty' <<< "$image_config")" test "$published_content_id" = "$IMAGE_CONTENT_ID" [[ "$source_revision" =~ ^[0-9a-f]{40}$ ]] jq -e \ - '.Architecture == "amd64" and - .Os == "linux" and - .Config.User == "daytona" and - (.Config.Env | any(startswith("PAPERCLIP_RUNNER_PROVIDER_PACK_ROOT=")))' \ + '.architecture == "amd64" and + .os == "linux" and + .config.User == "daytona" and + (.config.Env | any(startswith("PAPERCLIP_RUNNER_PROVIDER_PACK_ROOT=")))' \ <<< "$image_config" >/dev/null echo "image=$immutable" >> "$GITHUB_OUTPUT" echo "source_revision=$source_revision" >> "$GITHUB_OUTPUT" diff --git a/tests/runner-e2e/daytona-image.test.ts b/tests/runner-e2e/daytona-image.test.ts index 19211d331d..ec56047665 100644 --- a/tests/runner-e2e/daytona-image.test.ts +++ b/tests/runner-e2e/daytona-image.test.ts @@ -71,8 +71,16 @@ describe("runner E2E Daytona image contract", () => { ); expect(workflow).not.toContain("e2e-git-${{ github.sha }}"); expect(workflow).toContain("cosign sign --yes"); - expect(workflow).toContain("docker image inspect"); - expect(workflow).toContain('.Config.User == "daytona"'); + expect(workflow).toContain("docker logout ghcr.io"); + expect(workflow).toContain(`docker buildx imagetools inspect "$immutable"`); + expect(workflow).toContain(`--format '{{json .Image}}'`); + expect(workflow).not.toContain(`docker --config "$anonymous_config" pull`); + expect(workflow).not.toContain("docker image inspect"); + expect(workflow).not.toContain("docker buildx prune --all --force"); + expect(workflow).not.toContain("docker system prune --all --force"); + expect(workflow).toContain('.architecture == "amd64"'); + expect(workflow).toContain('.os == "linux"'); + expect(workflow).toContain('.config.User == "daytona"'); expect(workflow).toContain("PAPERCLIP_RUNNER_PROVIDER_PACK_ROOT="); expect(workflow).toContain( "node packages/paperclip-runner/scripts/build-provider-pack.mjs packages/paperclip-runner/provider-pack", @@ -83,7 +91,12 @@ describe("runner E2E Daytona image contract", () => { expect(workflow).toContain( "PAPERCLIP_RUNNER_SOURCE_REVISION: ${{ needs.daytona_image.outputs.source_revision }}", ); - expect(workflow).toContain("anonymous_config"); + expect(workflow.indexOf("cosign verify")).toBeLessThan( + workflow.indexOf("docker logout ghcr.io"), + ); + expect(workflow.indexOf("docker logout ghcr.io")).toBeLessThan( + workflow.indexOf(`--format '{{json .Image}}'`), + ); }); it("hashes the audited image dependency closure rather than the repository revision", async () => {