Pin wrangler to the version that actually runs on the pinned node image
All checks were successful
check / check (push) Successful in 6s
Build and Deploy to Cloudflare Pages / build (push) Successful in 7s
probe / s1-build (push) Successful in 13s
Build and Deploy to Cloudflare Pages / deploy (push) Has been skipped
probe / s2-deploy-dryrun (push) Successful in 10s

Round 3 (07af755) cleared the artifact path and left one failure:

    check / check                        success   8s
    Build and Deploy .../ build          success   8s   <- green
    Build and Deploy .../ deploy         skipped        <- if: guard
    probe / r1-wrangler-only             failure   7s
    probe / r2a-upload-proven            success  12s
    probe / r2b-download-proven          success   2s
    probe / r3a-upload-node20            success   8s
    probe / r3b-download-node20          success   2s

r2a/r2b and r3a/r3b upload and download the real site tarball across the two
job containers, so the artifact round trip is sound. r1 does nothing but
install wrangler and invoke it, and it fails.

Reproduced locally in the pinned node image, which is faster than another CI
round:

    $ docker run --rm node@sha256:8f693eaa... sh -c \
        'npm install -g wrangler@4.120.0; wrangler --version'
    install exit=0            (with EBADENGINE warnings)
    Wrangler requires at least Node.js v22.0.0. You are using v20.20.2.
    version exit=1

npm treats engines as a warning on an explicit version, so the install step
would have passed and the deploy step would have failed -- a second break,
independent of the artifact one, in the same job nobody could run.

The instructive part is what the unpinned command it replaced was doing:

    $ docker run --rm node@sha256:8f693eaa... sh -c \
        'npm install -g wrangler; wrangler --version'
    `-- wrangler@4.86.0
    4.86.0

npm resolves a bare name to the newest version whose engines the running node
satisfies, so `npm install -g wrangler` on node 20 has been installing 4.86.0,
not the 4.120.0 that `latest` points at. Pinning 4.120.0 was therefore not
"pin the version we are already getting", it was an unnoticed major-ish bump
onto a node the container does not have.

So this pins wrangler 4.86.0 (engines: node >= 20.3.0, published 2026-04-28),
which is exactly the version that has been deploying this site, verified to
install and run on the pinned node 20 digest. The node image digest is left
alone. Bumping the container to node 22 to keep 4.120.0 is the alternative,
but that changes the deploy runtime for no benefit this issue asks for.

Round 4 replaces the probe jobs with a single end-to-end rehearsal: the build
job as written, then the deploy job as written with `wrangler pages deploy
--help` in place of the publish call.
This commit is contained in:
2026-08-09 02:59:50 +00:00
parent 07af755d1e
commit 73f912c7ed
2 changed files with 49 additions and 66 deletions

View File

@@ -95,9 +95,19 @@ jobs:
- name: Extract site - name: Extract site
run: tar -xzf site.tar.gz run: tar -xzf site.tar.gz
# 4.86.0, not the 4.120.0 that `latest` points at: wrangler
# 4.120.0 requires node >= 22 and refuses to start on this
# container's node 20. Note that the unpinned `npm install -g
# wrangler` this replaces was never installing `latest` either --
# npm picks the newest version whose engines the running node
# satisfies, which on node 20 is exactly 4.86.0. So this pins the
# version that has actually been deploying this site, rather than
# silently changing it. Bumping the container to node 22 is the
# alternative; it is a bigger change and is not what this issue is
# for.
- name: Install Wrangler - name: Install Wrangler
# wrangler 4.120.0, 2026-08-09 # wrangler 4.86.0, 2026-08-09
run: npm install -g wrangler@4.120.0 run: npm install -g wrangler@4.86.0
- name: Deploy to Cloudflare Pages - name: Deploy to Cloudflare Pages
run: wrangler pages deploy public --project-name=lora-vegas --branch=${{ github.ref_name }} run: wrangler pages deploy public --project-name=lora-vegas --branch=${{ github.ref_name }}

View File

@@ -15,7 +15,7 @@
# p6 node:20-bookworm-slim + checkout success 11s # p6 node:20-bookworm-slim + checkout success 11s
# #
# -> the pinned alpine image, the prerequisite step and the site build are all # -> the pinned alpine image, the prerequisite step and the site build are all
# fine; upload-artifact v4 is what broke the deploy. # fine; upload-artifact v4 is what broke the build job on main.
# #
# Round 2 (602fd60): # Round 2 (602fd60):
# #
@@ -25,10 +25,27 @@
# q3 full build + upload v3.2.2-node20 success 11s # q3 full build + upload v3.2.2-node20 success 11s
# q4 download v3.1.0-node20 + wrangler install failure 43s # q4 download v3.1.0-node20 + wrangler install failure 43s
# #
# -> build is green, every v3 upload works, and the remaining failure is # -> build green on every v3 upload; a second, separate failure on the deploy
# somewhere in the deploy-side rehearsal. Round 3 splits q4 into its parts: # side.
# wrangler on its own, the artifact pair that was actually deploying this #
# site before this issue, and the newer node20 artifact pair. # Round 3 (07af755):
#
# build (deploy.yml, upload v3.2.1) success 8s
# r1 wrangler install + invoke, no artifacts failure 7s
# r2a/r2b artifact round trip, v3.2.1/v3.0.2 success 12s / 2s
# r3a/r3b artifact round trip, node20 builds success 8s / 2s
#
# -> the artifact round trip is sound in both pairs; wrangler is the second
# break. Reproduced locally in the pinned node image: `npm install -g
# wrangler@4.120.0` exits 0 with EBADENGINE warnings, then wrangler itself
# exits 1 with "Wrangler requires at least Node.js v22.0.0. You are using
# v20.20.2." Unpinned `npm install -g wrangler` on that same image resolves
# to 4.86.0, because npm picks the newest version the running node
# satisfies -- so 4.86.0 is what has actually been deploying this site, and
# that is what deploy.yml now pins.
#
# Round 4 is the full rehearsal of both jobs end to end with the corrected
# pins, stopping one step short of publishing.
name: probe name: probe
on: on:
@@ -37,24 +54,8 @@ on:
- pin-deploy-refs-observable - pin-deploy-refs-observable
jobs: jobs:
# Isolates the wrangler install and invocation from anything to do with # Producer: identical to the build job in deploy.yml.
# artifacts. wrangler 4.120.0 declares engines.node >= 22 while the deploy s1-build:
# container is node 20, so this needs measuring rather than assuming --
# the pre-issue deploy did run an unpinned wrangler on node:20
# successfully.
r1-wrangler-only:
runs-on: ubuntu-latest
container:
# node 20.20.2-bookworm, 2026-08-09
image: node@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
steps:
# wrangler 4.120.0, 2026-08-09
- run: npm install -g wrangler@4.120.0
- run: wrangler --version
# Producer for the pair that `@v3`/`@v3` resolved to before this issue,
# i.e. the code that was actually deploying the site, now pinned.
r2a-upload-proven:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
# alpine 3.21, 2026-02-28 # alpine 3.21, 2026-02-28
@@ -74,15 +75,17 @@ jobs:
# actions/upload-artifact v3.2.1, 2026-08-09 # actions/upload-artifact v3.2.1, 2026-08-09
- uses: actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 - uses: actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5
with: with:
name: site-proven name: site-dry
path: site.tar.gz path: site.tar.gz
# Consumer half: the deploy job's artifact handling, with no wrangler, so # Consumer: identical to the deploy job in deploy.yml, except that the
# a failure here means the artifact round trip and a pass here means it is # final step prints wrangler's view of the project instead of running
# sound. # `wrangler pages deploy`. Same pinned image, same pinned action, same
r2b-download-proven: # pinned wrangler version, same extracted tree. Needs no token and
# publishes nothing.
s2-deploy-dryrun:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: r2a-upload-proven needs: s1-build
container: container:
# node 20.20.2-bookworm, 2026-08-09 # node 20.20.2-bookworm, 2026-08-09
image: node@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5 image: node@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
@@ -90,40 +93,10 @@ jobs:
# actions/download-artifact v3.0.2, 2026-08-09 # actions/download-artifact v3.0.2, 2026-08-09
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a
with: with:
name: site-proven name: site-dry
- run: tar -xzf site.tar.gz - run: tar -xzf site.tar.gz
- run: test -f public/index.html - run: test -f public/index.html
# wrangler 4.86.0, 2026-08-09
# The newer node20 artifact pair, kept in the round so the choice between - run: npm install -g wrangler@4.86.0
# the two pairs rests on measurement rather than preference. - run: wrangler --version
r3a-upload-node20: - run: wrangler pages deploy --help
runs-on: ubuntu-latest
container:
# alpine 3.21, 2026-02-28
image: alpine@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709
defaults:
run:
shell: sh
steps:
- run: apk add --no-cache nodejs git tar
# actions/checkout v4.2.2, 2026-02-28
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- run: tar -czf alt.tar.gz hugo.toml
# actions/upload-artifact v3.2.2-node20, 2026-08-09
- uses: actions/upload-artifact@c6a3b2bd78b3985e4b2f15397fec357f0fd808de
with:
name: alt-node20
path: alt.tar.gz
r3b-download-node20:
runs-on: ubuntu-latest
needs: r3a-upload-node20
container:
# node 20.20.2-bookworm, 2026-08-09
image: node@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
steps:
# actions/download-artifact v3.1.0-node20, 2026-08-09
- uses: actions/download-artifact@ad191675b41f6a5b46da9a048cb6893812da158b
with:
name: alt-node20
- run: test -f alt.tar.gz