ci: isolate GHCR docker login and delete handoff tags safely
Use per-job DOCKER_CONFIG under RUNNER_TEMP so login-action logout on shared self-hosted runners cannot strip another job's credentials. Drop broken buildx imagetools rm; untag ci-* handoff labels via the registry distribution API after publish. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
parent
f8a5ff8e43
commit
de26578778
27
.github/workflows/publish-server-image.yml
vendored
27
.github/workflows/publish-server-image.yml
vendored
@ -3,6 +3,10 @@ name: publish-server-image
|
|||||||
# Release tags publish the source-addressed image used by in-app updates plus the human-readable
|
# Release tags publish the source-addressed image used by in-app updates plus the human-readable
|
||||||
# semver tags. Main publishes `edge`. Pull requests build with a read-only token and an isolated
|
# semver tags. Main publishes `edge`. Pull requests build with a read-only token and an isolated
|
||||||
# cache, so untrusted code can validate the images without receiving package-publish authority.
|
# cache, so untrusted code can validate the images without receiving package-publish authority.
|
||||||
|
# Self-hosted runners on one host often share ~/.docker; build/publish set DOCKER_CONFIG under
|
||||||
|
# RUNNER_TEMP so docker/login-action post-logout cannot evict a concurrent job's GHCR session.
|
||||||
|
# `ci-<run>-<image>-<arch>` handoff tags are registry labels on the per-arch digest; publish
|
||||||
|
# removes them via the distribution API (tag only, not the digest edge/sha tags reference).
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
@ -182,10 +186,14 @@ jobs:
|
|||||||
context: infra/sandboxes/computer
|
context: infra/sandboxes/computer
|
||||||
- arch: amd64
|
- arch: amd64
|
||||||
runner: [self-hosted, evoo-ci, Linux]
|
runner: [self-hosted, evoo-ci, Linux]
|
||||||
|
env:
|
||||||
|
DOCKER_CONFIG: ${{ runner.temp }}/docker-config
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
|
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
- name: Prepare isolated Docker config directory
|
||||||
|
run: mkdir -p "$DOCKER_CONFIG"
|
||||||
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||||
@ -249,7 +257,11 @@ jobs:
|
|||||||
- name: app
|
- name: app
|
||||||
- name: updater
|
- name: updater
|
||||||
- name: computer
|
- name: computer
|
||||||
|
env:
|
||||||
|
DOCKER_CONFIG: ${{ runner.temp }}/docker-config
|
||||||
steps:
|
steps:
|
||||||
|
- name: Prepare isolated Docker config directory
|
||||||
|
run: mkdir -p "$DOCKER_CONFIG"
|
||||||
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||||
@ -339,13 +351,22 @@ jobs:
|
|||||||
subject-name: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
subject-name: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||||
subject-digest: ${{ steps.manifest.outputs.digest }}
|
subject-digest: ${{ steps.manifest.outputs.digest }}
|
||||||
push-to-registry: true
|
push-to-registry: true
|
||||||
- name: Remove CI handoff tags
|
- name: Delete CI handoff tags
|
||||||
continue-on-error: true
|
|
||||||
env:
|
env:
|
||||||
|
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
IMAGE: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
IMAGE: ghcr.io/${{ steps.ns.outputs.repo }}/${{ matrix.name }}
|
||||||
IMAGE_NAME: ${{ matrix.name }}
|
IMAGE_NAME: ${{ matrix.name }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
repository="${IMAGE#ghcr.io/}"
|
||||||
|
token="$(curl -fsSL -u "${GITHUB_ACTOR}:${GHCR_TOKEN}" \
|
||||||
|
"https://ghcr.io/token?service=ghcr.io&scope=repository:${repository}:pull,push" \
|
||||||
|
| jq -er .token)"
|
||||||
for arch in amd64; do
|
for arch in amd64; do
|
||||||
docker buildx imagetools rm "${IMAGE}:ci-${GITHUB_RUN_ID}-${IMAGE_NAME}-${arch}"
|
tag="ci-${GITHUB_RUN_ID}-${IMAGE_NAME}-${arch}"
|
||||||
|
curl -fsSL -o /dev/null -X DELETE \
|
||||||
|
-H "Authorization: Bearer ${token}" \
|
||||||
|
-H "Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.index.v1+json" \
|
||||||
|
"https://ghcr.io/v2/${repository}/manifests/${tag}"
|
||||||
|
echo "Removed handoff tag ${tag} from ${repository}."
|
||||||
done
|
done
|
||||||
|
|||||||
@ -62,6 +62,14 @@ describe("server image publish workflow", () => {
|
|||||||
expect(workflowText).toContain("ci-${GITHUB_RUN_ID}-");
|
expect(workflowText).toContain("ci-${GITHUB_RUN_ID}-");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("isolates Docker registry auth per job and deletes handoff tags without imagetools rm", () => {
|
||||||
|
expect(workflowText).toContain("DOCKER_CONFIG: ${{ runner.temp }}/docker-config");
|
||||||
|
expect(workflowText).toContain("Prepare isolated Docker config directory");
|
||||||
|
expect(workflowText).not.toContain("imagetools rm");
|
||||||
|
expect(workflowText).toContain("Delete CI handoff tags");
|
||||||
|
expect(workflowText).toContain("/v2/${repository}/manifests/${tag}");
|
||||||
|
});
|
||||||
|
|
||||||
it("names every image under the lowercase repository namespace", () => {
|
it("names every image under the lowercase repository namespace", () => {
|
||||||
expect(workflowText).not.toContain(`ghcr.io/\${{ github.repository }}`);
|
expect(workflowText).not.toContain(`ghcr.io/\${{ github.repository }}`);
|
||||||
const references =
|
const references =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user