feat(targets): add cloudflare deploy target#843
Merged
Conversation
Add a `cloudflare` release target that deploys a release artifact to
Cloudflare via the `wrangler` CLI. Supports two config-selectable modes:
- `deployType: pages` (default): `wrangler pages deploy` to a Pages
project. Always deploys to production by passing the configured
`productionBranch` (default `main`) as `--branch`, plus commit
provenance flags so wrangler does not infer git state from the
extracted-artifact temp dir.
- `deployType: worker`: `wrangler deploy` using a `wrangler.toml` in the
artifact.
Details:
- Secrets CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID are passed via env,
never argv. Config values that would expand to `${VAR}` are rejected so
they can't be interpolated into secrets.
- The deploy is a remote, irreversible operation, so it is explicitly
skipped in dry-run mode (including worktree mode).
- wrangler resolved via WRANGLER_BIN override; presence checked in the
constructor. Bundled in the Docker image (pinned wrangler@4.111.0).
- Refactor ghPages' single-top-level-dir flatten logic into a shared
`extractZipArchiveWithFlattening` util reused by both targets.
Contributor
|
BYK
commented
Jul 20, 2026
| |--------|-------------| | ||
| | `deployType` | `pages` (default) or `worker`. | | ||
| | `projectName` | Cloudflare Pages project name. **Required** when `deployType` is `pages`. | | ||
| | `productionBranch` | The Pages project's production branch name. Passed to `wrangler pages deploy --branch` so a release always targets the **production** environment. Default: `main`. This is the Cloudflare environment selector, not your git release branch. | |
Member
Author
There was a problem hiding this comment.
If this is not a git branch, then why is thwe default main instead of 'production'?
| | `projectName` | Cloudflare Pages project name. **Required** when `deployType` is `pages`. | | ||
| | `productionBranch` | The Pages project's production branch name. Passed to `wrangler pages deploy --branch` so a release always targets the **production** environment. Default: `main`. This is the Cloudflare environment selector, not your git release branch. | | ||
| | `wranglerCliPath` | Path to the `wrangler` binary. Default: `wrangler` (or the `WRANGLER_BIN` env var). | | ||
| | `workingDir` | Subdirectory within the extracted artifact to deploy from. For `worker` deploys this is where the `wrangler.toml` lives. | |
Member
Author
There was a problem hiding this comment.
Do we have auto discovery for this? If yes, we should document, if not we should.
| | Name | Description | | ||
| |------|-------------| | ||
| | `CLOUDFLARE_API_TOKEN` | Cloudflare API token with permission to deploy Pages/Workers. | | ||
| | `CLOUDFLARE_ACCOUNT_ID` | Cloudflare account ID. | |
Member
Author
There was a problem hiding this comment.
Account id doesn't seem like a secret and maybe we can infer it from a config file or somkething?
| ```yaml | ||
| targets: | ||
| - name: cloudflare | ||
| deployType: pages |
Member
Author
There was a problem hiding this comment.
Can we also infer this automatically?
BYK
added a commit
that referenced
this pull request
Jul 21, 2026
…ranch (#846) ## Summary Follow-up to #843 addressing the review comments left on the cloudflare docs. Three behavior changes + docs/tests. ### 1. Default `deployType: worker` (was `pages`) Cloudflare is steering new projects to Workers (with static assets) and positioning Pages as legacy, so `worker` is the forward-looking default. Pages remains fully supported via `deployType: pages`. ### 2. `CLOUDFLARE_ACCOUNT_ID` is now optional (not a secret) The account ID is an **identifier, not a credential**. Only `CLOUDFLARE_API_TOKEN` is a required secret now. The account ID is read from the environment and forwarded to `wrangler` **only when set**; otherwise `wrangler` auto-discovers it for single-account tokens (confirmed against wrangler v4 behavior). ### 3. `productionBranch` auto-inferred for Pages `wrangler pages deploy --branch <X>` deploys to production **only** when `<X>` exactly matches the project's server-side production branch — any other value silently produces a *preview* deploy. Previously we defaulted to `main`, which is a guess. Now: 1. If `productionBranch` is configured → used verbatim. 2. Else, if the account ID is known → read the project's `production_branch` from `GET /accounts/{id}/pages/projects/{name}` (the same call wrangler makes internally, so **no extra token scope** — Pages:Edit covers it). 3. Else → omit `--branch` (a bare deploy from the non-git temp dir defaults to production). A **404** (wrong project/account) fails loudly rather than masking a misconfig; transient/network errors fall back to omitting `--branch`. ## Safety - **Dry-run makes zero network calls**: the `isDryRun()` guard runs *before* both the inference `fetch()` and `spawnProcess`. - **No token leakage**: token is env-only (never argv), and error messages don't carry it. - **Defense-in-depth**: API-sourced branch values are also checked against the `${VAR}` env-expansion guard before reaching argv. - No new dependency — raw global `fetch` (Node 24.18.0 baseline). ## Testing - `pnpm test` — full suite green (1052 passed). 27 cloudflare tests covering: worker default, account-id optional/forwarded-when-set, branch inference (correct URL + `Bearer` auth header), config-branch-skips-API, 404 hard-fail, API-failure/no-account-id fallbacks, suspicious-branch guard, worker-never-calls-API, and dry-run-makes-no-fetch. - `tsc` / `pnpm lint` clean. Docs build clean. ## Review Adversarial subagent review: dry-run zero-network-call invariant verified by code walk; no token leak; no CRITICAL/MAJOR issues. Findings MINOR-1 (guard the API-sourced branch) and MINOR-3 (hard-fail on 404) were addressed in this PR. Addresses review comments on #843 (productionBranch default, account-id-not-a-secret, auto-discovery/inference). 🤖 Generated with [opencode](https://opencode.ai)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new
cloudflarerelease target that deploys a release artifact to Cloudflare via thewranglerCLI. Part of the getsentry/toolkit monorepo work (#842) — enables deploying the CLI docs website to Cloudflare on release.Two config-selectable deploy modes:
deployType: pages(default) →wrangler pages deploy <dir> --project-name <name> --branch <productionBranch> .... Always targets production by passing the configuredproductionBranch(defaultmain) as--branch(the Cloudflare environment selector, not a git branch), plus--commit-hash/--commit-message/--commit-dirtyprovenance so wrangler doesn't infer git state from the temp dir.deployType: worker→wrangler deployusing awrangler.tomlin the artifact (run withcwd= deploy dir).Configuration
deployTypepages(default) orworkerprojectNamepages)productionBranch--branch. DefaultmainwranglerCliPathwrangler, orWRANGLER_BIN)workingDirSecrets
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_IDare passed via env, never argv.Safety
spawnProcesswould otherwise run for real. (Local extraction still happens; only the remote deploy is skipped.)${VAR}viaspawnProcess's env-substitution are rejected, so a config string can't be interpolated into a secret.Docker
wrangler@4.111.0is installed globally in the runtime stage (x64-only image;workerdprebuilt binary is fine) with awrangler --versionsmoke check, matching how other target toolchains are shipped.Other changes
extractZipArchiveWithFlatteningutil reused by both targets (behavior unchanged).targets/cloudflare.md+ overview table row.Testing
pnpm test— full suite green (17 new cloudflare tests, incl. dry-run guard, secret-in-env, both deploy types, config validation).pnpm lint/tsc --noEmitclean.wrangler pages deployarg parsing (incl.--commit-dirty false) verified empirically against wrangler@4.111.0.Acceptance
Move the CLI docs website to Cloudflare and confirm a real
craft publishdeploy works end-to-end before the toolkit merge.🤖 Generated with opencode