From 2f15d24f90314ef1b9566915a13a79d3a38f30d9 Mon Sep 17 00:00:00 2001 From: Marcin Beczynski Date: Fri, 23 Oct 2026 15:19:58 +0200 Subject: [PATCH] ci: cask do tapu przez GitHub App renacode-tap zamiast PAT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HOMEBREW_TAP_TOKEN był fine-grained PAT-em właściciela, wygasającym 7.10.2027. Teraz job robi godzinny token aplikacji `renacode-tap` (Contents: write) ograniczony do RenaCode/homebrew-tap (actions/create-github-app-token v3.2.0, przypięta po SHA). Sekret TAP_APP_KEY + zmienna TAP_APP_CLIENT_ID. Token i próba `git ls-remote` tapu chodzą przy KAŻDYM przebiegu, więc utrata instalacji wychodzi na PR, a nie po opublikowaniu DMG. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 32 ++++++++++++++++++++++++++------ packaging/README.md | 16 +++++++++++----- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df6bc49..6283bb6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,9 @@ name: Release # Without it the release does NOT build: an ad-hoc signature changes the # app's identity on every release and macOS revokes Full Disk Access after # every upgrade. -# HOMEBREW_TAP_TOKEN - token with write access to RenaCode/homebrew-tap. +# TAP_APP_KEY (secret) + TAP_APP_CLIENT_ID (variable) - GitHub App `renacode-tap` +# (Contents: write, installed on RenaCode/homebrew-tap). The job mints a +# 1-hour token scoped to that one repository; nothing expires. # # Still no Developer ID and no notarization - Gatekeeper does not know the # publisher; the cask removes the quarantine attribute in postflight. @@ -277,23 +279,41 @@ jobs: mac-app/build/*.sha256 generate_release_notes: true - - name: Check tap token + - name: Check tap app if: env.IS_RELEASE == 'true' env: - TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + TAP_APP_CLIENT_ID: ${{ vars.TAP_APP_CLIENT_ID }} + TAP_APP_KEY: ${{ secrets.TAP_APP_KEY }} run: | - if [ -z "$TAP_TOKEN" ]; then - echo "::error::Release published, but HOMEBREW_TAP_TOKEN is missing - the cask in the tap was NOT updated. Contents to insert manually:" + if [ -z "$TAP_APP_CLIENT_ID" ] || [ -z "$TAP_APP_KEY" ]; then + echo "::error::Release published, but TAP_APP_CLIENT_ID/TAP_APP_KEY is missing - the cask in the tap was NOT updated. Contents to insert manually:" cat mac-app/build/cloudmachine.rb exit 1 fi + # Token and the access probe run on EVERY build, not only on release: + # an app that lost its installation (or a missing key) fails the PR, + # not the release after the DMG is already published. + - name: Tap token + id: tap-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.TAP_APP_CLIENT_ID }} + private-key: ${{ secrets.TAP_APP_KEY }} + owner: RenaCode + repositories: homebrew-tap + + - name: Check tap access + env: + TAP_TOKEN: ${{ steps.tap-token.outputs.token }} + run: git ls-remote "https://x-access-token:${TAP_TOKEN}@github.com/RenaCode/homebrew-tap.git" HEAD + - name: Check out tap if: env.IS_RELEASE == 'true' uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: repository: RenaCode/homebrew-tap - token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + token: ${{ steps.tap-token.outputs.token }} path: homebrew-tap - name: Push cask to tap diff --git a/packaging/README.md b/packaging/README.md index 36ce741..83b937a 100644 --- a/packaging/README.md +++ b/packaging/README.md @@ -80,15 +80,21 @@ rm -rf "$work" Do this **once**. A new certificate is a new identity: every Mac would have to grant Full Disk Access again. -### 2. The tap repository and `HOMEBREW_TAP_TOKEN` +### 2. The tap repository and the `renacode-tap` app 1. Create the public repository `RenaCode/homebrew-tap` (empty is fine; the workflow creates `Casks/`). -2. Create a fine-grained token limited to that repository with - *Contents: Read and write*, and store it: - `gh secret set HOMEBREW_TAP_TOKEN -R RenaCode/CloudMachine` +2. Create a GitHub App in the RenaCode organization (no webhook, Repository + permissions *Contents: Read and write*), install it on `homebrew-tap` only, + and store its client ID and private key: + `gh variable set TAP_APP_CLIENT_ID -R RenaCode/CloudMachine --body ` + `gh secret set TAP_APP_KEY -R RenaCode/CloudMachine < key.pem` -Without the token the release is still published, but the job fails and + The release job turns the key into a one-hour token for `homebrew-tap` + (`actions/create-github-app-token`), so there is no token to renew. The app + is `renacode-tap` (since 2026-10-23; before that a fine-grained PAT). + +Without the app the release is still published, but the job fails and prints the cask, so a missing tap update cannot go unnoticed. ## What the cask does and deliberately does not do