Skip to content

feat(#5632): support --pem-dir for CF mint deploy (PEM bootstrap parity) - #5950

Open
fullsend-ai-coder[bot] wants to merge 6 commits into
mainfrom
agent/5632-cf-deploy-pem-bootstrap
Open

feat(#5632): support --pem-dir for CF mint deploy (PEM bootstrap parity)#5950
fullsend-ai-coder[bot] wants to merge 6 commits into
mainfrom
agent/5632-cf-deploy-pem-bootstrap

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

  • CF deploy now supports --pem-dir for bootstrapping role PEMs and ROLE_APP_IDS on Cloudflare Workers, matching the existing GCP capability
  • PEM files are validated, verified against the GitHub App API, then stored as Worker secrets (e.g. CODER_APP_PEM); ROLE_APP_IDS is set as a Worker env var
  • Works for both durable and preview deploys — preview uses version-scoped --var for ROLE_APP_IDS while PEM secrets go on the shared durable Worker script

Changes

  • internal/cli/mint.go: Added pemDir parameter to runMintDeployCloudflare(), PEM loading/verification via loadAppSetPEMs(), ROLE_APP_IDS env var injection, PEM secret storage via StoreAgentPEM(), dry-run output, and updated help text
  • internal/cli/mint_test.go: Enhanced fakeCFWranglerRunner to capture envVars and secret calls; added 5 new tests covering dry-run with PEMs, full deploy with PEMs, preview deploy with PEMs, bad PEM dir validation, and no-warning verification

Testing

  • All new and existing CF deploy tests pass with race detection (go test -race)
  • go vet passes on changed packages
  • gofmt reports no formatting issues
  • Secret scan passes on all changed files

Closes #5632

Post-script verification

  • Branch is not main/master (agent/5632-cf-deploy-pem-bootstrap)
  • Secret scan passed (gitleaks — 8549ba72c380c8d969a6620c0a94f2abd30898d5..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

CF deploy (--platform=cloudflare) can now bootstrap role PEMs and
ROLE_APP_IDS using the same --pem-dir flag that GCP already supports.

When --pem-dir is provided:
1. PEM files are validated and each key is verified against the
   GitHub App API (same loadAppSetPEMs path GCP uses).
2. ROLE_APP_IDS is set as a Worker env var via --var during deploy.
3. Each role PEM is stored as a Worker secret (e.g. CODER_APP_PEM)
   via wrangler secret put after the Worker is deployed.

This works for both durable and preview deploys. Preview deploys
pass ROLE_APP_IDS via --var on wrangler versions upload (version-
scoped), while PEM secrets are stored on the durable Worker script
(shared with production, as wrangler secret put targets the script).

The --pem-dir flag description drops its "(GCP only)" suffix and
is removed from the CF irrelevant-flags warning list. The deploy
command long help now documents --pem-dir for both platforms.

Note: golangci-lint could not run (not installed in sandbox).
go vet and gofmt passed. Pre-commit could not run (network error
in sandbox); post-script runs it authoritatively.

Closes #5632
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 5, 2026 19:09
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Agent PR ready for human review label Aug 5, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 5, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:10 PM UTC · Completed 7:25 PM UTC
Commit: 696acf0 · View workflow run →

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.78723% with 38 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/dispatch/cf/provisioner.go 67.10% 18 Missing and 7 partials ⚠️
internal/cli/mint.go 88.39% 9 Missing and 4 partials ⚠️

📢 Thoughts on this report? Let us know!

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review

Findings

High

  • [stale-doc] docs/cli/mint.md:77 — The flags table describes --pem-dir as "(GCP only, first-time bootstrap)" and --public as "(GCP only)", but this PR makes both flags cross-platform (registered under common flags, available for both GCP and Cloudflare).
    Remediation: Update the flags table to remove "(GCP only)" qualifiers and document cross-platform behavior.

Medium

  • [missing-doc] docs/cli/mint.md:82 — The flags table is missing the new Cloudflare-specific flags: --allowed-orgs, --per-repo-wif-repos, --workflow-host-repos, and --app-set.
    Remediation: Add rows for the new flags to the table with descriptions and platform scope.

  • [stale-doc] docs/cli/mint.md:66 — The Cloudflare mode section describes authentication as requiring CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN environment variables, but the PR adds Wrangler OAuth session support as an alternative via ResolveCloudflareAuth.
    Remediation: Update to document both authentication options: API token env vars and Wrangler OAuth session (wrangler login).

Low

  • [edge-case] internal/dispatch/cf/provisioner.go:555deployDurable accepts the secrets parameter but ignores it (named _). The doc comment explains the design (wrangler deploy does not support --secrets-file), and the CLI correctly gates Config.Secrets to preview deploys only. However, the interface allows constructing a Config with DeployMode=DeployDurable and non-nil Secrets, which would silently drop them.

  • [secrets-handling] internal/dispatch/cf/provisioner.go:690writeSecretsFile creates a temporary file for PEM keys via os.CreateTemp, which uses 0600 permissions per Go's standard library. The doc comment claims restrictive permissions but relies on implicit Go behavior rather than an explicit os.Chmod call.

  • [authorization] internal/cli/mint.go:659--public silently overrides an explicit --per-repo-wif-repos value to *. A user who passes both flags may not realize the explicit restriction is discarded. The override behavior is documented in the flag help text and tested intentionally.

  • [scope-creep] internal/cli/mint.go:470 — The PR adds --public flag support for Cloudflare, but issue mint deploy --platform=cloudflare: bootstrap role PEMs and ROLE_APP_IDS (parity with GCP non-WIF pieces) #5632's suggested serial order says "CF --public can follow this issue." The implementation is minimal (one line: perRepoWIFRepos = "*") and falls naturally from --per-repo-wif-repos support.

  • [architectural-alignment] internal/cli/mint.go:470 — The --public flag has divergent semantics across platforms: GCP sets ALLOWED_ORGS=* while CF sets PER_REPO_WIF_REPOS=*. The divergence is explicitly documented in the flag help text and reflects the different authorization stacks (GCP uses WIF pools; CF uses Worker env vars).

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:135 — The "Bootstrapping PEMs" section describes PEM bootstrapping exclusively in GCP terms. Now that --pem-dir works with Cloudflare, this section should mention CF support.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:123 — The flags table for mint deploy only lists GCP-specific flags and omits Cloudflare-specific flags.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:146 — The IAM requirements note for PEM bootstrapping reads as universal but describes GCP-specific roles.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run

Review

Findings

High

  • [stale-doc] docs/cli/mint.md:77 — The flags table describes --pem-dir as "(GCP only, first-time bootstrap)" and --public as "(GCP only)", but this PR makes both flags cross-platform. The cobra flag registration confirms --pem-dir and --public are now common flags (not under the GCP-specific section).
    Remediation: Update the --pem-dir and --public rows to remove "(GCP only)" qualifiers and document cross-platform behavior.

Medium

  • [missing-doc] docs/cli/mint.md:82 — The flags table is missing the new Cloudflare-specific flags: --allowed-orgs, --per-repo-wif-repos, and --workflow-host-repos.
    Remediation: Add rows for --allowed-orgs, --per-repo-wif-repos, and --workflow-host-repos to the flags table.

Low

  • [missing-doc] docs/cli/mint.md:53 — The Cloudflare mode section does not mention --pem-dir or the new configuration flags. The cobra command help already documents these, reducing urgency.

  • [architectural-alignment] internal/cli/mint.go:466 — The --public flag help text documents "GCP: ALLOWED_ORGS=; Cloudflare: PER_REPO_WIF_REPOS=". The Cloudflare side is correct per ADR 0078. The GCP side reflects existing behavior not changed by this PR.

  • [secrets-handling] internal/dispatch/cf/provisioner.go:580writeSecretsFile creates a temporary file for PEM keys via os.CreateTemp, which uses 0600 permissions per Go's standard library. Defensive-coding suggestion: an explicit os.Chmod call would make the permission guarantee visible to readers.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:135 — The "Bootstrapping PEMs" section describes PEM bootstrapping exclusively in GCP terms. Now that --pem-dir works with Cloudflare, this section should mention CF support.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:123 — The flags table for mint deploy only lists GCP-specific flags and omits Cloudflare-specific flags.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:146 — The IAM requirements note for PEM bootstrapping reads as universal but describes GCP-specific roles.

  • [edge-case] internal/dispatch/cf/provisioner.go:559deployDurable accepts the secrets parameter but ignores it (named _). The doc comment explains this design, but the interface allows constructing Config{DeployMode: DeployDurable, Secrets: ...} that silently drops secrets.


Labels: PR modifies mint CLI deploy code and CF dispatch provisioner


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (2)

Review

Findings

High

  • [stale-doc] docs/cli/mint.md:77 — The flags table describes --pem-dir as "(GCP only, first-time bootstrap)" but this PR removes the GCP-only qualifier from the source. The flag now works with --platform=cloudflare.
    Remediation: Update the --pem-dir row to remove "(GCP only)" and note that it works with both platforms.

Medium

  • [stale-doc] docs/cli/mint.md:53 — The Cloudflare mode section does not mention --pem-dir as an available flag or document PEM bootstrapping behavior for CF Workers. The CLI help text now lists it under Cloudflare optional flags.
    Remediation: Add --pem-dir to the Cloudflare optional flags list and document the PEM bootstrapping behavior.

Low

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:135 — The "Bootstrapping PEMs" section describes PEM bootstrapping exclusively in GCP terms (example uses --project, note references GCP IAM roles). Now that --pem-dir works with Cloudflare, this section should mention CF support.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:123 — The flags table for mint deploy only lists GCP-specific flags and omits Cloudflare-specific flags. Pre-existing gap made more noticeable by --pem-dir becoming cross-platform.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:146 — The IAM requirements note for PEM bootstrapping reads as universal but describes GCP-specific roles. With Cloudflare support added, this should be qualified as GCP-specific.

  • [edge-case] internal/dispatch/cf/provisioner.go:438deployDurable accepts the secrets parameter but ignores it (named _). The current CLI flow is correct (durable deploys store PEMs via StoreAgentPEM after deploy), but a future caller of Provision() that sets Config.Secrets for a durable deploy will have its secrets silently dropped.

  • [architectural-alignment] internal/cli/mint.go:455 — The --public flag still retains "(GCP only)" marker. The issue context mentions CF --public will follow as separate work.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (3)

Review

Findings

High

  • [stale-doc] docs/cli/mint.md:77 — The flags table describes --pem-dir as "(GCP only, first-time bootstrap)" but this PR removes the GCP-only qualifier from the source. The flag now works with --platform=cloudflare.
    Remediation: Update the --pem-dir row to remove "(GCP only)" and note that it works with both platforms.

Medium

  • [api-contract] internal/dispatch/cf/provisioner.go:491LiveWranglerRunner.PutSecret passes --preview-alias to wrangler secret put, but the Cloudflare Wrangler CLI’s secret put subcommand may not support --preview-alias. This flag is recognized by wrangler versions upload but not documented for wrangler secret put. Depending on the wrangler version, this will either cause an "unknown flag" error (breaking PEM bootstrap on preview deploys) or be silently ignored (storing the secret globally on the Worker rather than scoping it to the preview version).
    Remediation: Verify wrangler secret put supports --preview-alias. If not, use wrangler versions secret put or bundle secrets via --secrets-file on wrangler versions upload.

  • [stale-doc] docs/cli/mint.md:53 — The Cloudflare mode section does not mention --pem-dir as an available flag or document PEM bootstrapping behavior for CF Workers. The CLI help text now lists it under Cloudflare optional flags.
    Remediation: Add --pem-dir to the Cloudflare optional flags list and document the PEM bootstrapping behavior.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:135 — The "Bootstrapping PEMs" section describes PEM bootstrapping exclusively in GCP terms (example uses --project, note references GCP IAM roles). Now that --pem-dir works with Cloudflare, this section should mention CF support.
    Remediation: Add a note or example showing --pem-dir usage with --platform=cloudflare.

Low

  • [parameter-ordering] internal/cli/mint.go:613runMintDeployCloudflare places pemDir after dryRun bool, while runMintDeployGCP has a similar pattern but with additional bool params. Minor inconsistency between sibling functions.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:123 — The flags table for mint deploy only lists GCP-specific flags and omits Cloudflare-specific flags. Pre-existing gap made more noticeable by --pem-dir becoming cross-platform.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:146 — The IAM requirements note for PEM bootstrapping reads as universal but describes GCP-specific roles. With Cloudflare support added, this should be qualified as GCP-specific.

  • [architectural-alignment] internal/cli/mint.go:455 — The --public flag still retains "(GCP only)" marker. The issue context mentions CF --public will follow as separate work.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (4)

Review

Findings

High

  • [stale-doc] docs/cli/mint.md:77 — The flags table describes --pem-dir as "(GCP only, first-time bootstrap)" but this PR removes the GCP-only qualifier from the source. The flag now works with --platform=cloudflare.
    Remediation: Update the --pem-dir row to remove "(GCP only)" and note that it works with both platforms.

Medium

  • [stale-doc] docs/cli/mint.md:53 — The Cloudflare mode section does not mention --pem-dir as an available flag or document PEM bootstrapping behavior for CF Workers. (The CLI help text is updated in the source, but the docs page is stale.)
    Remediation: Add --pem-dir to the Cloudflare optional flags list and document the PEM bootstrapping behavior.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:135 — The "Bootstrapping PEMs" section describes PEM bootstrapping exclusively in GCP terms (example uses --project, note references GCP IAM roles). Now that --pem-dir works with Cloudflare, this section should mention CF support.
    Remediation: Add a note or example showing --pem-dir usage with --platform=cloudflare.

  • [scope-coherence] internal/cli/mint.go:736 — PEM secrets are stored on the durable Worker script even for preview deploys. The issue acceptance criterion says "Preview path uses version-scoped secrets/vars where Wrangler requires it." ROLE_APP_IDS uses version-scoped --var, but PEM secrets use script-level wrangler secret put.
    Remediation: Verify whether Wrangler supports version-scoped secrets for preview deploys. If not (technical limitation), document this constraint in a code comment.

Low

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:146 — The IAM requirements note for PEM bootstrapping reads as universal ("PEM bootstrapping requires...") but describes GCP-specific roles. With Cloudflare support added, this should be qualified as GCP-specific.

  • [parameter-ordering] internal/cli/mint.go:611runMintDeployCloudflare places pemDir between string params and dryRun bool, while runMintDeployGCP interleaves pemDir between bool parameters. Minor inconsistency between sibling functions.

  • [architectural-alignment] internal/cli/mint.go:446 — The --public flag still retains "(GCP only)" marker. The issue context mentions CF --public will follow as separate work.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (5)

Review

Findings

High

  • [stale-doc] docs/cli/mint.md:77 — The flags table describes --pem-dir as "(GCP only, first-time bootstrap)" but this PR removes the GCP-only qualifier from the source. The flag now works with --platform=cloudflare.
    Remediation: Update the flags table entry to remove "(GCP only)".

  • [stale-doc] docs/cli/mint.md:53 — The Cloudflare mode section does not mention --pem-dir as an available flag or document PEM bootstrapping behavior for CF Workers. The PR adds --pem-dir to the Cloudflare deploy path and the CLI help text lists it under Cloudflare optional flags.
    Remediation: Add --pem-dir documentation in the Cloudflare mode section.

Medium

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:135 — The "Bootstrapping PEMs" section describes PEM bootstrapping exclusively in GCP terms (example uses --project, note references GCP IAM roles). Now that --pem-dir works with Cloudflare, this section should mention CF support.
    Remediation: Extend the section to cover both platforms.

  • [code-organization] internal/cli/mint.go:446 — The --pem-dir flag registration still sits under the // GCP-specific flags. comment block, though it is now a cross-platform flag.
    Remediation: Move the flag registration to the // Common flags. block.

Low

  • [stale-doc] docs/guides/infrastructure/mint-administration.md:145 — The IAM requirements note for PEM bootstrapping reads as universal but is GCP-specific. The IAM role names are self-evidently GCP, making this a minor clarity issue.

  • [test-coverage-gap] internal/cli/mint_test.go:50 — The secretPutErr field was added to fakeCFWranglerRunner but no test exercises the PEM secret storage failure path.

  • [error-handling-gap] internal/cli/mint.go:739 — Partial PEM secret storage failure returns an error but doesn't indicate which secrets were already stored. Re-runs are safe due to --keep-vars.

  • [parameter-ordering] internal/cli/mint.go:608runMintDeployCloudflare appends pemDir after dryRun bool, while runMintDeployGCP interleaves it between bool parameters. Minor inconsistency.


Labels: PR modifies mint CLI deploy code (internal/cli/mint.go)


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the component/mint Token mint and cross-boundary credentials label Aug 5, 2026
@ifireball

Copy link
Copy Markdown
Member

/fs-fix

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 8:53 PM UTC · Completed 9:02 PM UTC
Commit: 696acf0 · View workflow run →

- Move --pem-dir flag from GCP-specific to Common flags block since it
  is now used by both GCP and Cloudflare platforms
- Add test for PEM secret storage failure path (exercises secretPutErr)
- Improve error message on partial PEM secret storage to report progress
  (N/M already stored) and note that re-run is safe
- Reorder runMintDeployCloudflare parameters to group string params
  before bool, matching runMintDeployGCP pattern

Addresses review feedback on #5950
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Addressed all 4 review findings: moved --pem-dir to common flags, added PEM secret failure test, improved partial-failure error messaging, and reordered function parameters for cross-platform consistency.

Fixed (4):

  1. --pem-dir flag registered under GCP-specific flags block (internal/cli/mint.go): Moved --pem-dir flag registration from the GCP-specific flags block to the Common flags block, since it is now used by both GCP and Cloudflare platforms
  2. secretPutErr field has no test coverage (internal/cli/mint_test.go): Added TestMintDeployCmd_CloudflareDeployPemSecretFailure test that sets secretPutErr on the fake wrangler runner and verifies the deploy returns an error with the expected message including role name, progress count, and re-run safety note
  3. Partial PEM secret storage failure lacks progress context (internal/cli/mint.go): Enhanced error message and StepFail output to include progress (N/M already stored) and note that re-run is safe, so operators know exactly which secrets succeeded before the failure
  4. runMintDeployCloudflare parameter ordering inconsistent with runMintDeployGCP (internal/cli/mint.go): Reordered runMintDeployCloudflare parameters to group all string parameters (workerName, sourceDir, previewAlias, pemDir) before the bool parameter (dryRun), matching the pattern used by runMintDeployGCP

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 5, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:03 PM UTC · Completed 9:21 PM UTC
Commit: aa7ded2 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix putting the secrets on the durable deploy for a preview deployment will not work. The LiveWranglerRunner PutSecret method should instead accept a previewAlias parameter like other commands and pass the relevant arguments to wrangler as needed. The provisioner StoreAgentPEM method should then call it passing the parameter as needed.

Also deal with other review agent comments

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 5:32 AM UTC · Completed 5:56 AM UTC
Commit: aa7ded2 · View workflow run →

PutSecret on the WranglerRunner interface now accepts a previewAlias
parameter so preview deploys scope secrets to the preview version
via --preview-alias instead of targeting the durable Worker script.
StoreAgentPEM forwards the provisioner's PreviewAlias to PutSecret.

Also reorder runMintDeployCloudflare parameters to group strings
before bools (addresses review feedback on parameter ordering).

Addresses review feedback on #5950
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Addressed 2 findings (human instruction + parameter ordering). Disagreed with 3 review agent findings that were factually incorrect: pemDir flag was already in Common block, PEM failure test already exists, and error messages already include stored-count information. Key change: WranglerRunner.PutSecret now accepts previewAlias so preview deploys scope secrets correctly instead of targeting the durable Worker.

Fixed (2):

  1. PutSecret should accept previewAlias for preview deploys (internal/dispatch/cf/provisioner.go): Added previewAlias parameter to WranglerRunner.PutSecret interface and LiveWranglerRunner.PutSecret implementation. When previewAlias is non-empty, --preview-alias is passed to wrangler secret put. StoreAgentPEM now forwards p.cfg.PreviewAlias to PutSecret. Added tests for StoreAgentPEM with preview alias and LiveWranglerRunner preview command error.
  2. parameter ordering inconsistency between runMintDeployGCP and runMintDeployCloudflare (internal/cli/mint.go): Reordered runMintDeployCloudflare parameters from (workerName, sourceDir, previewAlias, pemDir string, dryRun bool) to (workerName, sourceDir, previewAlias string, dryRun bool, pemDir string) so dryRun bool follows the string group, matching the GCP function's pattern of grouping types together.

Disagreed (3):

  1. --pem-dir flag registration still sits under GCP-specific flags comment block: The PR already moved --pem-dir from the GCP-specific flags block to the Common flags block (line 447 in current code). The review agent's finding is outdated — the flag registration is correctly placed under '// Common flags.' in the current code.
  2. No test exercises the PEM secret storage failure path: TestMintDeployCmd_CloudflareDeployPemSecretFailure already exists (line 863 in mint_test.go). It sets secretPutErr to simulate failure and verifies the error message includes the role name, count of already-stored secrets, and the underlying error.
  3. Partial PEM secret storage failure does not indicate which secrets were already stored: The error message already includes this information: fmt.Errorf("storing PEM for role %s (%d/%d already stored; re-run is safe): %w", role, i, len(pemRoles), err). The %d/%d pattern shows exactly how many secrets were stored before the failure.

Tests: passed

Decision points
  • Chose to pass --preview-alias to wrangler secret put when previewAlias is set (alternatives: Use wrangler versions secret put instead, Pass --env flag to wrangler; rationale: Mirrors the existing pattern used by Deploy (which passes --preview-alias to wrangler versions upload). Using the same flag name provides consistency across wrangler operations.)

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:58 AM UTC · Completed 6:16 AM UTC
Commit: 71a491d · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix --preview-alias cannot be passed to "wrangler secret put", to attach secrets to a particular preview version, the secrets must all be passed via a file specified to the --secrtes-file parameter of the "wrangler version upload" command, which can also accept --preview-alias to upload a preview version. Consider all changes needed for a deployment process that would be based on a single invocation of "wrangler version upload". The wrngler version comamnd required wrangler v3.73.0 or newer so upgrade what we use if we currently use an older version.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:18 AM UTC · Completed 6:37 AM UTC
Commit: 71a491d · View workflow run →

…ngler secret put

wrangler secret put does not support --preview-alias, so preview deploys
cannot scope PEM secrets to a preview version via separate PutSecret
calls. Instead, pass secrets through Deploy via --secrets-file on
wrangler versions upload. Durable deploys continue using PutSecret
(which works without --preview-alias).

Changes:
- Add secrets parameter to WranglerRunner.Deploy interface
- Add Config.Secrets field for passing secrets through Provision
- Remove previewAlias from WranglerRunner.PutSecret (not supported by CLI)
- Write secrets to temp JSON file and pass --secrets-file in deployPreview
- Add PEMSecretsFromRoles helper for role→secret-name conversion
- In mint.go: preview deploys pass PEMs via Config.Secrets; durable
  deploys keep post-deploy StoreAgentPEM loop

Wrangler v3.73.0+ is required for --secrets-file support; package.json
already specifies wrangler ^4.0.0 so no version upgrade was needed.

Addresses review feedback on #5950
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (human-triggered)

Addressed all human findings. Removed --preview-alias from PutSecret (not supported by wrangler secret put). Preview PEM secrets now go through wrangler versions upload via --secrets-file. Durable deploys unchanged. Wrangler v4.0.0 already in use (above v3.73.0 requirement). All tests pass with race detection.

Fixed (4):

  1. wrangler secret put does not support --preview-alias (internal/dispatch/cf/provisioner.go): Removed --preview-alias from PutSecret. For preview deploys, PEM secrets are now passed through Deploy via --secrets-file on wrangler versions upload. Added secrets parameter to WranglerRunner.Deploy interface, writeSecretsFile helper, and PEMSecretsFromRoles conversion function.
  2. preview deploy should use single wrangler versions upload invocation with --secrets-file (internal/cli/mint.go): For preview deploys with PEMs, secrets are now built via cf.PEMSecretsFromRoles and passed through Config.Secrets to the deploy command. The post-deploy StoreAgentPEM loop is skipped for preview deploys since secrets were already included via --secrets-file. Durable deploys continue using separate PutSecret calls.
  3. update tests for new interface signatures (internal/dispatch/cf/provisioner_test.go): Updated fake WranglerRunner implementations, replaced TestProvisioner_StoreAgentPEM_WithPreviewAlias with TestProvisioner_Provision_PreviewWithSecrets that verifies secrets flow through Deploy. Added tests for PEMSecretsFromRoles and writeSecretsFile. Removed TestLiveWranglerRunner_PutSecret_PreviewCommandError (no longer valid).
  4. update CLI tests for new preview PEM flow (internal/cli/mint_test.go): Updated TestMintDeployCmd_CloudflarePreviewDeployWithPemDir to verify PEM secrets go through Deploy (not PutSecret) for preview deploys. Removed previewAlias from fakeCFSecretCall since PutSecret no longer accepts it.

Disagreed (1):

  1. upgrade wrangler version to v3.73.0+: package.json already specifies wrangler ^4.0.0 which is newer than v3.73.0. No version upgrade was needed.

Tests: passed

Strategy change: Previous iterations tried to pass --preview-alias to wrangler secret put. This iteration fundamentally changes the approach: preview secrets are now included in the deploy command via --secrets-file, eliminating the need for separate PutSecret calls for preview deploys.

Decision points
  • Chose to keep wrangler deploy for durable deploys and only use --secrets-file for preview deploys (alternatives: Switch all deploys to wrangler versions upload + versions deploy, Add --secrets-file to wrangler deploy (uncertain if supported); rationale: wrangler secret put works correctly for durable deploys (no --preview-alias needed). The bug only affects preview deploys. Switching durable deploys to wrangler versions upload would require a second wrangler versions deploy step to activate, adding complexity without fixing a bug.)
  • Added secrets parameter to WranglerRunner.Deploy interface rather than a separate method (alternatives: Add a DeployWithSecrets method, Add secrets to Config only; rationale: Adding to the existing Deploy method keeps the interface simple and avoids method proliferation. The secrets parameter is nil for most calls and only used when PEMs are present.)

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:38 AM UTC · Completed 6:55 AM UTC
Commit: a4d52ac · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix

We need to adjust the design because ot the linearity of wrangler worker deployment versions.

For preview deploys we will only support the mint deploy command (and undeploy counterpart), so we need to be able to specify all the mint configuration with one command so it needs to accept options to set ALLOWED_ORGS, PER_REPO_WIF_REPOS and WORKFLOW_HOST_REPOS with --public being an alias for setting PER_REPO_WIF_REPOS to "*".

Lets try to unify the code path for preview and non-preview deployments as much as possible

Non-preview deployments will also support configuration commands such as "add-role" and "enroll", but those would setup new deployment versions based on the last deployed version, so not applicable to a multiple previews situation where the latest deployed version might refer to the wrong code version (but the other commands are out of scope for this PR)

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 7:38 AM UTC · Completed 7:59 AM UTC
Commit: a4d52ac · View workflow run →

Add --allowed-orgs, --per-repo-wif-repos, --workflow-host-repos flags
to `mint deploy --platform=cloudflare` so preview deploys can receive
all mint configuration in a single command. Make --public work for CF
as an alias for --per-repo-wif-repos="*".

These flags set Worker env vars via the unified cfEnvVars → Config.EnvVars
→ wrangler --var path, which works identically for both preview and
durable deploys.

Addresses review feedback on #5950
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 4 (human-triggered)

Added --allowed-orgs, --per-repo-wif-repos, --workflow-host-repos flags and made --public work for CF deploys. All config env vars use the unified cfEnvVars path for both preview and durable deploys. 10 new tests added.

Fixed (5):

  1. Add deploy config flags for preview and durable CF deploys (internal/cli/mint.go): Added --allowed-orgs, --per-repo-wif-repos, --workflow-host-repos flags to mint deploy --platform=cloudflare. These set ALLOWED_ORGS, PER_REPO_WIF_REPOS, WORKFLOW_HOST_REPOS as Worker env vars via --var flags during deploy.
  2. Make --public work for CF as alias for PER_REPO_WIF_REPOS=* (internal/cli/mint.go): Moved --public from GCP-only to common flag. On CF, --public sets PER_REPO_WIF_REPOS to '', overriding any explicit --per-repo-wif-repos value. On GCP, behavior is unchanged (ALLOWED_ORGS=).
  3. Unify preview and non-preview code paths (internal/cli/mint.go): Built cfEnvVars map early from deploy flags before PEM handling. Both preview and durable deploys use the same Config.EnvVars -> wrangler --var path for all config env vars (ALLOWED_ORGS, PER_REPO_WIF_REPOS, WORKFLOW_HOST_REPOS, ROLE_APP_IDS).
  4. Update warnIrrelevantFlags for new flags (internal/cli/mint.go): Added allowed-orgs, per-repo-wif-repos, workflow-host-repos to GCP irrelevant list. Removed public from CF irrelevant list since it now works on both platforms.
  5. Add comprehensive tests for new config flags (internal/cli/mint_test.go): Added 10 new tests: durable deploy with config flags, preview deploy with config flags, --public sets PER_REPO_WIF_REPOS=*, --public overrides explicit value, dry-run output, omitted flags don't set env vars, CF flags warn on GCP, --public doesn't warn on CF, new flags exist.

Tests: passed

Strategy change: Iteration 4: shifted from PEM secret handling fixes to a design adjustment adding deploy-time config flags per human instruction.

Decision points
  • Made --public a common flag with platform-specific behavior rather than duplicating it (alternatives: Separate --public-cf and --public-gcp flags, Keep --public GCP-only and add --per-repo-wif-repos-all for CF; rationale: The human instruction says --public should work for CF. A common flag with platform-aware behavior keeps the CLI surface simple.)
  • New config flags are CF-specific, not common (alternatives: Make them common flags usable by both GCP and CF; rationale: GCP manages these values via separate commands (enroll, workflow-host add/remove) which also handle WIF conditions. CF passes them as simple env vars during deploy. Platform-specific flags match the underlying architecture.)

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:01 AM UTC · Completed 8:17 AM UTC
Commit: 73907f0 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix Add --app-set to Cloudflare mint deploy (parity with app-set usage elsewhere).

Why

mint deploy --platform=cloudflare --pem-dir=… hardcodes appsetup.DefaultAppSet (fullsend-ai) when calling loadAppSetPEMs. That blocks bootstrapping PEMs for a non-default app set such as fullsand-ai (fullsand-ai-coder, etc.).

What to implement

  • Add --app-set flag on mint deploy (same validation as elsewhere via appsetup.ValidateAppSet).
  • Default remains fullsend-ai / appsetup.DefaultAppSet when omitted.
  • Thread the chosen app set into the CF --pem-dir path (loadAppSetPEMs(ctx, pemDir, appSet)), dry-run messaging, and deploy summary — not only GCP.
  • Help text should document --app-set for Cloudflare PEM bootstrap (and GCP if that path also hardcodes the default today).
  • Tests: CF deploy with --pem-dir + --app-set=<non-default> looks up {app-set}-{role} slugs; omitting --app-set keeps current fullsend-ai behavior.

Out of scope

Do not change enrollment/workflow-host CF support in this fix unless required to compile/pass tests for the flag wiring.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Fix · ⚠️ Cancelled · Started 10:12 AM UTC · Ended 10:13 AM UTC
Commit: 73907f0 · View workflow run →

@ifireball

Copy link
Copy Markdown
Member

/fs-fix Cancel/supersede the previous /fs-fix on this PR. Implement both of the following in one change.

1. --app-set on Cloudflare mint deploy

mint deploy --platform=cloudflare --pem-dir=… hardcodes appsetup.DefaultAppSet (fullsend-ai) when calling loadAppSetPEMs. That blocks bootstrapping PEMs for a non-default app set such as fullsand-ai (fullsand-ai-coder, etc.).

  • Add --app-set on mint deploy (validate via appsetup.ValidateAppSet).
  • Default remains appsetup.DefaultAppSet (fullsend-ai) when omitted.
  • Thread the chosen app set into the CF --pem-dir path (loadAppSetPEMs(ctx, pemDir, appSet)), dry-run messaging, and deploy summary — not only GCP.
  • Help text documents --app-set for Cloudflare PEM bootstrap (and GCP if that path also hardcodes the default today).
  • Tests: CF deploy with --pem-dir + --app-set=<non-default> looks up {app-set}-{role} slugs; omitting --app-set keeps current fullsend-ai behavior.

2. Accept Wrangler OAuth / login session (not only API token env)

Today ValidateCloudflareEnv() hard-requires CLOUDFLARE_API_TOKEN (and CLOUDFLARE_ACCOUNT_ID), so wrangler login OAuth cannot be used with fullsend mint deploy --platform=cloudflare even though Wrangler itself supports browser OAuth like gh auth login.

  • Prefer existing env credentials when set (CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID) — CI and explicit tokens must keep working.
  • If CLOUDFLARE_API_TOKEN is unset, allow deploy when a usable Wrangler user session exists (e.g. after wrangler login / wrangler whoami succeeds), instead of failing solely on the missing token env var.
  • If CLOUDFLARE_ACCOUNT_ID is unset, discover/select the account from the authenticated Wrangler session when possible (or fail with a clear message listing accounts / how to set the env var).
  • Update help text to document: API token env or wrangler login session.
  • Tests covering: token+account env still required/accepted as today; missing token but valid session path; missing both still errors clearly.

Out of scope

Do not add CF enroll/workflow-host subcommands in this fix unless required to compile/pass tests for the above.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 10:14 AM UTC · Completed 10:40 AM UTC
Commit: 73907f0 · View workflow run →

1. Add --app-set flag to `mint deploy` for both GCP and Cloudflare
   platforms, allowing PEM bootstrap against a non-default app set
   (e.g. fullsand-ai). Defaults to fullsend-ai when omitted.
   Validates via appsetup.ValidateAppSet and threads the chosen
   app set through loadAppSetPEMs, dry-run messaging, and deploy
   summary for both platforms.

2. Accept Wrangler OAuth login session as an alternative to
   CLOUDFLARE_API_TOKEN for CF deploy. ResolveCloudflareAuth()
   replaces the hard ValidateCloudflareEnv() requirement:
   - Prefers CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID when set
   - Falls back to wrangler login session when token is absent
   - Auto-discovers account ID from wrangler whoami output when
     CLOUDFLARE_ACCOUNT_ID is unset and exactly one account exists

Addresses review feedback on #5950
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 5 (human-triggered)

Implemented both features from the /fs-fix instruction: (1) --app-set flag on mint deploy for both GCP and CF platforms, threading custom app set through PEM bootstrap, dry-run messages, and deploy summaries; (2) Wrangler OAuth session support as an alternative to CLOUDFLARE_API_TOKEN, with automatic account ID discovery from wrangler whoami. All existing tests pass; 11 new tests added covering both features.

Fixed (5):

  1. Add --app-set flag on CF mint deploy (internal/cli/mint.go): Added --app-set flag to newMintDeployCmd() that threads through both runMintDeployGCP and runMintDeployCloudflare. Defaults to appsetup.DefaultAppSet (fullsend-ai) when omitted. Validates via appsetup.ValidateAppSet. Replaced all hardcoded appsetup.DefaultAppSet references with the resolved appSet variable in both GCP and CF deploy paths (dry-run messages, loadAppSetPEMs calls, and deploy summaries). Updated help text to document --app-set for both platforms.
  2. Accept Wrangler OAuth / login session (internal/dispatch/cf/provisioner.go): Added ResolveCloudflareAuth(ctx) function that replaces ValidateCloudflareEnv() in the CF deploy path. Resolution order: (1) CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID env vars, (2) Wrangler OAuth session via wrangler whoami with CLOUDFLARE_ACCOUNT_ID from env, (3) auto-discover account ID from wrangler whoami output when exactly one account exists. Updated help text to document both auth methods. Made WranglerWhoamiFn exported for test mockability.
  3. Tests for --app-set with non-default app set (internal/cli/mint_test.go): Added TestMintDeployCmd_CloudflareAppSetNonDefault verifying that --app-set=fullsand-ai causes slug lookups with fullsand-ai- prefix, and TestMintDeployCmd_CloudflareAppSetDefault verifying omitting --app-set uses fullsend-ai- prefix. Also added --app-set flag existence check to TestMintDeployCmd_NewFlagsExist.
  4. Tests for Wrangler OAuth auth path (internal/cli/mint_test.go): Added TestMintDeployCmd_CloudflareWranglerSession (deploy succeeds with wrangler session + CLOUDFLARE_ACCOUNT_ID, no API token), TestMintDeployCmd_CloudflareNoCredentialsError (both missing + whoami fails produces clear error), and updated TestMintDeployCmd_CloudflareMissingEnv to mock wrangler whoami.
  5. Unit tests for ResolveCloudflareAuth and parseWranglerWhoamiAccountID (internal/dispatch/cf/provisioner_test.go): Added 6 ResolveCloudflareAuth tests (token+account, token-only, session+account, session-discover, session-multiple-accounts, no-credentials) and 3 parseWranglerWhoamiAccountID tests (single, none, multiple accounts).

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:41 AM UTC · Completed 10:57 AM UTC
Commit: 9b00f01 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

// caller (Provisioner or CLI) after deploy completes — wrangler deploy
// does not support --secrets-file. The secrets parameter is accepted
// for interface consistency but not used in the deploy command.
func (r *LiveWranglerRunner) deployDurable(ctx context.Context, sourceDir, workerName string, envVars map[string]string, _ map[string][]byte) (string, error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] edge-case

deployDurable accepts the secrets parameter but ignores it (named _). The doc comment explains the design (wrangler deploy does not support --secrets-file), and the CLI correctly gates Config.Secrets to preview deploys only. However, the interface allows constructing a Config with DeployMode=DeployDurable and non-nil Secrets, which would silently drop them.

if err != nil {
return "", nil, fmt.Errorf("marshaling secrets: %w", err)
}
f, err := os.CreateTemp("", "wrangler-secrets-*.json")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] secrets-handling

writeSecretsFile creates a temporary file for PEM keys via os.CreateTemp, which uses 0600 permissions per Go's standard library. The doc comment claims restrictive permissions but relies on implicit Go behavior rather than an explicit os.Chmod call.

Comment thread internal/cli/mint.go

accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")
// Handle --public as an alias for --per-repo-wif-repos="*".
if public {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] authorization

--public silently overrides an explicit --per-repo-wif-repos value to *, widening authorization. A user who passes both flags may not realize the explicit restriction is discarded. The override behavior is documented in the flag help text and tested intentionally.

Comment thread internal/cli/mint.go
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "preview changes without making them")
cmd.Flags().StringVar(&pemDir, "pem-dir", "", "optional: directory containing {role}.pem files for PEM bootstrap")
cmd.Flags().StringVar(&appSet, "app-set", "", "app set name for PEM bootstrap (default: fullsend-ai)")
cmd.Flags().BoolVar(&public, "public", false, "deploy public mint (GCP: ALLOWED_ORGS=*; Cloudflare: PER_REPO_WIF_REPOS=*)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] scope-creep

The PR adds --public flag support for Cloudflare, but issue #5632's suggested serial order says 'CF --public can follow this issue.' The implementation is minimal (one line: perRepoWIFRepos = "*") and falls naturally from --per-repo-wif-repos support.

Comment thread internal/cli/mint.go
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "preview changes without making them")
cmd.Flags().StringVar(&pemDir, "pem-dir", "", "optional: directory containing {role}.pem files for PEM bootstrap")
cmd.Flags().StringVar(&appSet, "app-set", "", "app set name for PEM bootstrap (default: fullsend-ai)")
cmd.Flags().BoolVar(&public, "public", false, "deploy public mint (GCP: ALLOWED_ORGS=*; Cloudflare: PER_REPO_WIF_REPOS=*)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] architectural-alignment

The --public flag has divergent semantics across platforms: GCP sets ALLOWED_ORGS=* while CF sets PER_REPO_WIF_REPOS=*. The divergence is explicitly documented in the flag help text and reflects the different authorization stacks (GCP uses WIF pools; CF uses Worker env vars).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/dispatch Workflow dispatch and triggers component/mint Token mint and cross-boundary credentials ready-for-review Agent PR ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mint deploy --platform=cloudflare: bootstrap role PEMs and ROLE_APP_IDS (parity with GCP non-WIF pieces)

1 participant