Per-repository setup¶
A repository admin does this when a repository adopts the process. Set
REPO=Algorithmiq/<name> and APP_ID=<the App ID> first. Steps 1 to 7 come before
merging the PR that adds the stubs, 8 after it.
- Install the app and share the credentials with the repository: add it to the app's
installation (Organisation settings → GitHub Apps → algorithmiq-release-bot →
Configure), and to the
--reposlists of the variable and the secret. - Tag
v0.0.0, the start of the changelog, on the last commit that should not appear in it, before the tag ruleset (step 5) exists. The first release is then0.1.0(or1.0.0when started with major).
git tag -a v0.0.0 -m "Start of the changelog" <commit>
git push origin v0.0.0
just changelog # what the first release will contain
- Settings. Squash-merge only, with the PR title and description as the commit
message (otherwise the migration notes never reach the changelog), auto-merge, automatic
deletion of head branches, immutable releases, and the
backportlabel:
gh api -X PATCH repos/$REPO -F allow_squash_merge=true -F allow_merge_commit=false \
-F allow_rebase_merge=false -F allow_auto_merge=true -F delete_branch_on_merge=true \
-f squash_merge_commit_title=PR_TITLE -f squash_merge_commit_message=PR_BODY
gh api -X PUT repos/$REPO/immutable-releases
gh label create backport -R $REPO --color B60205 \
--description "Also ship this fix in the current release"
- Files. The three stubs, the publish workflow, the
justfilerecipe, the Migration section of the PR template, an emptyCHANGELOG.mdif there is none, and the Dependabot group. Delete any local copies ofrelease.pyandcliff.toml. - Tag ruleset: only the release bot creates, moves or deletes
v*tags.
gh api -X POST repos/$REPO/rulesets --input - <<EOF
{"name": "release tags", "target": "tag", "enforcement": "active",
"conditions": {"ref_name": {"include": ["refs/tags/v*"], "exclude": []}},
"rules": [{"type": "creation"}, {"type": "update"}, {"type": "deletion"}],
"bypass_actors": [{"actor_id": $APP_ID, "actor_type": "Integration", "bypass_mode": "always"}]}
EOF
release/*ruleset: changes only through squash-merged PRs that pass CI, and only the bot creates the branches. List the repository's own required checks next to the PR checks (15368is the GitHub Actions app, which reports them):
gh api -X POST repos/$REPO/rulesets --input - <<EOF
{"name": "release branches", "target": "branch", "enforcement": "active",
"conditions": {"ref_name": {"include": ["refs/heads/release/*"], "exclude": []}},
"rules": [
{"type": "creation"}, {"type": "deletion"}, {"type": "non_fast_forward"}, {"type": "required_linear_history"},
{"type": "pull_request", "parameters": {"allowed_merge_methods": ["squash"], "dismiss_stale_reviews_on_push": true,
"require_code_owner_review": false, "require_last_push_approval": false,
"required_approving_review_count": 1, "required_review_thread_resolution": false}},
{"type": "required_status_checks", "parameters": {"strict_required_status_checks_policy": true,
"required_status_checks": [{"context": "PR checks / PR title", "integration_id": 15368},
{"context": "PR checks / Migration notes", "integration_id": 15368}]}}],
"bypass_actors": [{"actor_id": $APP_ID, "actor_type": "Integration", "bypass_mode": "always"}]}
EOF
- PyPI Trusted Publishing for the publish workflow: on PyPI, add a (pending)
publisher with the repository, the workflow file name and the environment (
pypiin the example). The first pre-release is published to PyPI straight away. - After the stubs are on
main: require the PR checks onmainand let the bot bypass themainruleset.pull_request_targetruns the workflow file of the base branch, so the checks never report until the stub is onmain; requiring them earlier blocks every PR, including the one adding them. The bypass lets the bot merge its changelog PR at once, without a review or the PR checks (see Caveats); without it, each changelog PR waits for a maintainer's approval.
id=$(gh api repos/$REPO/rulesets --jq '.[] | select(.name == "main") | .id')
gh api repos/$REPO/rulesets/$id | jq --argjson app "$APP_ID" '{name, target, enforcement, conditions,
rules: (.rules | map(if .type == "required_status_checks"
then .parameters.required_status_checks += [{"context": "PR checks / PR title", "integration_id": 15368},
{"context": "PR checks / Migration notes", "integration_id": 15368}]
else . end)),
bypass_actors: (.bypass_actors + [{"actor_id": $app, "actor_type": "Integration", "bypass_mode": "always"}])}' \
| gh api -X PUT repos/$REPO/rulesets/$id --input -
This assumes a main ruleset with a required_status_checks rule; create one first
if there is none.
The first release: check that no deprecation is due for removal, then
Actions → Release → Run workflow on main (or
gh workflow run release.yml -R $REPO --ref main -f bump=minor -f stage=rc).