Skip to content

Unblock the web staging deploy - #18

Merged
angusbezzina merged 2 commits into
mainfrom
angus/fix-staging-deploy-npm
Sep 3, 2026
Merged

Unblock the web staging deploy#18
angusbezzina merged 2 commits into
mainfrom
angus/fix-staging-deploy-npm

Conversation

@angusbezzina

Copy link
Copy Markdown
Collaborator

The staging deploy has been failing for reasons unrelated to any code change. Two separate breakages, both in the deploy pipeline itself.

1. npm ci crashes under Node 22's npm

web/package.json depends on eyecons as a git dependency, so npm ci cannot satisfy it from our lockfile — it clones the repo and resolves that project's floating devDependencies against the live registry. A recent publish put eslint 10 in that tree next to plugins whose peer ranges stop at 9, and npm 10's arborist walks the result into a null node:

npm error Cannot read properties of null (reading 'edgesOut')

Nothing in this repo moved; the registry did. Reproduced locally against an unchanged web/package-lock.json — npm 10.9.4 fails, npm 11.14.1 installs cleanly.

CI stayed green through all of this because ci.yml runs Node 20 while the deploy job runs Node 22 for wrangler. Different bundled npm, so the deploy job is the only one that sees it.

Fix: pin npm 11 in the deploy job.

2. The deploy's own CSP check was stale

With installs working, the worker deployed successfully and then failed its own post-deploy verification:

staging propagation verification timed out:
live CSP does not match the pinned staging policy

The worker served img-src 'self' blob: data: https: — the https: added by "Allow approved remote document images" — while the verifier held a hand-copied img-src 'self' blob: data:. The deployment was healthy; only the copy was stale.

This is the second time that duplicated list has rejected a good deploy (the first was the script-src preflight hash, which is why the hash alone was already read from source). Fix: derive the whole expected policy from buildContentSecurityPolicy(), the same function the worker serves it from. That makes the script import TypeScript, so it runs under tsx — already a devDependency, already used by the live-test scripts.

Verification

  • Derived and live policies match on all 14 directives against the deployed worker.
  • Staging is serving the new code: the one document and its asset dependencies manifest rule is present in the live bundle and the old exactly one entry rule is gone.

Follow-up worth filing

The eyecons git dependency makes every install non-hermetic — the lockfile pins its commit, but not the transitive tree npm resolves from its package.json. This will break again. Vendoring the generated icons, or depending on a published version, would make installs reproducible.

Also worth aligning ci.yml to Node 22 so CI actually exercises the version the deploy uses; that gap is what let this reach a dispatch.

🤖 Generated with Claude Code

https://claude.ai/code/session_015TnncCnVNhggoZ9QLQiqeW

angusbezzina and others added 2 commits September 3, 2026 08:09
web/package.json depends on `eyecons` as a git dependency, so `npm ci`
cannot install it from the lockfile alone — it clones the repo and
resolves that project's own floating devDependencies against the live
registry. A recent publish put eslint 10 in that tree alongside plugins
whose peer ranges stop at 9, and npm 10's arborist walks the resulting
graph into a null node:

    npm error Cannot read properties of null (reading 'edgesOut')

Nothing in this repo moved; the registry did. Reproduced locally against
an unchanged web/package-lock.json: npm 10.9.4 fails, npm 11.14.1
installs cleanly.

CI stayed green through this because ci.yml runs Node 20 while the deploy
job runs Node 22 for wrangler — different bundled npm, so the deploy is
the only job that sees the bug.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015TnncCnVNhggoZ9QLQiqeW
The staging deploy succeeded and then failed its own propagation check:

    staging propagation verification timed out:
    live CSP does not match the pinned staging policy

The worker served `img-src 'self' blob: data: https:` — the `https:`
that "Allow approved remote document images" added — while the verifier
still held a hand-copied `img-src 'self' blob: data:`. The deployment was
healthy; only the copy was stale.

This is the second time the duplicate list has rejected a good deploy;
the first was the script-src preflight hash, which is why the hash alone
was already being read from source. Take the whole policy from
buildContentSecurityPolicy() instead, so the check cannot drift from the
worker again. That makes the script import TypeScript, so it runs under
tsx (already a devDependency, already used by the live-test scripts).

Verified against the deployed staging worker: derived and live policies
match on all 14 directives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015TnncCnVNhggoZ9QLQiqeW
@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
attn Ready Ready Preview Sep 3, 2026 1:25pm UTC

Request Review

@angusbezzina
angusbezzina merged commit e84b267 into main Sep 3, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant