diff --git a/.github/workflows/check-plcc-release.yml b/.github/workflows/check-plcc-release.yml index b0476a2..84f6a00 100644 --- a/.github/workflows/check-plcc-release.yml +++ b/.github/workflows/check-plcc-release.yml @@ -7,22 +7,32 @@ on: - cron: '0 9 * * 1' # Every Monday at 09:00 UTC workflow_dispatch: # Allow manual trigger for testing -permissions: - contents: write - pull-requests: write +# Nothing here uses GITHUB_TOKEN any more: checkout and gh both authenticate +# with the App installation token, so GITHUB_TOKEN needs no scopes at all. +permissions: {} jobs: check: runs-on: ubuntu-latest + permissions: {} steps: + # Minted per run and short-lived, so it cannot go stale between the + # weekly runs this workflow depends on. + - name: Generate release bot token + id: app-token + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Checkout uses: actions/checkout@v4 with: - token: ${{ secrets.RELEASE_TOKEN }} + token: ${{ steps.app-token.outputs.token }} - name: Check for new PLCC release env: - GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | # Fetch the latest PLCC release tag LATEST=$(gh api repos/ourPLCC/plcc/releases/latest --jq '.tag_name') diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8042d55..9e9a894 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,21 +6,33 @@ on: push: branches: [main] -permissions: - contents: write - packages: write - pull-requests: write - issues: write +# These grants applied to GITHUB_TOKEN, which semantic-release no longer +# uses — it authenticates with the App token now. The only remaining use of +# GITHUB_TOKEN is the GHCR login, so packages:write is all that is needed. +permissions: {} jobs: release: runs-on: ubuntu-latest + permissions: + packages: write steps: + # A static PAT rots silently: this one expired and only surfaced months + # later, failing checkout on the next push to main. App installation + # tokens are minted per run and expire in about an hour, so there is + # nothing long-lived to go stale. + - name: Generate release bot token + id: app-token + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - token: ${{ secrets.RELEASE_TOKEN }} + token: ${{ steps.app-token.outputs.token }} - name: Log in to GHCR uses: docker/login-action@v3 @@ -40,7 +52,7 @@ jobs: id: semantic uses: cycjimmy/semantic-release-action@v4 env: - GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - name: Read PLCC version if: steps.semantic.outputs.new_release_published == 'true' diff --git a/README.md b/README.md index 9586f00..c2aea00 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,17 @@ Commands available: `plccmk`, `scan`, `parse`, `rep` Use [Conventional Commits](https://www.conventionalcommits.org/) in your commit messages: - `fix:` — patch release - `feat:` — minor release -- `feat!:` or `BREAKING CHANGE:` — major release +- `feat:` **with a `BREAKING CHANGE:` footer** — major release +- `ci:`, `docs:`, `chore:`, `test:`, `refactor:` — no release + +Write the footer, not `feat!:`. semantic-release uses the Angular preset, +which does not parse the `!` shorthand: a `feat!:` subject with no footer +matches nothing and produces **no release at all**, which is easy to miss +because the pipeline still goes green. The `BREAKING CHANGE:` footer is what +actually triggers a major. + +If you squash-merge a major, make sure the footer survives into the squash +commit body — with it dropped, the release silently downgrades to a minor. ### Automated PLCC updates @@ -58,4 +68,11 @@ A weekly workflow checks for new PLCC releases and opens a PR automatically. If ### Release setup -The release workflow requires a `RELEASE_TOKEN` repository secret with a GitHub App token (preferred) or a classic PAT. Classic PAT scopes required: `repo`, `write:packages`. This is required because `GITHUB_TOKEN` cannot push to the protected `main` branch. +The release and PLCC-update workflows need two repository secrets, `APP_ID` and `APP_PRIVATE_KEY`, belonging to the **ourPLCC Release Bot** GitHub App. Each run mints its own short-lived installation token. This is required because `GITHUB_TOKEN` cannot push to the protected `main` branch. + +The App also needs, per repository: +- Contents and Pull requests set to **Read and write** +- Installation access granted to this repository — org-level installation does not cover new repos automatically +- A place in the `main` ruleset's bypass list, which only becomes selectable once repository access is granted + +An earlier setup used a static `RELEASE_TOKEN` PAT. That is no longer referenced by any workflow and can be deleted. It is worth knowing why it was replaced: a PAT gives no expiry signal, so when it lapsed nothing surfaced until the next push to main failed at checkout, months later.