Repository navigation
feat(release): release on merge to stable, App credentials from the release environment - #12
Closed
pjhartlieb wants to merge 4 commits into
Closed
pjhartlieb wants to merge 4 commits into
pjhartlieb wants to merge 4 commits into
Conversation
Implements the approved publishing policy (.github-config#17) in the shared workflows: - release-check runs on the trunk only, reads the version from the merged manifest, and reports release=false when that version already has a GitHub release, so a push that keeps the version publishes nothing. The new manifest.py release command refuses prerelease versions, a Helm chart that disagrees with the manifest, and git dependencies, so stable pins released versions. - publish creates the tag and the GitHub release last, on the released commit, with a token from the release GitHub App, the only actor the release-tags-create ruleset lets create a v* tag. - crates, docker and publish run in the release environment, which a bbot-leads member approves. crates.io uses trusted publishing and skips a version already published. docker drops the dev tag; dev images go to GHCR. - The tag subcommand and release.sh, which cut tags by hand, are gone.
…release check Builds on 4894c4a (CLA#11). Addresses the review on CLA#11: - H1: the release App ID and key are read from the caller repository's release environment (vars.BLS_RELEASE_APP_ID, secrets.BLS_RELEASE_PRIVATE_KEY). Callers pass nothing; the release-app-id input and release-app-key secret are removed, so the key can't end up at repo or org level. docker.yml reads DOCKER_* the same way, so callers don't need secrets: inherit. - Fail fast: a preflight job in release-check.yml, inside the release environment, checks the variable and secret exist and mints a token (proving the key and the installation) before anything publishes. - M1: the manifest version must be exactly MAJOR.MINOR.PATCH (rejects 1.2, 1.2.3.4, .postN, epochs, +local, leading zeros, a v prefix). - M2: "already released" is the immutable v<version> tag, not the GitHub release. A version already on PyPI or crates.io without its tag fails the check instead of tagging a commit the artifacts weren't built from. - M3: releases run from refs/heads/stable explicitly, not "the default branch". Refs blacklanternsecurity/.github-config#16 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A job that names a missing environment makes GitHub create it with no protection. The sandbox's first publish run did exactly that (preflight failed closed and nothing was published, but release was created with no reviewers, no branch policy and admin bypass on). The check job now reads the release environment from outside it, before any job enters it, and fails unless: it exists; admins can't bypass it; deployments are limited to the stable branch only; and, on public repositories, it has required reviewers with prevent self-review (private repositories on Team can't have environment reviewers). Anything unreadable fails the run. The check job needs actions: read; callers must grant it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before a release, check each commit since the last v* tag against the pull requests it belongs to (GitHub's commit-to-PR association), not its author, which anyone can set. A commit that didn't arrive through a merged pull request may touch only the paths in the new direct-push-paths input (cloudcheck: its two daily data files); with no paths given, the default, any such commit fails the release. A first release (no earlier v* tag) is skipped with a notice. This is the release-time backstop the publishing policy requires for the cloudcheck-update-bot exception, and a free extra control everywhere else, where direct pushes to stable shouldn't exist at all. The check job needs pull-requests: read; callers must grant it. Verified: 8 new tests (32 in total); 3 mutations of the rule each fail them; on cloudcheck's real v11.1.0..v11.2.0 history (158 commits: 112 bot data pushes, 46 through PRs) it passes with the data paths allowed, and on stable it fails with none allowed. Refs blacklanternsecurity/.github-config#16, blacklanternsecurity/bbot-enterprise#406 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
singlerider
pushed a commit
that referenced
this pull request
Oct 26, 2026
A job that names a missing environment makes GitHub create it with no protection. Before any job enters release, the check job now reads the environment and fails unless it exists, admins can't bypass it, deployments are limited to stable only, and, on public repositories, it has required reviewers with prevent self-review. Anything unreadable fails the run. The check job needs actions: read; callers must grant it. From pjhartlieb's 6fc2cf5 on #12.
singlerider
pushed a commit
that referenced
this pull request
Oct 26, 2026
Before a release, check each commit since the last v* tag against the pull requests it belongs to (GitHub's commit-to-PR association), not its author, which anyone can set. A commit that didn't arrive through a merged pull request may touch only the paths in the new direct-push-paths input (cloudcheck: its daily data files). With no paths given, the default, any such commit fails the release. A first release (no earlier v* tag) is skipped with a notice. This is the release-time backstop the publishing policy requires for the cloudcheck-update-bot exception. The check job needs pull-requests: read; callers must grant it. From pjhartlieb's 31d7219 on #12. Refs blacklanternsecurity/.github-config#16, blacklanternsecurity/bbot-enterprise#406
Contributor
Author
|
Superseded by #11, which carries this code with F1 fixed at |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #11. It keeps @singlerider's commit (
4894c4a) and adds the fixes from the review on #11 inf6816f0and6fc2cf5.Draft until the release flow has been rehearsed end to end in
blacklanternsecurity/release-sandboxagainst this commit, and until the policy amendment (.github-config#17, v1.2.0) is merged.What changes on top of #11
releaseenvironment (vars.BLS_RELEASE_APP_ID,secrets.BLS_RELEASE_PRIVATE_KEY). Callers pass nothing, and therelease-app-idinput andrelease-app-keysecret are removed, so the key can't end up at repository or organization level.docker.ymlreadsDOCKER_USERNAME/DOCKER_TOKENthe same way, so callers never needsecrets: inherit.6fc2cf5). A job that names a missing environment makes GitHub create it with no protection, and the sandbox's first run did exactly that. Thecheckjob now readsreleasefrom outside it, before any job enters it, and fails unless it exists, admins can't bypass it, deployments are limited tostableonly, and (on public repositories) it has required reviewers with prevent self-review. Anything unreadable fails the run. The check job needsactions: read, so callers must grant it. Verified live inrelease-sandbox: an unprotected environment stopped the run atcheck, and the environment wasn't touched.preflightjob inrelease-check.ymlruns insiderelease. It checks that the variable and secret exist and mints a token, which proves the key and the App installation, before anything is built or published.MAJOR.MINOR.PATCH.1.2,1.2.3.4,.postN, epochs,+local, leading zeros and avprefix are refused, each with a test.v<version>tag. A version already on PyPI or crates.io without its tag fails the check, rather than tagging a commit the artifacts weren't built from (an interrupted release, or a version from before this workflow).stableonly (M3).refs/heads/stableexplicitly, not "the default branch".Callers
Callers drop the App lines, pass no secrets, and grant
actions: readto the job that callsrelease-check.yml. Apublish.ymljob that callspublish.ymlneeds onlyversion,tagand the optional inputs.Validation
uv run --group dev pytest -q tests: 24 passed (adds 7 version cases and a crate-name test)actionlintok,ruff checkok, zizmor: no findingsBefore this merges
release-sandbox(a release with approval by someone other than the merger, a tag created by the App, tags that can't be moved, an unbumped push that releases nothing, a non-stablerun refused, a re-run after a failure).maingets required status checks and a CODEOWNERS file requiringbbot-leads: every release in the org runs this code.Refs blacklanternsecurity/.github-config#16