Skip to content

fix(ci): authenticate to Vault directly for the contentful-docs sync - #488

Merged
Tim Beyer (TimBeyer) merged 1 commit into
mainfrom
fix/pin-vault-action-to-v1-6-0
Sep 11, 2026
Merged

Tim Beyer (TimBeyer) merged 1 commit into
mainfrom
fix/pin-vault-action-to-v1-6-0

Conversation

@TimBeyer

@TimBeyer Tim Beyer (TimBeyer) commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

The manual sync run failed with:

Error: Unable to resolve action `contentful/vault-github-actions`, not found

The pin was fine. The constraint is that GitHub will not resolve an action from an internal repository for a public one, and contentful/optimization is public while contentful/vault-github-actions is internal. The error names the repository rather than the ref, and the previously pinned commit is still reachable.

Supporting evidence: all twelve other consumers of that action in the organization are INTERNAL or PRIVATE. There is no public consumer, so this was never a supported path.

This calls hashicorp/vault-action directly instead, exactly as publish-npm.yaml in this repository already does.

The trust boundary does not change

Same target-owned grant, same one-hour contentful-cross-repository App token scoped to contentful-docs by immutable repository ID (contents: write, pull_requests: write). Only the caller differs — the role names, secret path, and bot identity are copied from the preset.

Derived and verified against the merged cf-vault constructs:

Event Vault role
release (published, tag) optimization-github-action-cross-repository-write-contentful-docs-release
workflow_dispatch (default branch) optimization-github-action-cross-repository-write-contentful-docs-workflow-dispatch

Secret: github-cross-repository/token/optimization-cross-repository-write-contentful-docs on mount github-actions.

The role step rejects an ineligible run itself rather than letting Vault refuse the login, because a dispatch from a feature branch falls outside the role's bound subject and would otherwise surface as an opaque permission error.

Trade-off

The event-to-role mapping now lives in this repository and must follow contentful/vault-github-actions if that naming changes. Recorded in ADR 0002, along with the alternative — making that repository public — which is not ours to decide.

Validation

Simulated the role-selection step against every event shape:

Run Result
release published (tag) …-release
dispatch from main …-workflow-dispatch
dispatch from a feature branch rejected
release created, not published rejected
push to main rejected

Both role names and the secret path match the merged Terraform character for character.

  • workflow YAML parses; every remaining action SHA-pinned with a # vX.Y.Z comment Renovate can read
  • all run blocks pass bash -n; no ${{ }} inside any run:
  • pnpm fern:check, Prettier clean
  • No contentful-owned actions remain in the workflow

The Vault login itself is still unrun — it cannot be exercised from a branch, since both triggers read the workflow from the default branch.

🤖 Generated with Claude Code

@bito-code-review

bito-code-review Bot commented Sep 11, 2026

Copy link
Copy Markdown

Code Review Agent Run #0f7b25

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: c930f3d..c930f3d
    • .github/workflows/sync-fern-docs.yaml
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Sep 11, 2026

Copy link
Copy Markdown

Impact Analysis by Bito

Cross-Repository Impact Analysis
What Changed Impact of Change Suggested Review Actions
The workflow no longer invokes the shared contentful/vault-github-actions action and instead derives Vault role and secret-path names locally before calling hashicorp/vault-action. - ⚠️ unverified — No indexed cross-repository consumers found for contentful/vault-github-actions/action in the searched repositories.: The removed action reference is a workflow dependency. Other workflows using the same action could require coordinated changes, but the organization search returned no matches for the searched repositories.
- ⚠️ unverified — No indexed cross-repository consumers found for template-preset: cross-repository-write.: The preset input is removed and replaced by explicit role, method, path, and secrets inputs. The organization search returned no matches for the searched repositories.
- ⚠️ unverified — No indexed cross-repository consumers found for target-repository:.: The target repository is now incorporated into locally generated role and secret names. The organization search returned no matches for the searched repositories.
- Confirm the locally generated role names exactly match the roles provisioned by cf-vault for both release and workflow_dispatch events.
- Run a repository-wide search, including repositories not present in the MCP index, for the removed shared-action inputs before merging.
- Execute one published-release run and one workflow_dispatch run from main against a test or controlled documentation change.
The documentation sync now depends directly on Vault's JWT authentication action and on the target repository's existing cross-repository grant. - ⚠️ unverified — contentful-docs/.contentful/vault-secrets.yaml:1 or equivalent target grant CONSUMES optimization's generated cross-repository role and secret contract.: The workflow still relies on contentful-docs owning the grant through cross-repository-write.sources, as documented by the diff. The exact consumer file was not available through the repository index, so this relationship is not call-site verified.
- ⚠️ unverified — cf-vault role and policy provisioning CONSUMES the exact role suffixes release and workflow-dispatch.: The workflow constructs role names dynamically and states that they must match cf-vault-created roles. A naming mismatch will fail authentication before the token is issued.
- ⚠️ unverified — vault-github-actions preset conventions CONSUME the same role and secret naming contract indirectly.: The workflow explicitly duplicates the preset's mapping. Any future change to the preset or its infrastructure naming can silently break this workflow because there is no shared implementation.
- Add a CI validation or documented contract test that compares generated role and secret paths with cf-vault provisioning.
- Pin and validate the expected output name GITHUB_TOKEN from hashicorp/vault-action.
- Verify the Vault policy grants only contents:write and pull_requests:write for contentful-docs.
Code Paths Analyzed

Impact:
The workflow changes only the authentication and token-acquisition implementation. Documentation generation, ref resolution, target checkout, branch reuse, force-push, and pull-request upsert remain unchanged. The main operational risk is drift between locally duplicated Vault naming logic and the infrastructure that provisions those names.

Flow:
Release or workflow_dispatch trigger → checkout optimization → resolve export ref → fern:check → build Fern bundle → select event-specific Vault role and secret path → authenticate to Vault with OIDC/JWT → retrieve scoped GitHub App token → configure bot identity → checkout contentful-docs → apply generated bundle → push sync branch and create or update pull request.

Direct Changes (Diff Files):
• .github/workflows/sync-fern-docs.yaml [5-25] — Documents the direct Vault integration, scoped GitHub App token, duplicated role mapping, and relationship to the internal shared action.
• .github/workflows/sync-fern-docs.yaml [35-97] — Replaces the shared Vault preset authentication step with explicit Vault role selection, direct hashicorp/vault-action usage, and explicit bot identity configuration.
• .github/workflows/sync-fern-docs.yaml [110-153] — Adds event validation and dynamic construction of the release/workflow-dispatch Vault role and secret path, then retrieves GITHUB_TOKEN.
• .github/workflows/sync-fern-docs.yaml [196-203] — Updates the comment describing where the global Git identity is configured; push and pull-request behavior is otherwise unchanged.
• docs/ADRs/0002-generate-the-public-documentation-site-content-from-this-repository.md [47-58] — Updates the accepted decision to describe direct Vault authentication and the event-dependent role model.
• docs/ADRs/0002-generate-the-public-documentation-site-content-from-this-repository.md [92-105] — Adds the rejected shared-preset alternative and records the maintenance cost of duplicating event-to-role mapping.

Repository Impact:
GitHub Actions authentication: The workflow now depends directly on hashicorp/vault-action and the runner's OIDC token instead of the internal preset action.
Cross-repository documentation delivery: The same contentful-docs checkout, branch push, and pull-request flow now receives its token through explicitly constructed Vault paths.
Release and manual-dispatch behavior: Release runs require a published tag event; manual runs require execution from the repository default branch, while the ref input can select another tree for export.
Operational documentation: The ADR now records the direct-authentication design and its dependency on naming consistency with external infrastructure.

Cross-Repository Dependencies:
contentful-docs: Receives the generated branch and pull request and owns the target-side cross-repository grant.
cf-vault: Must provision the event-specific Vault roles and secret path consumed by the new shell mapping.
vault-github-actions: The workflow intentionally duplicates the preset's role and secret naming rather than invoking the preset.
HashiCorp Vault action: The workflow adds a direct pinned dependency on hashicorp/vault-action v4.0.0 for JWT authentication and secret retrieval.

Database/Caching Impact:
• None

API Contract Violations:
• The generated Vault role and secret-path strings are operational contracts. If their spelling differs from cf-vault provisioning, both release and manual publication fail before contentful-docs is checked out.
• The workflow assumes hashicorp/vault-action v4.0.0 emits the configured Vault field as the GITHUB_TOKEN output.
• The direct implementation removes the shared preset abstraction, so future changes to preset naming or policy behavior will not automatically propagate here.

Infrastructure Dependencies:
• GitHub Actions OIDC permission remains required through id-token: write.
• The VAULT_URL repository secret must remain available.
• Vault must expose the github-actions authentication mount and the generated event-specific roles.
• The target-side cross-repository grant must allow this repository to obtain the contentful-cross-repository App token.
• The contentful-cross-repository GitHub App must remain installed on contentful-docs with contents:write and pull_requests:write.
• The pinned hashicorp/vault-action commit must remain available and compatible with the supplied role, method, path, and secrets inputs.

Additional Insights:
Security: The change preserves short-lived token issuance and target scoping, but moves role and secret selection into shell code. The explicit set -euo pipefail and rejection of unsupported event/ref combinations reduce accidental issuance.
Maintainability: The ADR correctly records that role naming is duplicated and must be kept in sync with external infrastructure; this is now a manual compatibility boundary.
Validation coverage: git diff --check passed locally. YAML parsing could not be completed because the local yaml Node module was unavailable, and actionlint was not available in the environment.

Testing Recommendations

Frontend Impact:
None.

Service Integration:
• Run the workflow from a published release event and verify Vault authentication, contentful-docs checkout, branch push, and pull-request creation.
• Run workflow_dispatch from main with no ref and with a non-main ref input; verify both authenticate successfully and export the requested tree.
• Run workflow_dispatch from a feature branch and verify the Select the Vault role for this event step fails before Vault authentication or any repository write.
• Trigger repeated release events and confirm concurrency plus the fixed sync/optimization-docs branch converge on one open pull request.

Data Serialization:
• Validate the generated GitHub Actions YAML with actionlint or an equivalent parser before merge.
• Check that the multiline Vault secrets expression resolves the exact secret path and maps its token field to the GITHUB_TOKEN output.
• Test role and secret-path construction for source and target repository names, release tags, and workflow_dispatch refs.

Privacy Compliance:
• Confirm Vault audit logs show only short-lived contentful-cross-repository tokens and that no token is written to GitHub outputs, logs, commit messages, or pull-request bodies.
• Verify the issued App token cannot write to repositories other than contentful-docs and cannot perform permissions beyond contents:write and pull_requests:write.

Backward Compatibility:
• Compare the generated role names and secret path with the previous cross-repository-write preset implementation and cf-vault provisioning for both suffixes: release and workflow-dispatch.
• Verify the existing release-triggered behavior remains unchanged after replacing the shared action.
• Verify the existing publish-npm workflow's direct hashicorp/vault-action convention remains compatible with the new workflow.

OAuth Functionality:
• None

Cross-Service Communication:
• Perform an end-to-end test from optimization to contentful-docs: retrieve the App token, call gh api for contentful-cross-repository[bot], checkout the target repository, push the sync branch, and create or edit the pull request.
• Test Vault authentication failure and confirm the workflow exits before checkout or push when the role name, secret path, or default-branch subject is invalid.

Reliability Testing:
• None

Additional Insights:
• Add a lightweight contract check that compares the workflow's role and secret naming templates with the authoritative cf-vault configuration, since the implementation intentionally duplicates the preset mapping.
• Install actionlint in CI or run it locally; the current environment could not validate workflow syntax because actionlint and the Node yaml package were unavailable.
• Review the pinned hashicorp/vault-action commit and confirm its documented v4 input/output behavior before enabling the workflow in production.

Analysis based on known dependency patterns and edges. Actual impact may vary.

@bito-code-review

Copy link
Copy Markdown

✅ Review Settings Overridden

Status: Overridden Successfully

Guidelines:

  • Accepted:

    • General : Review Posture, Repo Truth And Boundaries, Domain Invariants

    • Language : typescript= TypeScript Review Evidence

Note: Extra guidelines beyond 3 general purpose guidelines and 1 language specific guideline per language are not processed. Guidelines are fetched from the source branch.

bito-code-review[bot]
bito-code-review Bot previously approved these changes Sep 11, 2026
The manual sync failed with `Unable to resolve action
contentful/vault-github-actions, not found`. The pin was fine; the constraint is
that GitHub will not resolve an action from an internal repository for a public
one, and this repository is public. All twelve other consumers of that action in
the organization are internal or private, so this was never a supported path.

Calls `hashicorp/vault-action` directly instead, as publish-npm.yaml already
does. The role names, secret path, and bot identity are the preset's, so the
trust boundary is unchanged: the same target-owned grant, the same one-hour token
scoped to contentful-docs by immutable repository ID. Only the caller differs.

Derived and verified against the merged cf-vault constructs:

  release           optimization-github-action-cross-repository-write-contentful-docs-release
  workflow_dispatch optimization-github-action-cross-repository-write-contentful-docs-workflow-dispatch
  secret            github-cross-repository/token/optimization-cross-repository-write-contentful-docs

The role step rejects an ineligible run itself rather than letting Vault refuse
the login, because a dispatch from a feature branch is outside the role's bound
subject and would otherwise surface as an opaque permission error.

The cost, recorded in the ADR, is that the event-to-role mapping now lives here
and has to follow contentful/vault-github-actions if that naming changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Sep 11, 2026

Copy link
Copy Markdown

Code Review Agent Run #aacca4

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: e78ac3b..e78ac3b
    • .github/workflows/sync-fern-docs.yaml
    • docs/ADRs/0002-generate-the-public-documentation-site-content-from-this-repository.md
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

@TimBeyer Tim Beyer (TimBeyer) changed the title fix(ci): pin the Vault action to the v1.6.0 release tag fix(ci): authenticate to Vault directly for the contentful-docs sync Sep 11, 2026
@TimBeyer
Tim Beyer (TimBeyer) merged commit e0fa1a3 into main Sep 11, 2026
41 of 68 checks passed
@TimBeyer
Tim Beyer (TimBeyer) deleted the fix/pin-vault-action-to-v1-6-0 branch September 11, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants