|
| 1 | +--- |
| 2 | +title: "Release automation failed due to GitHub App token contract mismatch" |
| 3 | +module: "Release CI/CD" |
| 4 | +problem_type: "integration_issue" |
| 5 | +component: "cinzel/steps.hcl, .github/workflows/release.yaml, .github/workflows/release-published.yaml, .goreleaser.yaml" |
| 6 | +severity: "high" |
| 7 | +root_cause: "different release actions and GoReleaser expected the GitHub App token under different input and environment names" |
| 8 | +resolution_type: "workflow_improvement" |
| 9 | +symptoms: |
| 10 | + - "Manual release tagging failed with token/auth errors despite a generated GitHub App token" |
| 11 | + - "Published release packaging did not trigger or failed to publish Homebrew updates" |
| 12 | + - "GoReleaser failed templating homebrew token because HOMEBREW_TAP_GITHUB_TOKEN was missing" |
| 13 | +tags: |
| 14 | + - "github-actions" |
| 15 | + - "github-app" |
| 16 | + - "release" |
| 17 | + - "goreleaser" |
| 18 | + - "homebrew" |
| 19 | + - "auth" |
| 20 | +created_date: "2026-03-12" |
| 21 | +updated_date: "2026-03-12" |
| 22 | +--- |
| 23 | + |
| 24 | +## Problem |
| 25 | + |
| 26 | +Release automation broke after migrating from PAT-based auth to a GitHub App installation token. The token existed, but different actions and GoReleaser expected it under different names, so some steps silently ignored it while others failed explicitly. |
| 27 | + |
| 28 | +## Root Cause |
| 29 | + |
| 30 | +The release flow treated GitHub App auth as one generic token contract, but the actual integrations differed: |
| 31 | + |
| 32 | +- `mathieudutour/github-tag-action` requires `github_token` |
| 33 | +- `ncipollo/release-action` requires `token` |
| 34 | +- `orhun/git-cliff-action` expects `github_token` |
| 35 | +- GoReleaser Homebrew publishing still resolves from `{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}` |
| 36 | + |
| 37 | +Using the wrong name caused actions to ignore the installation token and fall back to defaults or fail with auth errors. |
| 38 | + |
| 39 | +## What Didn't Work |
| 40 | + |
| 41 | +**Attempted state:** pass the same input name to every action after moving to GitHub App auth. |
| 42 | + |
| 43 | +- **Why it failed:** action interfaces are not uniform; a valid token value is useless if the action reads a different input/env name. |
| 44 | + |
| 45 | +## Solution |
| 46 | + |
| 47 | +Create one GitHub App installation token per workflow run, then map it to the exact contract each tool expects. |
| 48 | + |
| 49 | +1. Validate release app secrets exist: |
| 50 | + - `RELEASE_APP_ID` |
| 51 | + - `RELEASE_PRIVATE_KEY` |
| 52 | + |
| 53 | +2. Mint installation token with `actions/create-github-app-token`. |
| 54 | + |
| 55 | +3. Pass that token to each step using the correct name: |
| 56 | + - `github-tag-action` -> `github_token` |
| 57 | + - `git-cliff-action` -> `github_token` |
| 58 | + - `release-action` -> `token` |
| 59 | + |
| 60 | +4. Export the same token for GoReleaser packaging as: |
| 61 | + - `GITHUB_TOKEN` |
| 62 | + - `HOMEBREW_TAP_GITHUB_TOKEN` |
| 63 | + |
| 64 | +5. Keep `.goreleaser.yaml` unchanged so `homebrew_casks.repository.token` continues resolving from `{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}`. |
| 65 | + |
| 66 | +## Verification |
| 67 | + |
| 68 | +Commands run while fixing and regenerating workflow output: |
| 69 | + |
| 70 | +- `mise run cinzel github parse --directory ./cinzel --output-directory ./.github/workflows` |
| 71 | +- `mise x actionlint@latest -- actionlint` |
| 72 | + |
| 73 | +Observed result: |
| 74 | + |
| 75 | +- Manual release workflow now uses the app token with action-specific input names. |
| 76 | +- Published release workflow now exports the app token under `HOMEBREW_TAP_GITHUB_TOKEN` for GoReleaser Homebrew publishing. |
| 77 | +- Generated workflow YAML is back in sync with `cinzel/*.hcl` source. |
| 78 | + |
| 79 | +## Prevention |
| 80 | + |
| 81 | +- Treat auth mapping as an explicit release contract, not a generic token assumption. |
| 82 | +- Keep a per-action auth table in release docs when multiple third-party actions are chained together. |
| 83 | +- Always review each action’s real input names during auth migrations or version bumps. |
| 84 | +- Prefer one token source per workflow run, then map it deliberately to each downstream interface. |
| 85 | +- Preserve `.goreleaser.yaml` env contracts unless there is a strong reason to change them in the same PR. |
| 86 | + |
| 87 | +## Related |
| 88 | + |
| 89 | +- `docs/solutions/integration-issues/workflow-release-integration-fix.md` |
| 90 | +- `docs/solutions/integration-issues/release-automation-deprecation-trigger-contract-cleanup.md` |
| 91 | +- `docs/solutions/integration-issues/git-cliff-action-offline-token-fix.md` |
| 92 | +- `docs/plans/2026-03-11-ci-git-cliff-manual-release-automation-plan.md` |
| 93 | +- `docs/release/homebrew.md` |
0 commit comments