Skip to content

feat(#6226): gate WIF secrets in per-repo shim by credential mode - #6241

Open
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/6226-scaffold-wif-gating
Open

feat(#6226): gate WIF secrets in per-repo shim by credential mode#6241
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/6226-scaffold-wif-gating

Conversation

@fullsend-ai-coder

@fullsend-ai-coder fullsend-ai-coder Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Gate WIF secrets (FULLSEND_GCP_WIF_PROVIDER, FULLSEND_GCP_PROJECT_ID) in the per-repo shim workflow template by credential mode — OIDC repos no longer get confusing WIF secret references in their generated .github/workflows/fullsend.yaml
  • Add CredentialMode to scaffold RenderOptions and thread it through CollectPerRepoInstallFiles, BuildScaffoldFiles, and FetchRemoteScaffold
  • Make FULLSEND_GCP_PROJECT_ID optional (required: false) in reusable-dispatch.yml so OIDC-mode shims don't fail validation
  • Document credential modes (wif/oidc/token) in the mint administration guide

Changes

  • internal/scaffold/render.go: Add CredentialMode field to RenderOptions; add stripWIFSecrets() that removes WIF secret lines when credential mode is oidc
  • internal/scaffold/installfiles.go: Add credentialMode parameter to CollectPerRepoInstallFiles
  • internal/repos/install.go: Resolve credential mode via resolveCredentialMode() in BuildScaffoldFiles before passing to scaffold layer
  • internal/repos/remote_scaffold.go: Thread credentialMode through FetchRemoteScaffold and fetchRemoteGitHubScaffold
  • internal/repos/batch_install.go: Pass resolved credential mode to FetchRemoteScaffold
  • internal/cli/github.go: Pass empty credential mode (backward compat for org-mode setup)
  • .github/workflows/reusable-dispatch.yml: Change FULLSEND_GCP_PROJECT_ID to required: false
  • docs/guides/infrastructure/mint-administration.md: Add "Credential modes" section

Testing

  • TestRenderPerRepoShimWIFMode — verifies WIF secrets present when credential mode is wif
  • TestRenderPerRepoShimOIDCMode — verifies WIF secrets stripped and OTEL secrets preserved when credential mode is oidc
  • TestRenderPerRepoShimDefaultCredMode — verifies empty credential mode defaults to including WIF secrets
  • TestCollectPerRepoInstallFiles_OIDCMode and TestCollectPerRepoInstallFiles_WIFMode — end-to-end tests through install file collection
  • TestFetchRemoteScaffold_GitHubOIDCMode — remote scaffold path strips WIF secrets in OIDC mode
  • All existing scaffold and repos tests pass

Out of scope

Upgrade SHA-pinning behavior change moved to a separate issue.


Closes #6226

@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 14, 2026 21:50
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Agent PR ready for human review label Aug 14, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 14, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:52 PM UTC · Completed 10:11 PM UTC

Commit: e3ac5c2 · View workflow run →

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

Site preview

Preview: https://ce4501f2-site.fullsend-ai.workers.dev

Commit: 8ac981b8404e2b91a93ae7ea403049cd133001b2

@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 14, 2026

Copy link
Copy Markdown

Review

Findings

Medium

Low

  • [secret requirement relaxation] .github/workflows/reusable-dispatch.yml:66FULLSEND_GCP_PROJECT_ID changed from required: true to required: false. Combined with FULLSEND_GCP_WIF_PROVIDER already being required: false, the reusable workflow now accepts calls with no GCP credentials. This is fail-closed in practice (setup-gcp still rejects empty values), but a YAML comment explaining why these secrets are optional and which modes require them would aid future maintainers.

  • [stale-doc] docs/guides/infrastructure/infrastructure-reference.md:240 — The "Per-Repo Mode Secrets/Variables > GitHub" section lists FULLSEND_GCP_PROJECT_ID and FULLSEND_GCP_WIF_PROVIDER as unconditional "Target repo secrets." With credential-mode-aware shim generation, OIDC-mode repos do not have these secrets.
    Remediation: Add a WIF-mode qualifier (e.g., "Target repo secrets (WIF mode only):").

  • [intent-alignment] internal/scaffold/render.gostripWIFSecrets uses exact indentation-sensitive string matching (6-space prefix). Template indentation changes would silently break stripping. Mitigated by existing tests.

  • [API-shape-consistency] internal/scaffold/installfiles.go:49CollectPerRepoInstallFiles takes 4 positional parameters (bool + 3 strings), while its sibling CollectInstallFiles uses a CollectInstallFilesOptions struct. The new credentialMode parameter deepens this divergence.

  • [growing-positional-parameter-list] internal/repos/remote_scaffold.go:34FetchRemoteScaffold now accepts 7 parameters including 4 consecutive strings, which is error-prone at call sites.

  • [magic-string-vs-constant] internal/scaffold/render.go:120stripWIFSecrets compares against the string literal "oidc" rather than a named constant. The CredModeOIDC constant in the repos package cannot be imported due to circular dependency; consider a package-local constant.

  • [stale-doc] docs/guides/dev/cli-internals.md:256 — Phase 6 diagram states "Both modes write the same credential set" and unconditionally lists FULLSEND_GCP_PROJECT_ID and FULLSEND_GCP_WIF_PROVIDER. With credential-mode gating, OIDC-mode installs omit these secrets.

  • [stale-doc] docs/guides/getting-started/operations.md:26 — The GitHub operations table lists FULLSEND_GCP_PROJECT_ID and FULLSEND_GCP_WIF_PROVIDER as unconditional "Repo secret" entries without noting they are WIF-mode only. The GitLab section on the same page already annotates its equivalent entries with a mode qualifier.


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

  • [API contract violation] .github/workflows/reusable-dispatch.yml — The setup-gcp composite action is called unconditionally in every stage job (triage, code, review, fix, retro, prioritize, harness-run) and its action.yml declares gcp_wif_provider as required: true. For OIDC-mode repos, the generated shim omits FULLSEND_GCP_WIF_PROVIDER (via stripWIFSecrets), so the secret will be empty at runtime, causing the google-github-actions/auth step to fail. OIDC-mode repos cannot successfully dispatch any agent stage. Issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226 item 2 ("setup-gcp action: skip when credential mode is oidc") is not addressed by this PR, which claims to close repos: scaffold template WIF gating and mint-administration docs for credential modes #6226.
    Remediation: Make the setup-gcp step conditional on the presence of WIF secrets (e.g., if: secrets.FULLSEND_GCP_WIF_PROVIDER != ''), or make the gcp_wif_provider input optional in setup-gcp/action.yml and add a conditional skip inside the composite action.

Medium

  • [scope-creep] internal/repos/upgrade.go:220 — The upgrade logic changes alter SHA pinning semantics in a way not authorized by issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226. Previously, all floating refs (tags, branches) were resolved to commit SHAs during upgrades, writing the pinned format (@<sha> # <ref>). Now SHA resolution is only performed for repos already SHA-pinned; tag-only or branch-pinned repos keep the string ref as-is. This spans ~60 lines of logic rewrite in upgrade.go, ~184 lines of test changes in upgrade_test.go, and documentation changes in repo-management.md. Issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226 scopes work to scaffold template WIF gating and mint-administration documentation.
    Remediation: Extract the upgrade.go, upgrade_test.go, and repo-management.md changes into a separate PR with its own issue that documents the rationale for changing SHA pinning behavior.

  • [breaking-change-risk] internal/repos/upgrade.go:293 — The upgrade behavior change may constitute a breaking change per COMMITS.md criteria: previously, running repos install on a tag-pinned repo (e.g., @v2.1.0 with target v2.3.0) would produce @<sha> # v2.3.0 (SHA-pinned output). Now the same operation produces @v2.3.0 (tag-only output). Users who depend on SHA pinning being applied universally during upgrades will see different workflow file contents. If this change is retained, the PR title needs the ! suffix and a BREAKING CHANGE trailer per COMMITS.md.
    Remediation: Evaluate whether this meets the breaking change threshold. If retained (in this PR or a separate one), mark the PR title with ! and add a BREAKING CHANGE trailer.

  • [stale-doc] docs/guides/infrastructure/infrastructure-reference.md:240 — The "Per-Repo Mode Secrets/Variables > GitHub" section lists FULLSEND_GCP_PROJECT_ID and FULLSEND_GCP_WIF_PROVIDER as unconditional "Target repo secrets" for all GitHub per-repo installations. With credential-mode-aware shim generation, OIDC-mode repos do not have these secrets in the generated workflow.
    Remediation: Add a WIF-mode qualifier (e.g., "Target repo secrets (WIF mode only):"), similar to the GitLab section's pattern.

  • [protected-path] .github/workflows/reusable-dispatch.yml — This PR modifies files under the .github/ protected path. The change is authorized by issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226 item 3 (change FULLSEND_GCP_PROJECT_ID to required: false). Human approval is required for all protected-path changes regardless of context.

Low

  • [intent-alignment] internal/scaffold/render.go:62stripWIFSecrets uses exact string matching against indentation-specific patterns (" FULLSEND_GCP_WIF_PROVIDER: ..."). Template indentation changes would silently break stripping (mitigated by existing tests).

  • [API-shape-consistency] internal/scaffold/installfiles.go:49CollectPerRepoInstallFiles takes 4 positional parameters (bool + 3 strings), while its sibling CollectInstallFiles uses a CollectInstallFilesOptions struct. The new credentialMode parameter deepens this divergence.

  • [growing-positional-parameter-list] internal/repos/remote_scaffold.go:37FetchRemoteScaffold now accepts 7 parameters including 4 consecutive strings, which is error-prone at call sites.

  • [magic-string-vs-constant] internal/scaffold/render.go:120stripWIFSecrets compares against the string literal "oidc" rather than a named constant. The CredModeOIDC constant in the repos package cannot be imported due to circular dependency; consider a package-local constant.

  • [stale-doc] docs/guides/dev/cli-internals.md:256 — Phase 6 diagram states "Both modes write the same credential set" and unconditionally lists FULLSEND_GCP_PROJECT_ID and FULLSEND_GCP_WIF_PROVIDER. With credential-mode gating, OIDC-mode installs omit these secrets.

  • [stale-doc] docs/guides/getting-started/operations.md:26 — The GitHub operations table lists FULLSEND_GCP_PROJECT_ID and FULLSEND_GCP_WIF_PROVIDER as unconditional "Repo secret" entries without noting they are WIF-mode only. The GitLab section on the same page already annotates its equivalent entries with a mode qualifier.


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

  • [API contract violation] .github/workflows/reusable-dispatch.yml — The setup-gcp composite action is called unconditionally in every stage job and its action.yml declares gcp_wif_provider as required: true. For OIDC-mode repos — where the generated shim omits FULLSEND_GCP_WIF_PROVIDER — the secret will be empty at runtime, causing the google-github-actions/auth step to fail. OIDC-mode repos cannot successfully dispatch any agent stage. This is issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226 item 2, which the PR claims to close but does not address.
    Remediation: Make the setup-gcp step conditional on the presence of WIF secrets (e.g., if: secrets.FULLSEND_GCP_WIF_PROVIDER != ''), or pass credential mode as a workflow input and skip setup-gcp when mode is oidc.

Medium

  • [scope-creep] internal/repos/upgrade.go:220 — The upgrade logic changes alter SHA pinning semantics: previously, all floating refs were resolved to SHAs during upgrades; now SHA pinning is only preserved for repos that are already SHA-pinned. This is a significant behavioral change not authorized by issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226, which scopes work to scaffold template WIF gating and mint-administration documentation.
    Remediation: Split the upgrade.go SHA-pinning changes (and corresponding test rewrites in upgrade_test.go and documentation update in repo-management.md) into a separate PR linked to a dedicated issue.

  • [stale-doc] docs/guides/infrastructure/infrastructure-reference.md:240 — The "Per-Repo Mode Secrets/Variables > GitHub" section lists FULLSEND_GCP_PROJECT_ID and FULLSEND_GCP_WIF_PROVIDER as unconditional "Target repo secrets" for all GitHub per-repo installations. With credential-mode-aware shim generation, OIDC-mode repos do not have these secrets.
    Remediation: Add a WIF-mode qualifier, e.g., "Target repo secrets (WIF mode only):", similar to the GitLab section's pattern.

  • [protected-path] .github/workflows/reusable-dispatch.yml — This PR modifies files under the .github/ protected path. The change is authorized by issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226 (item 3: change FULLSEND_GCP_PROJECT_ID to required: false). Human approval is required for all protected-path changes regardless of context.

Low

  • [dry-run inconsistency] internal/repos/upgrade.go:222 — DryRun for SHA-pinned repos at the target version produces a false positive: replaceShimRef(content, targetRef, "", ...) replaces @<sha> # v2.3.0 with @v2.3.0 (changed=true), but non-DryRun resolves the tag back to the same SHA and correctly skips.

  • [API-shape-consistency] internal/scaffold/installfiles.go:49CollectPerRepoInstallFiles takes 4 positional parameters (bool + 3 strings), while its sibling CollectInstallFiles uses an options struct.

  • [growing-positional-parameter-list] internal/repos/remote_scaffold.go:37FetchRemoteScaffold now accepts 7 parameters including forge-specific ones (credentialMode for GitHub, runnerTags for GitLab).

  • [magic-string-vs-constant] internal/scaffold/render.go:120stripWIFSecrets compares against the string literal "oidc" rather than a named constant. The CredModeOIDC constant in the repos package cannot be imported due to circular dependency; consider a package-local constant.

  • [intent-alignment] internal/scaffold/render.go:124stripWIFSecrets uses exact string matching against indentation-specific patterns. Template indentation changes would silently break stripping (mitigated by existing tests).

  • [upgrade behavior change] internal/repos/upgrade.go:235 — Floating refs are no longer resolved to SHAs for non-SHA-pinned repos, reducing automatic supply-chain pinning on upgrade.


Labels: PR modifies scaffold/install code, dispatch workflow, and documentation


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

  • [API contract violation] internal/scaffold/render.go:126stripWIFSecrets removes the FULLSEND_GCP_PROJECT_ID secret line from the per-repo shim when credential mode is oidc. However, reusable-dispatch.yml declares this secret as required: true (line 65-66). When a repo is installed with credential_mode: oidc, the generated fullsend.yaml will call reusable-dispatch.yml without passing FULLSEND_GCP_PROJECT_ID, causing GitHub Actions to reject the workflow call with a validation error.
    Remediation: Either (1) change FULLSEND_GCP_PROJECT_ID to required: false in .github/workflows/reusable-dispatch.yml (item 3 from issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226), or (2) only strip FULLSEND_GCP_WIF_PROVIDER in OIDC mode while keeping FULLSEND_GCP_PROJECT_ID.

Medium

  • [parameter-threading-inconsistency] internal/repos/install.go:316BuildScaffoldFiles passes the raw cfg.CredentialMode to CollectPerRepoInstallFiles, but Install() resolves credential mode via resolveCredentialMode() earlier. When CredentialMode is empty (common default for GitHub repos without --inference-project), resolveCredentialMode returns "oidc", but BuildScaffoldFiles passes "" to the scaffold layer, so stripWIFSecrets is a no-op. The scaffold output is inconsistent with the resolved credential mode.
    Remediation: Pass the resolved credential mode to BuildScaffoldFiles.

  • [scope-gap] .github/workflows/reusable-dispatch.yml:66 — Issue repos: scaffold template WIF gating and mint-administration docs for credential modes #6226 lists three scaffold items: (1) conditionally pass WIF secrets, (2) skip setup-gcp when oidc, (3) change FULLSEND_GCP_PROJECT_ID from required: true. This PR implements only item 1 and says "Closes repos: scaffold template WIF gating and mint-administration docs for credential modes #6226". Item 3 directly causes the high-severity API contract violation above.
    Remediation: Implement item 3 in this PR to fix the contract violation, or change "Closes repos: scaffold template WIF gating and mint-administration docs for credential modes #6226" to a partial reference and file a follow-up.

  • [technical-documentation-accuracy] docs/guides/infrastructure/mint-administration.md:78 — The new documentation states "The CLI flag --credential-mode on repos install overrides both the manifest default and per-repo settings." No --credential-mode flag exists on repos install.
    Remediation: Remove the paragraph or implement the flag.

Low

  • [API-shape-consistency] internal/scaffold/installfiles.go:49CollectPerRepoInstallFiles now takes 4 positional parameters (bool + 3 strings), while its sibling CollectInstallFiles uses an options struct. Consider refactoring for consistency.

  • [fail-open-default] internal/scaffold/render.go:120stripWIFSecrets only strips when CredentialMode is exactly "oidc". Any other value (including empty string) leaves WIF secret references. Practical risk mitigated by upstream validation.

  • [intent-alignment] internal/scaffold/render.go:119stripWIFSecrets uses exact string matching against indentation-specific patterns. If template indentation changes, stripping silently stops working. Consistent with existing codebase patterns.

  • [growing-positional-parameter-list] internal/repos/remote_scaffold.go:37FetchRemoteScaffold now accepts 7 parameters including 4 consecutive strings, making call sites error-prone.


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.

@ggallen
ggallen force-pushed the agent/6226-scaffold-wif-gating branch from e3ac5c2 to e188658 Compare August 14, 2026 22:52
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 14, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:54 PM UTC · Completed 11:14 PM UTC

Commit: e188658 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added component/install CLI install and app setup component/dispatch Workflow dispatch and triggers go Pull requests that update go code labels Aug 14, 2026
@ggallen
ggallen force-pushed the agent/6226-scaffold-wif-gating branch from e188658 to b300bb1 Compare August 15, 2026 00:40
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 12:42 AM UTC · Completed 1:22 AM UTC

Commit: b300bb1 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

Signed-off-by: Greg Allen <gallen@redhat.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
@ggallen
ggallen force-pushed the agent/6226-scaffold-wif-gating branch from b300bb1 to 8ac981b Compare August 15, 2026 01:38
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:39 AM UTC · Completed 2:00 AM UTC

Commit: 8ac981b · 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.


Note: The following inline comments could not be posted on the diff (GitHub returned 422) and are included here instead:

  • .github/workflows/reusable-dispatch.yml (file-level): Line 626 · [medium] API contract violation

The setup-gcp composite action is called unconditionally in every stage job, and its action.yml declares gcp_wif_provider as required: true. For OIDC-mode repos, stripWIFSecrets removes FULLSEND_GCP_WIF_PROVIDER from the generated shim, so the secret reaches reusable-dispatch.yml as an empty string. google-github-actions/auth will fail with an empty workload_identity_provider, aborting the stage job. This is a known-deferred item (issue #6226 item 2) and is fail-closed, but the PR claims Closes #6226 while this item remains unaddressed.

Suggested fix: Either add an if condition to each setup-gcp step (e.g., if: secrets.FULLSEND_GCP_WIF_PROVIDER != ''), or change Closes #6226 to a partial reference and file a follow-up for the setup-gcp conditional.

  • .github/workflows/reusable-dispatch.yml:66: [medium] protected-path

This PR modifies a file under the .github/ protected path. The change is authorized by issue #6226 item 3 (change FULLSEND_GCP_PROJECT_ID to required: false). Human approval is required for all protected-path changes regardless of context.

  • .github/workflows/reusable-dispatch.yml:66: [low] secret-requirement-relaxation

FULLSEND_GCP_PROJECT_ID changed from required: true to required: false. Combined with FULLSEND_GCP_WIF_PROVIDER already being required: false, the reusable workflow now accepts calls with no GCP credentials. This is fail-closed in practice (setup-gcp still rejects empty values), but a YAML comment explaining why these secrets are optional would aid future maintainers.

  • internal/scaffold/installfiles.go:49: [low] API-shape-consistency

CollectPerRepoInstallFiles takes 4 positional parameters (bool + 3 strings), while its sibling CollectInstallFiles uses a CollectInstallFilesOptions struct. The new credentialMode parameter deepens this divergence.

  • internal/repos/remote_scaffold.go:34: [low] growing-positional-parameter-list

FetchRemoteScaffold now accepts 7 parameters including 4 consecutive strings, which is error-prone at call sites.

  • internal/scaffold/render.go:120: [low] magic-string-vs-constant

stripWIFSecrets compares against the string literal oidc rather than a named constant. The CredModeOIDC constant in the repos package cannot be imported due to circular dependency; consider a package-local constant.

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/install CLI install and app setup go Pull requests that update go code ready-for-review Agent PR ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

repos: scaffold template WIF gating and mint-administration docs for credential modes

1 participant