Skip to content

Commit eadef6d

Browse files
committed
fix(ci): authenticate tap push via org GitHub App, not a personal PAT
The homebrew-tap workflow pushes the generated formula to the separate TechMatrix-labs/homebrew-pythinker repo, which the default GITHUB_TOKEN cannot write to, so it used a personal PAT in HOMEBREW_TAP_TOKEN. That PAT broke on the org migration and again on re-issue (authenticated but lacked Contents: write -> 403 on push), freezing the tap at 0.25.0. Replace it with a short-lived token minted at runtime from an org-owned GitHub App (actions/create-github-app-token) that has Contents: Read and write on the tap repo. The App is owned by the org (survives member/org changes), its installation token expires in ~1h and is minted fresh each run, and owner/repositories scope it to only the tap repo. Deploy keys — the simpler robust option — are disabled org-wide, so the App is the frictionless path that needs no org policy change. Requires two secrets on this repo: HOMEBREW_TAP_APP_ID and HOMEBREW_TAP_APP_PRIVATE_KEY. The old HOMEBREW_TAP_TOKEN secret is no longer referenced and can be deleted.
1 parent 707817d commit eadef6d

1 file changed

Lines changed: 17 additions & 2 deletions

File tree

.github/workflows/homebrew-tap.yml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,29 @@ jobs:
7070
echo "--- generated formula head ---"
7171
head -40 out/Formula/pythinker-code.rb
7272
73+
# Mint a short-lived installation token for an org-owned GitHub App that
74+
# has Contents: Read and write on the tap repo. This replaces a personal
75+
# PAT: the App is owned by the org (survives member/org changes), its token
76+
# expires in ~1h (minted fresh each run), and it is scoped to only the tap
77+
# repo. owner/repositories are required because the App must reach a repo
78+
# other than the one this workflow runs in.
79+
- name: Mint GitHub App token for the tap repo
80+
id: app-token
81+
uses: actions/create-github-app-token@v2
82+
with:
83+
app-id: ${{ secrets.HOMEBREW_TAP_APP_ID }}
84+
private-key: ${{ secrets.HOMEBREW_TAP_APP_PRIVATE_KEY }}
85+
owner: ${{ env.TAP_OWNER }}
86+
repositories: ${{ env.TAP_REPO }}
87+
7388
- name: Sync formula into tap repo (handles empty repo on first run)
7489
env:
7590
PKG_VERSION: ${{ steps.ver.outputs.version }}
76-
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
91+
TAP_TOKEN: ${{ steps.app-token.outputs.token }}
7792
run: |
7893
set -euxo pipefail
7994
if [ -z "${TAP_TOKEN:-}" ]; then
80-
echo "::error::HOMEBREW_TAP_TOKEN secret is empty or unset — cannot push to ${TAP_OWNER}/${TAP_REPO}. Add a fine-grained PAT (Contents: Read and write on the tap repo) as the HOMEBREW_TAP_TOKEN secret, then re-run this workflow." >&2
95+
echo "::error::No tap token available — the GitHub App token mint produced an empty value. Confirm the HOMEBREW_TAP_APP_ID and HOMEBREW_TAP_APP_PRIVATE_KEY secrets are set and the App is installed on ${TAP_OWNER}/${TAP_REPO} with Contents: Read and write, then re-run this workflow." >&2
8196
exit 1
8297
fi
8398
# We do NOT use actions/checkout for the tap repo because on the

0 commit comments

Comments
 (0)