-
-
Notifications
You must be signed in to change notification settings - Fork 11.5k
Updated CI to mirror playwright image to GHCR #27318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6316fb1
a5284f3
467e08c
c4b5aa8
3e428a8
77a289f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| name: 'Mirror Container Image' | ||
| description: 'Mirror an upstream container image into GHCR' | ||
| inputs: | ||
| source-image: | ||
| description: 'Fully-qualified source image reference' | ||
| required: true | ||
| target-image: | ||
| description: 'Fully-qualified target image reference' | ||
| required: true | ||
|
|
||
| runs: | ||
| using: 'composite' | ||
| steps: | ||
| - name: Mirror image | ||
| shell: bash | ||
| env: | ||
| SOURCE_IMAGE: ${{ inputs.source-image }} | ||
| TARGET_IMAGE: ${{ inputs.target-image }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| echo "Pulling source image: $SOURCE_IMAGE" | ||
| docker pull "$SOURCE_IMAGE" | ||
|
|
||
| echo "Tagging image as: $TARGET_IMAGE" | ||
| docker tag "$SOURCE_IMAGE" "$TARGET_IMAGE" | ||
|
|
||
| echo "Pushing mirrored image: $TARGET_IMAGE" | ||
| docker push "$TARGET_IMAGE" | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -74,6 +74,9 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Setting BASE_COMMIT to $BASE_COMMIT" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "BASE_COMMIT=$BASE_COMMIT" >> $GITHUB_ENV | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Fetch base commit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: git fetch --no-tags --depth=1 origin "${BASE_COMMIT}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check user org membership | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: check_user_org_membership | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: github.event_name == 'pull_request' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -191,6 +194,25 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Install dependencies | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: bash .github/scripts/install-deps.sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Resolve Playwright image | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: playwright | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PLAYWRIGHT_VERSION="$(node -p "require('./e2e/package.json').devDependencies['@playwright/test']")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "version=$PLAYWRIGHT_VERSION" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Check if the image already exists in GHCR (anonymous OCI registry check) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TOKEN="$(curl -sf "https://ghcr.io/token?service=ghcr.io&scope=repository:tryghost/playwright:pull" | jq -r .token)" || TOKEN="" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| HTTP_CODE="$(curl -so /dev/null -w "%{http_code}" -H "Authorization: Bearer $TOKEN" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://ghcr.io/v2/tryghost/playwright/manifests/v${PLAYWRIGHT_VERSION}-noble")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ "$HTTP_CODE" = "200" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "image-exists=true" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "image-exists=false" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+205
to
+214
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: For
Sources: [1] OCI Distribution Spec (Pulling manifests) (oci-playground.github.io), [2] Docker Registry HTTP API V2 spec (auth, 403/429 errors, 5xx guidance) (matsuand.github.io) Citations:
Distinguish manifest not found (404) from transient errors (401/429/5xx). Lines 205–214 currently collapse every non-200 HTTP status into 🛡️ Suggested fix- if [ "$HTTP_CODE" = "200" ]; then
- echo "image-exists=true" >> "$GITHUB_OUTPUT"
- else
- echo "image-exists=false" >> "$GITHUB_OUTPUT"
- fi
+ case "$HTTP_CODE" in
+ 200)
+ echo "image-exists=true" >> "$GITHUB_OUTPUT"
+ ;;
+ 404)
+ echo "image-exists=false" >> "$GITHUB_OUTPUT"
+ ;;
+ *)
+ echo "::error::Unexpected GHCR manifest response: $HTTP_CODE"
+ exit 1
+ ;;
+ esac📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| changed_admin: ${{ steps.changed.outputs.admin }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -216,6 +238,41 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dependency_cache_key: ${{ env.cachekey }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node_version: ${{ env.NODE_VERSION }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node_test_matrix: ${{ steps.node_matrix.outputs.matrix }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| playwright_version: ${{ steps.playwright.outputs.version }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| playwright_image_exists: ${{ steps.playwright.outputs.image-exists }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| job_mirror_playwright_image: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Mirror Playwright image | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| needs: [job_setup] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: needs.job_setup.outputs.is_tag != 'true' && needs.job_setup.outputs.playwright_image_exists != 'true' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| packages: write | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Checkout | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Login to GHCR | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| registry: ghcr.io | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| username: ${{ github.actor }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| password: ${{ secrets.GITHUB_TOKEN }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Mirror Playwright image | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uses: ./.github/actions/mirror-container-image | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source-image: mcr.microsoft.com/playwright:v${{ needs.job_setup.outputs.playwright_version }}-noble | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| target-image: ghcr.io/tryghost/playwright:v${{ needs.job_setup.outputs.playwright_version }}-noble | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Fail when Playwright image cannot be mirrored | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "::error::Playwright v${{ needs.job_setup.outputs.playwright_version }}-noble is not mirrored in GHCR yet. Publish ghcr.io/tryghost/playwright:v${{ needs.job_setup.outputs.playwright_version }}-noble before running E2E for this branch." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+244
to
+275
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make this an always-running prerequisite for Playwright consumers. Nothing in the workflow currently 🔁 Suggested shape job_mirror_playwright_image:
name: Mirror Playwright image
runs-on: ubuntu-latest
needs: [job_setup]
- if: needs.job_setup.outputs.is_tag != 'true' && needs.job_setup.outputs.playwright_image_exists != 'true'
+ if: needs.job_setup.outputs.is_tag != 'true'
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Login to GHCR
- if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
+ if: needs.job_setup.outputs.playwright_image_exists != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
- name: Mirror Playwright image
- if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
+ if: needs.job_setup.outputs.playwright_image_exists != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
uses: ./.github/actions/mirror-container-imageThen add 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| job_app_version_bump_check: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Check app version bump | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Doesdocker pull IMAGE && docker tag IMAGE TARGET && docker push TARGETpreserve a multi-platform manifest list, or only the pulled platform? What is the recommended way to copy a multi-arch image between registries?💡 Result:
docker pull IMAGE && docker tag IMAGE TARGET && docker push TARGETonly preserves (and pushes) the single platform variant that got pulled, not the original multi-platform manifest list/image index. Ondocker pull, the registry may return a manifest list, but Docker then selects just the matching platform and pulls that image; the engine doesn’t use a locally-stored manifest list for subsequent operations likepush. [1], [2]Recommended ways to copy a multi-arch image between registries (without rebuilding):
Docker-native (recommended): copy the manifest list/index directly
If the source is a manifest list / OCI index,
imagetools createmakes a “carbon copy” to the destination tag. [3]Skopeo (common alternative):
--allensures the manifest list and all referenced arch images are copied. [4]Sources:
[1] Docker Docs – Multi-platform (pull selects correct variant) (docs.docker.com)
[2] Docker Docs –
docker manifest(local manifest lists not used by engine on pull) (docs.docker.com)[3] Docker Docs –
docker buildx imagetools create(“carbon copy” of manifest list/index) (docs.docker.com)[4] Google Cloud Blog – Skopeo
copy --allcopies entire manifest list and associated images (cloud.google.com)Citations:
Preserve the upstream manifest list when mirroring multi-platform images.
Lines 22-29 use
docker pull,docker tag, anddocker push, which only republishes the single platform variant that the runner pulled locally. For multi-arch Playwright tags, this produces a single-platform copy instead of a true multi-platform mirror. The registry returns a manifest list, but Docker selects only the matching platform during pull; subsequenttagandpushoperations don't preserve the original manifest structure.Use a manifest-aware copy tool instead:
docker buildx imagetools create,skopeo copy --all, or equivalent.🔁 Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents