fix(deploy): migrate off deprecated Cloudflare service environments#269
fix(deploy): migrate off deprecated Cloudflare service environments#269chitcommit wants to merge 2 commits into
Conversation
wrangler.jsonc set "legacy_env": false, putting the worker in the
service-environments model that Cloudflare has deprecated ("DO NOT USE
IN PRODUCTION" on every build). Production deploys only worked because
the `production` service environment was already grandfathered in;
staging deploys failed outright with API error 10223 ("This account
cannot create new non-production service environments").
Changes:
- remove top-level "legacy_env": false
- pin env.production.name = "chittyconnect" (EXISTING script)
- pin env.staging.name = "chittyconnect-staging" (new top-level worker)
The production name pin is load-bearing. Without service environments
wrangler name-suffixes by default, and env.production had no "name" key
of its own, so `--env production` would have targeted a brand-new
`chittyconnect-production` worker and stranded the live script along
with its connect.chitty.cc/* route. Verified the trap is real: env.dev,
which still has no "name" key, now resolves to "chittyconnect-dev".
Also pins --config in safe-deploy.sh. Wrangler's config discovery prefers
`wrangler.json` over `wrangler.jsonc` and walks up parent directories, so
an untracked (gitignored, therefore CI-invisible) `wrangler.json` at the
repo root was silently shadowing the tracked config — the script audited
wrangler.jsonc while deploying whatever wrangler happened to find. That
stray file has been removed and --config now makes the reviewed file
authoritative on every deploy path.
Validation (no credentials required, no deploy performed):
- wrangler's own resolver (unstable_readConfig) confirms:
--env production -> "chittyconnect", route connect.chitty.cc/*
--env staging -> "chittyconnect-staging", no routes
- `wrangler deploy --env production --dry-run` resolves 73 bindings,
byte-identical to the pre-migration baseline (diff empty)
- `--env staging` likewise resolves 73, identical to baseline
- scripts/lib/extract-declared-bindings.mjs production independently
agrees at 73
- deprecation warning is gone
- 496 tests pass, 1 pre-existing skip
Refs #219, #207, #218, #223
Co-Authored-By: Claude <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
chittyconnect | aa030c4 | Jul 25 2026, 02:23 AM |
📝 WalkthroughWalkthroughThe deployment configuration now pins staging and production worker names. ChangesDeployment safety
Estimated code review effort: 3 (Moderate) | ~20 minutes ### Sequence Diagram(s) sequenceDiagram
participant SafeDeploy
participant Wrangler
participant CloudflareAPI
SafeDeploy->>Wrangler: deploy with explicit environment and config
Wrangler-->>SafeDeploy: deployment status and uploaded worker name
SafeDeploy->>CloudflareAPI: fetch deployed script settings
CloudflareAPI-->>SafeDeploy: live binding names
SafeDeploy->>SafeDeploy: validate target and binding drift
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Migrate chittyconnect off Cloudflare’s deprecated service environments by removing the legacy_env: false opt-in and explicitly pinning worker names for production and staging, plus hardening the deploy wrapper to always deploy using the reviewed Wrangler config.
Changes:
- Remove
legacy_env: falseand explicitly setenv.production.name = "chittyconnect"to avoid accidental-productionsuffix deployments. - Set
env.staging.name = "chittyconnect-staging"so staging deploys become a separate top-level worker (avoids API 10223). - Update
scripts/safe-deploy.shto pass--config "$WRANGLER_CFG"to prevent config shadowing by an untrackedwrangler.json.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
wrangler.jsonc |
Removes deprecated service-environments opt-in and pins env.production/env.staging worker names to prevent route/binding mishaps. |
scripts/safe-deploy.sh |
Pins Wrangler to the reviewed config during deploys to avoid accidental shadow-config behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # ── 2. Audit declared vs attached bindings ─────────────────────────────────── | ||
| echo "[safe-deploy] auditing bindings on live worker $DEPLOYED_NAME ..." |
⛔ DO NOT MERGE — this branch clobbers production config on pushConverting to draft. Pushing this branch overwrote production configuration, and the cause is infrastructure coupling that must be fixed before this change can land. What happenedPushing commit Both triggers are bound to the same Why this change surfaced itThe pre-existing It never reached the upload step. Removing Current production state
Code is unaffected — bundle etag identical ( Config is wrong:
Blockers before this can merge
Not currently at riskOther open branches still carry The config change itself validated cleanly (73 bindings byte-identical, correct name resolution, same-etag proof of no route handoff). The defect is not in the diff — it's that the diff removes a guard the CI topology was silently depending on. |
… a service env
Two defects that let a branch push write staging config onto production
on 2026-07-25 (build fe9061f3) without this script noticing.
1. Cloudflare Workers Builds ignores the name in wrangler.jsonc and forces
the name of the script its trigger is bound to, printing a warning and
deploying anyway:
▲ [WARNING] Failed to match Worker name. Your config file is using the
Worker name "chittyconnect-staging", but the CI system expected
"chittyconnect". Overriding using the CI provided Worker name.
Both build triggers were bound to the same script id, so `deploy:staging`
landed on the production script. This script could not see it: the audit
checked the name it COMPUTED, never the name wrangler actually shipped to.
Now the deploy output is captured with tee and checked two ways — grep for
the override warning, and compare wrangler's "Uploaded <name>" line against
the intended target. Either mismatch exits 72. Verified both trip when
replayed against the real fe9061f3 log.
2. The binding audit queried
/workers/services/$WORKER_NAME/environments/$ENV/bindings
a service-environments path that only resolves for a grandfathered
environment. It 404s for any real top-level worker — that 404 is what
failed build fe9061f3, *after* the bad deploy had already landed, which is
why the failure looked unrelated to the clobber. Switched to the
script-scoped /workers/scripts/$DEPLOYED_NAME/settings endpoint, which
works for production and staging alike (confirmed live: it returns the same
binding set for chittyconnect as the legacy path did).
Prevention belongs at the infra layer — the branch trigger must not be bound
to the production script. These guards make a recurrence loud and immediately
diagnosable instead of silent.
Co-Authored-By: Claude <noreply@anthropic.com>
✅ Production restored — incident closed. PR still blocked on CI decoupling.RestoreRolling back to v947 did not work, and the reason is worth recording. Cloudflare's Rollbacks doc, verbatim:
A rollback re-points traffic at an old version's code; it does not revert bindings or vars. The damage here was entirely bindings, so the rollback (deployment Fixed by re-running the
The build log independently confirms the binding arithmetic rather than us asserting it:
Two benign deviations, recorded not glossed
Unrelated find: version #949Version ContainmentTrigger Side effect: branch/preview deploys no longer exist at all. That's intended containment, but it's a capability gap until a real Remaining blocker for this PRJust one now — item 2 from the previous comment. The config diff in this PR is correct and validated; the bug is in Workers Builds' name-override behaviour, not in this diff. Before it can merge, Item 3 is now addressed in |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/safe-deploy.sh`:
- Around line 85-109: Post-deploy log inspection in scripts/safe-deploy.sh
cannot prevent a Workers Builds override, so block or disable this staging CI
deployment path before wrangler deploy runs until its trigger is bound to
chittyconnect-staging; do not rely on the exit-72 check as prevention. Keep the
staging target in wrangler.jsonc at lines 180-184 unchanged, and explicitly
treat that site as requiring no direct code change; rebind or disable the
dashboard trigger and restore chittyconnect before enabling staging deploys.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 556eda4a-ad7c-40ba-836d-6da25e6c1889
📒 Files selected for processing (2)
scripts/safe-deploy.shwrangler.jsonc
| CHITTYCONNECT_SAFE_DEPLOY=1 npx wrangler deploy --env "$ENV" --config "$WRANGLER_CFG" 2>&1 | tee "$DEPLOY_LOG" | ||
| WRANGLER_RC="${PIPESTATUS[0]}" | ||
| set -e | ||
|
|
||
| # ── 1a. Detect a CI-side Worker name override ──────────────────────────────── | ||
| # Cloudflare Workers Builds IGNORES the name in wrangler.jsonc and forces the | ||
| # name of the script its build trigger is bound to. It prints a warning and | ||
| # then deploys anyway: | ||
| # | ||
| # ▲ [WARNING] Failed to match Worker name. Your config file is using the | ||
| # Worker name "chittyconnect-staging", but the CI system expected | ||
| # "chittyconnect". Overriding using the CI provided Worker name. | ||
| # | ||
| # On 2026-07-25 that override put the *staging* var set onto the *production* | ||
| # script, because both build triggers were bound to the same script id. Nothing | ||
| # in this script noticed: the audit below checks the name we COMPUTED, not the | ||
| # name wrangler actually shipped to. Fail loud so a clobber is diagnosed in the | ||
| # build that caused it rather than discovered later in prod. | ||
| if grep -q "Failed to match Worker name" "$DEPLOY_LOG"; then | ||
| echo "::error::safe-deploy: CI overrode the Worker name. This deploy may have written env.$ENV config onto a DIFFERENT worker than intended." >&2 | ||
| echo " Expected to deploy: $DEPLOYED_NAME" >&2 | ||
| grep -m1 "but the CI system expected" "$DEPLOY_LOG" >&2 || true | ||
| echo " Fix the Workers Builds trigger so it is bound to a script named '$DEPLOYED_NAME', then redeploy." >&2 | ||
| echo " If this ran against production, restore it before doing anything else." >&2 | ||
| exit 72 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
This detects the Workers Builds override only after it has mutated the target Worker.
wrangler deploy completes before the log is inspected, so exit 72 only reports the already-applied staging-to-production overwrite described in the PR objectives. Rebind or disable the staging Workers Builds trigger and restore chittyconnect before enabling these staging deploys.
scripts/safe-deploy.sh#L85-L109: do not treat post-deploy log inspection as a preventive safeguard; block this CI path until its trigger targetschittyconnect-staging.wrangler.jsonc#L180-L184: keep this target, but do not enable the associated Workers Builds deployment until the dashboard trigger is bound tochittyconnect-staging.
📍 Affects 2 files
scripts/safe-deploy.sh#L85-L109(this comment)wrangler.jsonc#L180-L184
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/safe-deploy.sh` around lines 85 - 109, Post-deploy log inspection in
scripts/safe-deploy.sh cannot prevent a Workers Builds override, so block or
disable this staging CI deployment path before wrangler deploy runs until its
trigger is bound to chittyconnect-staging; do not rely on the exit-72 check as
prevention. Keep the staging target in wrangler.jsonc at lines 180-184
unchanged, and explicitly treat that site as requiring no direct code change;
rebind or disable the dashboard trigger and restore chittyconnect before
enabling staging deploys.
Problem
wrangler.jsoncset"legacy_env": false, putting chittyconnect in Cloudflare's deprecated service-environments model. Every build logged:Consequences:
productionservice environment was already grandfathered in.code 10223 — This account cannot create new non-production service environments. This is the source of the perpetually-reddeploy:stagingPR check.env.stagingwas fully declared; the account-level capability was the blocker.chittyconnect and chittysecrets were the only two repos across CHITTYOS/ and CHITTYFOUNDATION/ still setting
legacy_env: false.Changes
The production name pin is load-bearing.
env.productionhad nonamekey of its own, so it inherited. Without service environments wrangler name-suffixes by default — an unnamedenv.productionwould have targeted a brand-newchittyconnect-productionworker and stranded the live script along with itsconnect.chitty.cc/*route. That failure mode has already caused three prod binding-wipe incidents (#219, #207, and twice on 2026-06-03; #218 closed, #223 open).Verified the trap is real rather than theoretical:
env.dev, which still has nonamekey, now resolves tochittyconnect-dev.Follows the org's existing pattern —
chittyentity/workers/chittyagent-imessage/wrangler.jsonc(top-levelname+env.production.namepinned to the same value).Also: a shadow config was silently overriding the tracked one
scripts/safe-deploy.shranwrangler deploy --env "$ENV"with no--config. Wrangler's config discovery preferswrangler.jsonoverwrangler.jsoncand walks up parent directories — and an untrackedwrangler.json(gitignored at.gitignore:59, so invisible in CI) sat at the repo root shadowing the tracked file. The script was auditingwrangler.jsoncwhile deploying whatever wrangler happened to find.Both files happened to resolve to byte-identical production bindings, so nothing was broken — but it was un-reviewable drift waiting to happen, and it would have made this migration silently inert for local deploys.
--config "$WRANGLER_CFG"now pins the reviewed file on every deploy path; the stray file has been removed (backed up off-repo).Live verification — no route handoff
Confirmed against the live Cloudflare API (read-only) that the base script and the
productionservice environment are the same underlying Worker object, so this migration is a no-op on worker identity:/workers/scripts(base script)e66e8221e9712ac7d1473ccd1da42532…/workers/services/chittyconnect→default_environment.script.etage66e8221e9712ac7d1473ccd1da42532…Corroborated by identical
script_tag(19a1d50542d84acb99cd2c1ee8142c7f) and byte-identical binding sets (114 live bindings) via both paths.Decisive datapoint — route ownership:
{"pattern":"connect.chitty.cc/*","script":"chittyconnect"}The route is owned by the unsuffixed base script. Also confirmed:
chittyconnect-productiondoes not exist (10090),chittyconnect-stagingdoes not yet exist, andchittyconnecthas exactly one service environment (production) — consistent with the 10223 error on staging.No route handoff, no cutover window, no orphaned script.
Config validation — no credentials, no deploy
unstable_readConfig):--env production→"chittyconnect", routeconnect.chitty.cc/*--env staging→"chittyconnect-staging", no routeswrangler deploy --env production --dry-run→ 73 bindings, byte-identical to the pre-migration baseline (diff empty)--env staging --dry-run→ 73, identical to baselinescripts/lib/extract-declared-bindings.mjs productionindependently agrees at 73bash -n scripts/safe-deploy.shcleanRollback plan for
connect.chitty.cc/*Because the name is pinned to the existing script, the route never changes hands. If a prod deploy still needs reverting:
808c1147-a377-4107-a5c0-ec4d76d3439f/ versionac53e49e-63fc-4f78-aa35-2316e42eac46(#946,2026-07-24T23:32:35Z)0384daab-f6a6-4f70-a22d-ded03e9f05e7(#947,2026-07-25T02:15:33Z) — the Workers Build for fix(ci): stop a stale binding-drift issue from suppressing new incident alerts #268npx wrangler rollback --name chittyconnect, or revert this commit and re-runnpm run deploy:production10bad0c1831d44f29906383ea3c1c8d8on zone7a4f759e0928fb2be4772a2f72ad0df2toscript: chittyconnectExpected effect on CI
The
deploy:stagingcheck should go green for the first time — staging becomes a top-level worker (chittyconnect-staging, workers_dev only, no routes) instead of an impossible service environment. That staging deploy is the first real end-to-end proof of the migration and it cannot touch production.Refs #219, #207, #218, #223
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes