Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,25 @@ A library rendered before the answers file existed needs one written by hand fir
answers it was rendered with, `_src_path: gh:bedrock-python/python-library-template`,
and as `_commit` the template commit it was rendered from, or the closest one.

## The release App

Release Please opens and updates its release PR with a token of an org-owned GitHub App.
With the workflow's own `GITHUB_TOKEN`, the PR's CI run is held and never starts, the
required "All checks passed" never reports, and the PR can only be merged after someone
closes and reopens it. One App serves every library and is set up once:

1. Create a GitHub App owned by the org (org Settings → Developer settings → GitHub Apps):
webhook off, repository permissions **Contents: Read and write** and **Pull requests:
Read and write**, installable on this account only.
2. Install it on the org, for all repositories.
3. In org Settings → Secrets and variables → Actions, visible to all repositories:
variable `RELEASE_PLEASE_APP_CLIENT_ID` = the App's Client ID, secret
`RELEASE_PLEASE_APP_PRIVATE_KEY` = a private key generated for it.

Release PRs, tags and GitHub Releases are then authored by the App's bot account. While
the variable is unset, the workflow falls back to `GITHUB_TOKEN` and its run carries a
warning, and `setup_repo.py` warns for any repository that cannot see the two names.

## For AI Agents

See [`.claude/LIBRARY_CREATION.md`](.claude/LIBRARY_CREATION.md) for important rules:
Expand Down
18 changes: 18 additions & 0 deletions scripts/setup_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

RULESET_NAME = "master-rules"
REQUIRED_CHECK = "All checks passed"
RELEASE_APP_CLIENT_ID = "RELEASE_PLEASE_APP_CLIENT_ID" # organization variable
RELEASE_APP_PRIVATE_KEY = "RELEASE_PLEASE_APP_PRIVATE_KEY" # organization secret
MAX_TOPICS = 20


Expand Down Expand Up @@ -111,6 +113,22 @@ def has_workflow(name: str) -> bool:
gh(f"repos/{repo}/pages", "-X", "POST", payload={"build_type": "workflow"}, ok=(409,))
pages = gh(f"repos/{repo}/pages", ok=(404,))

if has_workflow("release-please.yml"):
# Without the App the release PR is opened with GITHUB_TOKEN and CI never
# runs on it; the workflow falls back and warns, this says so up front.
step("Release Please: the release App's client ID and private key reach the repository")
def visible(kind: str) -> set[str]:
names: set[str] = set()
for scope in (f"organization-{kind}", kind):
listed = gh(f"repos/{repo}/actions/{scope}?per_page=100", ok=(403, 404)) or {}
names.update(item["name"] for item in listed.get(kind, []))
return names

for kind, name in (("variables", RELEASE_APP_CLIENT_ID), ("secrets", RELEASE_APP_PRIVATE_KEY)):
if name not in visible(kind):
print(f" [WARN] Actions {kind[:-1]} {name} is not visible to {repo}: CI will not run on"
" release PRs until it is (template README, 'The release App')")

step("Actions: workflows get a read-only token unless they ask for more; Release Please may open PRs")
gh(f"repos/{repo}/actions/permissions/workflow", "-X", "PUT",
payload={"default_workflow_permissions": "read", "can_approve_pull_request_reviews": True})
Expand Down
24 changes: 24 additions & 0 deletions template/.github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,31 @@ jobs:
release-please:
runs-on: ubuntu-latest
steps:
# A pull request opened or updated with GITHUB_TOKEN does not get a CI run
# that ever starts, so the release PR could not pass its required check
# until a person closed and reopened it. A token of the org's release App
# starts CI like anyone else's push. Until the App is set up, GITHUB_TOKEN
# is used and the run says so. Setup, once per org:
# https://github.com/bedrock-python/python-library-template#the-release-app
- name: Token from the release App
id: app
if: vars.RELEASE_PLEASE_APP_CLIENT_ID != ''
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.RELEASE_PLEASE_APP_CLIENT_ID }}
private-key: ${{ secrets.RELEASE_PLEASE_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write

- name: No release App
if: steps.app.outcome == 'skipped'
run: >-
echo "::warning::RELEASE_PLEASE_APP_CLIENT_ID is not set, so the release PR
is opened with GITHUB_TOKEN and CI will not run on it: close and reopen it
right before merging."

- uses: googleapis/release-please-action@v5
with:
token: ${{ steps.app.outputs.token || github.token }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
2 changes: 2 additions & 0 deletions template/NEW_LIBRARY_CHECKLIST.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ This configures (idempotent, re-run any time):
private vulnerability reporting
- A `master` ruleset: pull requests only, no force-push or deletion, `All checks passed`
required (left out with a warning if CI has not reported yet — re-run after it has)
- A warning if the repository cannot see the release App's credentials: release PRs
would then get no CI (template README, "The release App")

## Step 4 — PyPI Trusted Publisher (manual)

Expand Down
Loading