[wrangler] add replace mode for --secrets-file - #15357
Closed
theoephraim wants to merge 7 commits into
Closed
Conversation
A bad file path or malformed contents now fails before the preview resource is created or assets are uploaded, instead of leaving a half-finished preview behind.
The API may echo the uploaded env on the deployment response; strip secret_text values as soon as it is received so --json output can never print them, matching how preview secret list only outputs names/types.
The diverged-config warning compared top-level bindings against config preview bindings only, so names supplied via --var or --secrets-file were reported as missing even though they were uploaded. Compare against the env that was actually sent with the deployment instead.
🦋 Changeset detectedLatest commit: 229e5af The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
workers-devprod
requested review from
a team and
emily-shen
and removed request for
a team
August 25, 2026 18:09
Contributor
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/codemods
@cloudflare/config
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-plugin
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
Add an opt-in --secrets-file-mode <merge|replace> flag to wrangler deploy and wrangler versions upload. The default (merge) keeps the existing additive behavior. Replace mode stops sending the secret_text/secret_key keep_bindings entries so remote secrets that are neither supplied in the secrets file nor declared in secrets.required are deleted (deploy) or not carried into the new version (versions upload). Wrangler always warns which remote secrets will be dropped (no confirmation prompt), and aborts under --strict. The flag errors when used without --secrets-file.
Extend --secrets-file-mode to the private beta preview surface, on top of the base --secrets-file/--var support for wrangler preview added in the preview-secrets-file-var-flags branch (PR cloudflare#15256). With --secrets-file-mode replace, wrangler preview converges the new Preview deployment's secret set to the file by reading the effective env off the create response and cutting one follow-up deployment that deletes the unlisted secrets (the server-side merge of base config and carried-over secrets cannot be suppressed in the create request); the secret-value redaction now runs after that convergence so the follow-up deployment's echoed env is redacted too. wrangler preview secret bulk accepts --secrets-file-mode replace as well, deleting existing secrets not present in the input within the same patch. secrets.required names are never dropped, the deletion warning is always logged, and behavior without the new flag is unchanged.
theoephraim
force-pushed
the
secrets-file-replace-mode
branch
from
August 25, 2026 18:32
cff5890 to
229e5af
Compare
theoephraim
marked this pull request as draft
August 25, 2026 18:55
Author
|
Closing this for now in favor of the design discussion in #15343. The client-side implementation here (replace mode for deploy and versions upload, plus the preview bulk variant) is ready to revive or resubmit once the overall approach is agreed, but the preview-create side needs API-level support to be atomic, and it makes sense to settle that design first rather than add more flags. |
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.
Builds on #15256; the base
--secrets-file/--varsupport forwrangler previewis reviewed there. This PR's own changes are the last two commits.Fixes #15343
This PR adds an opt-in "replace mode" for secrets when deploying with
--secrets-file, so that remote secrets not present in the provided file can be deleted instead of always being preserved. It also extends the mode to the private betawrangler previewsurface:wrangler previewgains--secrets-file-modeon top of the--secrets-filesupport added in #15256, andwrangler preview secret bulkgains--secrets-file-mode(see "Preview command coverage" below).The problem
--secrets-fileis forcibly additive today:wrangler deploysetskeepSecretswhenever a secrets file is supplied, andwrangler versions uploadsets it unconditionally, so the upload always sendskeep_bindings: ["secret_text", "secret_key"]. There is no way to converge the remote secret set to a declared set; secrets removed from the file linger on the Worker forever.The new flag
--secrets-file-mode <merge|replace>onwrangler deployandwrangler versions upload(and, with preview-specific semantics described below, onwrangler previewandwrangler preview secret bulk):merge(the default, and the behavior when the flag is omitted) keeps remote secrets that are not present in the file. Existing behavior is completely unchanged.replacedrops them: Wrangler simply does not send thesecret_text/secret_keyentries inkeep_bindings, and the Workers upload API's default behavior (no keep_bindings, no inherit bindings) is full replacement. Replace mode is purely client-side.Flag design notes:
--replace-secrets) because it names the existing default (merge), leaves room for future modes, and matches existing enum-valued flags such as--containers-rollout <immediate|gradual|none>, using the samechoicesmechanism.--keep-secretsboolean mirroring--keep-varswas also considered and rejected, for two reasons. First, the defaults would be opposite:--keep-varseffectively defaults to false (vars are replaced by the config on every deploy), while--keep-secretswould have to default to true, so the symmetric naming would suggest symmetric behavior that does not exist. Second, the symmetry is shallower than it looks at the API level: the upload API only replaces the secret set when the upload actually carries secret bindings, so--keep-secrets=falsewithout--secrets-filecould not be honored at all (there is nothing in the upload to replace with) and would either silently do nothing or need the same "requires--secrets-file" guard this flag has, at which point it is a mode of the secrets file rather than an independent switch. Tying the name to--secrets-filemakes that dependency explicit. There is also an existing entanglement where--keep-varsimplies keeping secrets; a new independent--keep-secretswould have had to define conflict semantics against that side effect, whereas a file-scoped mode simply takes precedence over it (see Semantics below).sharedDeployVersionsArgsnext to--secrets-fileand--keep-vars, so it is shared bydeployandversions uploadlike the flag it modifies.--secrets-file-modewithout--secrets-fileis an error (raised invalidateWorkerProps(), covering both commands).Semantics
secrets.requiredconfig field are always kept: they still get explicit{ type: "inherit" }bindings viaaddRequiredSecretsInheritBindings()and survive the upload. Replace mode only drops secrets that are neither supplied in the file nor declared as required, so a config-declared secret set stays coherent with the file-supplied one.--keep-vars=falsebehavior) deletes remote vars without prompting. Under--strictthe upload aborts instead of continuing, consistent with how the other pre-upload conflict checks inpreUploadApiChecks()treat strict mode (conflicts abort instead of auto-continuing).--keep-vars(which historically also kept secrets).--keep-varsstill keeps vars; only the secret handling follows the explicitly requested mode.wrangler versions upload, replace mode means the new version carries only the supplied secrets plus thesecrets.requiredinherit bindings. Gradual deployments implication: the removal only takes effect for traffic served by the new version, and old versions still have the old secrets while they receive a share of traffic. The pre-upload warning wording reflects this ("will be removed when this version is deployed").unsafebindings path and Pages are untouched (the Pages-to-Workers delegation passessecretsFileMode: undefined).Preview command coverage
The mode flag now covers the private beta
wrangler previewsurface too, in two places. The base feature it modifies (wrangler preview --secrets-file, which attaches the file's secrets to the new Preview deployment assecret_textbindings, plus--var) is implemented and reviewed in #15256; this PR only adds the mode on top of that plumbing.wrangler preview --secrets-file <file> --secrets-file-mode replaceconverges the new Preview deployment's secret set to the file. Mechanism: create-then-patch, not create-time exclusion. A Preview deployment's effective secret set is composed server-side; on top of the requestenv, the server can merge in secrets from the Preview base config on the parent Worker and secrets carried over from the Preview's earlier deployments (e.g. ones added withwrangler preview secrets put). The deployment creation API has no keep_bindings-style knob to suppress that merge, and merge-patchnulldeletions are only defined for the PATCH endpoint, so the create request cannot express "and nothing else". Instead, replace mode reads the effectiveenvoff the deployment creation response (falling back to fetching the deployment if the API omits the field), computes thesecret_textbindings that are neither in the file nor insecrets.required, and if that set is non-empty logs the same always-on warning and cuts one follow-up deployment patching those bindings tonull. This is robust to whatever the server-side merge does: wrangler converges the set it can actually observe rather than predicting the merge client-side. The tradeoff is a second Preview deployment when something is dropped (none when the set already converges); for an ephemeral preview that history costs nothing, and the follow-up deployment keeps the original message/tag annotations because the patch deliberately does not touch them.wrangler preview secret bulk <file> --secrets-file-mode replaceconverges an existing Preview's secret set to the file. Here no create is involved, so it is a single patch: wrangler fetches the latest Preview deployment'senv, computes thesecret_textbindings that are neither among the file's keys nor insecrets.required, warns, and includesnulldeletions for them in the same merge-patch that uploads the file's secrets, producing one new Preview deployment. Keys the file explicitly sets tonullare already deletions in the existing additive semantics and are excluded from the replace-computed set, so they are neither deleted nor reported twice.Preview-specific flag design and semantics notes:
preview secret bulk: the file is a positional argument rather than--secrets-file, so a plain--mode <merge|replace>was considered.--secrets-file-modewas chosen anyway, for uniformity: it is one flag name with one meaning acrossdeploy,versions upload,preview, andpreview secret bulk, so knowledge transfers between surfaces and scripts can pass the same flag everywhere. A bare--modeis also ambiguous on a command that could plausibly grow other modes, while--secrets-file-modestill reads correctly here because the positional argument is a secrets file (the same JSON/.env format).secrets.requiredapplies to previews the same way: replace mode never drops a secret named there. Previews have no inherit-binding mechanism, so nothing needs to be added for required secrets; they are simply excluded from the deletion set. This keeps a config-declared secret contract consistent across production deploys and previews.secret_textbindings count as secrets on the preview surface, matching the existingpreview secret list/put/bulkcommands, which exclusively read and writesecret_text(the deploy path also handlessecret_key, which does not appear on Preview deployments).--strictabort on the preview surface:wrangler previewandpreview secret bulkhave no--strictflag today, so replace mode there is warn-only, consistent with the rest of the (beta) preview UX. If previews grow a--strictflag, wiring the abort in is a one-liner next to the warning.--secrets-file-modewithout--secrets-fileerrors onwrangler preview(viavalidateArgs, before the Worker is built), with the same message asdeploy. There is deliberately no such error onpreview secret bulk: its secrets input is the positional file or piped stdin, so the mode always has an input to apply to, and the existing "no content found" early-exit already fires before any API call when there is none.Implementation
packages/wrangler/src/deployment-bundle/deploy-args.ts: newsecrets-file-modearg (choicesmerge/replace), updated--secrets-filehelp text.packages/wrangler/src/deployment-bundle/merge-config-args.ts,packages/deploy-helpers/src/shared/types.ts: threadsecretsFileModeinto the shared deploy/versions props.packages/deploy-helpers/src/deploy/deploy.tsandversions-upload.ts: computekeepSecretsfrom the mode.packages/deploy-helpers/src/deploy/helpers/check-remote-secrets-override.ts: newgetSecretsDroppedByReplaceMode()helper (reuses the existing remote secrets fetch, tolerates missing Workers).packages/deploy-helpers/src/deploy/helpers/validate-worker-props.ts: flag combination validation plus the pre-upload deletion warning (and the strict-mode abort).packages/deploy-helpers/src/preview/preview.ts:PreviewArgsgainssecretsFileMode(on top of [wrangler] Add --secrets-file and --var flags to wrangler preview #15256'ssecretsFileparsing and create-time attachment), andconvergePreviewDeploymentSecrets()implements the replace-mode follow-up patch. The secret-value redaction from [wrangler] Add --secrets-file and --var flags to wrangler preview #15256 now runs after the convergence, so the follow-up deployment's echoed env is redacted too.packages/wrangler/src/preview/preview.ts: the new--secrets-file-modeflag plus the mode-without-filevalidateArgscheck.packages/wrangler/src/preview/secrets/index.ts: newfetchPreviewDeploymentSecretNames()helper (reads the latest deployment'ssecret_textbinding names, mapping the API's no-deployment/not-found errors to the existing user-facing messages).packages/wrangler/src/preview/secrets/bulk.ts: the--secrets-file-modeflag and the replace-mode deletion merge into the single patch.Tests cover: replace mode sends no secret keep_bindings (deploy and versions upload), explicit merge and default behavior unchanged, the deletion warning lists only dropped secrets and excludes
secrets.required, the warning is emitted in both interactive and non-interactive sessions and the upload proceeds without prompting,--strictaborts the upload when secrets would be dropped,secrets.requiredinherit bindings survive replace mode, the flag without--secrets-fileerrors, invalid mode values are rejected, and unit tests forgetSecretsDroppedByReplaceMode().On the preview surface (the base
--secrets-fileattachment behavior is tested in #15256), tests additionally cover: merge/default cutting no follow-up deployment, replace deleting exactly the unlistedsecret_textbindings (plain bindings, file-supplied secrets, andsecrets.requireduntouched) with the follow-up deployment reported to the user, the fallback fetch when the create response omitsenv, secret values echoed back on the follow-up deployment being redacted from--jsonoutput, mode-without-file and invalid-mode errors onwrangler preview,preview secret bulkreplace deletions in a single patch (including no double-deletion of explicitly nulled keys and correct created/deleted counts), merge mode never reading the current deployment, and replace-mode error mapping when the Preview or its deployments do not exist.wrangler deploy --helpand the changeset; a follow-up cloudflare-docs PR will document--secrets-file-modealongside the existing--secrets-filedocs once this is merged and released.A picture of a cute animal (not mandatory, but encouraged)