Skip to content

fix: CI runs on release PRs without a close and reopen - #23

Merged
AlexeyShalaev merged 1 commit into
masterfrom
fix/12-release-pr-ci
Oct 16, 2026
Merged

AlexeyShalaev merged 1 commit into
masterfrom
fix/12-release-pr-ci

Conversation

@AlexeyShalaev

Copy link
Copy Markdown
Member

The problem

release-please.yml ran release-please-action@v5 without token:, so the release PR was opened and updated with GITHUB_TOKEN. GitHub creates the pull_request CI run for those events but holds it until it expires, so the required All checks passed never reports. Every release has needed someone to close and reopen the PR after its last update (idempotency-kit #38, clientwright #40/#43, deadline-budget #23). One held run in deadline-budget waited 30 days.

The decision

Release Please gets an installation token of an org-owned GitHub App, minted per run with actions/create-github-app-token@v3. Events from that token start CI the same way a person's would.

  • The token is scoped to contents: write and pull-requests: write, which are the permissions the job's GITHUB_TOKEN has today and releases with today. My plan comment also listed issues: write, but the current releases show it isn't needed, so I left it out.
  • One App serves every library: org variable RELEASE_PLEASE_APP_CLIENT_ID, org secret RELEASE_PLEASE_APP_PRIVATE_KEY. A variable rather than a secret for the client ID, because a client ID isn't secret and a variable can be tested in if:.
  • While the variable is unset, the workflow falls back to GITHUB_TOKEN and the run carries a ::warning:: that says what that means. A library created before the App exists still releases the way it does today and does not fail. If the variable is set and the key is wrong or missing, the token step fails loudly. It does not fall back.
  • setup_repo.py warns when a repository cannot see the variable or the secret, at org or repository level.
  • The template README has a "The release App" section with the one-time setup, and the generated workflow links to it.

Rejected:

  • A PAT. It belongs to a person, expires, acts as that person (release PRs and tags authored by the maintainer), and reaches every repository that person can.
  • Documenting the close and reopen, or approving the held run. That keeps the manual step, and it has to be repeated after every push to the release PR.

What I could and could not test

  • The App path end to end needs the App, which doesn't exist in the org yet (installed apps today: release-please (Google's hosted one, no key we hold), codecov, claude, and the two bedrock-python-assets-* apps). The first release PR after setup is the real check: its CI run should be started by the App's bot and get jobs.
  • The workflow: actionlint 1.7.12 is clean, except for two findings that come from its bundled action metadata, which predates client-id. actions/create-github-app-token's action.yml at v3 (= v3.3.0, 64ab729) declares client-id and marks app-id as deprecated ("Use 'client-id' instead"), so I used client-id and ignored those two findings.
  • setup_repo.py with a fake gh:
org var + org secret:  (no warning)
neither:               [WARN] Actions variable RELEASE_PLEASE_APP_CLIENT_ID is not visible to o/r: …
                       [WARN] Actions secret RELEASE_PLEASE_APP_PRIVATE_KEY is not visible to o/r: …
var set, the key saved under the client ID's name as a secret:
                       [WARN] Actions secret RELEASE_PLEASE_APP_PRIVATE_KEY is not visible to o/r: …

The four listing endpoints it reads (actions/{organization-,}{variables,secrets}) answer with the repo scope; I checked them read-only against this repository.

  • The generated gate on a render is unchanged and green.

What the maintainer has to set up

Once per org, as described in the README section: create the App (Contents and Pull requests read/write, no webhook), install it on all repositories, add variable RELEASE_PLEASE_APP_CLIENT_ID and secret RELEASE_PLEASE_APP_PRIVATE_KEY, both visible to all repositories.

Closes #12

Release Please opened and updated its PR with GITHUB_TOKEN. GitHub holds the
pull_request run such an event creates until it expires, so the required
All checks passed never reported, and every release needed someone to close
and reopen the PR after its last update.

The workflow now mints a token from an org-owned GitHub App
(actions/create-github-app-token, scoped to contents and pull-requests
write, the same permissions the job's GITHUB_TOKEN has) and hands it to
Release Please, so its PR events start CI like anyone else's. The App is
configured once per org: RELEASE_PLEASE_APP_CLIENT_ID as a variable,
RELEASE_PLEASE_APP_PRIVATE_KEY as a secret. Until then the workflow falls
back to GITHUB_TOKEN and warns, so a new library still releases the way it
does today. setup_repo.py warns when a repository cannot see the two names.

Closes #12
@AlexeyShalaev
AlexeyShalaev merged commit d9d0a6f into master Oct 16, 2026
3 checks passed
@AlexeyShalaev
AlexeyShalaev deleted the fix/12-release-pr-ci branch October 16, 2026 15:07
AlexeyShalaev added a commit to bedrock-python/touchmark that referenced this pull request Oct 18, 2026
With GITHUB_TOKEN the release PR's CI run is held and never starts, so it
needed a close and reopen before every merge. The token now comes from
the org's release App (bedrock-python/python-library-template#23), with
GITHUB_TOKEN and a warning as the fallback while the App's variable is
unset.
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.

CI never runs on Release Please PRs, so every release needs a manual close and reopen

1 participant