release: 0.3.3 #10
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
| name: Release PR Version | |
| on: | |
| # `edited` is not one of the default pull_request types, but it is the point of this | |
| # workflow: retitling the release PR is how a maintainer picks an exact version. The | |
| # other types keep the consistency check attached to the PR as it evolves. | |
| pull_request: | |
| types: [opened, reopened, edited, synchronize] | |
| # Read-only by default; only the job that pushes the Release-As commit widens this. | |
| permissions: | |
| contents: read | |
| jobs: | |
| version-consistency: | |
| # Also the check name the release PR's footer tells maintainers to wait for. | |
| name: Release PR version | |
| # A release-shaped title against the release branch is the whole test here, with no clause | |
| # on who opened the pull request or what branch it is rendered onto. This job only reads, | |
| # and a pull request claiming to be a release is worth checking against the committed | |
| # version whoever opened it. Every other pull request skips this job, which GitHub reports | |
| # as neutral. | |
| # | |
| # Loose on purpose: the prefix, not the full semver pattern the script matches. A version | |
| # typo'd into the title still reaches the script and fails there, rather than falling out | |
| # of the guard and leaving no check at all — a missing check is not a failing one, and this | |
| # is the check the release PR's footer tells maintainers to wait for before merging. | |
| # | |
| # It is also what keeps the workflow from failing silently if the platform ever opens | |
| # release PRs from a different account: the bridge below stops firing, but this check still | |
| # runs, the retitled version and the committed one disagree, and it turns red. The merge is | |
| # blocked loudly instead of releasing the old version. | |
| if: >- | |
| ${{ github.event.pull_request.base.ref == 'main' | |
| && startsWith(github.event.pull_request.title, 'release: ') }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| # The version committed on the PR head is what merging would actually release. | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| # Fails while the title version and the committed version disagree — the window between | |
| # a maintainer's retitle and the platform re-rendering the PR from it. Without this a | |
| # merge in that window would tag a release whose own files self-report the old version, | |
| # and a title release-please cannot parse would silently cut no release at all. | |
| - name: Compare the title version with the committed version | |
| env: | |
| # The title is human input, so it is bound through the environment (never | |
| # interpolated into the script) and matched against a strict semver pattern | |
| # before it is read. | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| run: | | |
| pattern='^release: ((0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?)$' | |
| if [[ ! "$PR_TITLE" =~ $pattern ]]; then | |
| echo "::error::Release PR title must be \"release: X.Y.Z\" with a full semver version, got: $PR_TITLE" | |
| exit 1 | |
| fi | |
| title_version="${BASH_REMATCH[1]}" | |
| manifest_version="$(jq -r '.["."]' .release-please-manifest.json)" | |
| if [[ "$title_version" != "$manifest_version" ]]; then | |
| echo "::error::Release PR title says $title_version but the pull request is versioned $manifest_version. Wait for the release PR to be re-rendered at $title_version before merging." | |
| exit 1 | |
| fi | |
| echo "Release PR title and committed version agree on $title_version." | |
| apply-title-version: | |
| name: Apply edited release version | |
| # A human retitle of the open release PR is bridged into the canonical explicit-version | |
| # mechanism (a Release-As commit on scalar-next, which the platform re-renders the | |
| # release PR from). `changes.title` is only set when the title itself changed, and bot | |
| # senders are ignored so the platform's own retitles cannot bounce back into another | |
| # commit. | |
| # | |
| # Both signals are required here, unlike the read-only check above, and each covers what the | |
| # other cannot. The author, because this job pushes with `contents: write` and a title is | |
| # free text — anyone able to open a pull request against main could otherwise | |
| # name a version and have it committed to scalar-next. `user.login` is set by GitHub when | |
| # the pull request is opened and cannot be forged by whoever edits the title afterwards. | |
| # The title, because those accounts open pull requests other than release PRs, and retitling | |
| # one of those must not push a release. | |
| # | |
| # One login per platform deployment, since a repo generated by staging carries staging's | |
| # app. Parenthesised because the group is ANDed with the title clause below: without the | |
| # parens that `&&` would bind to the last login alone, letting the other accounts push a | |
| # release off any title. Listed rather than matched on the shared `scalar-docs` stem — a | |
| # prefix test would also admit any future `scalar-docs-*[bot]`, including someone else's. | |
| if: >- | |
| ${{ github.event.action == 'edited' | |
| && github.event.changes.title != null | |
| && github.event.sender.type != 'Bot' | |
| && github.event.pull_request.state == 'open' | |
| && github.event.pull_request.base.ref == 'main' | |
| && (github.event.pull_request.user.login == 'scalar-docs[bot]' | |
| || github.event.pull_request.user.login == 'scalar-docs-staging[bot]' | |
| || github.event.pull_request.user.login == 'scalar-docs-development[bot]') | |
| && startsWith(github.event.pull_request.title, 'release: ') }} | |
| # One bridge run at a time per pull request, newest retitle wins. Two retitles in quick | |
| # succession (a version typo corrected seconds later) would otherwise start two runs that | |
| # both read the version committed on the PR head, both get past the no-op guard, and both | |
| # push — and since each fetches scalar-next immediately before committing, the second | |
| # push fast-forwards instead of failing. release-please honours the newest Release-As | |
| # footer, so the release would land on whichever run happened to push last, not on the | |
| # title the maintainer actually left behind. | |
| concurrency: | |
| group: release-title-edit-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| # The PR head, so the version the pull request currently carries can be read before | |
| # deciding whether anything needs to change. | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| - name: Push a Release-As commit for the edited version | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| run: | | |
| set -euo pipefail | |
| pattern='^release: ((0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?)$' | |
| if [[ ! "$PR_TITLE" =~ $pattern ]]; then | |
| echo "::error::Release PR title must be \"release: X.Y.Z\" with a full semver version, got: $PR_TITLE" | |
| exit 1 | |
| fi | |
| # Only ever a validated semver from here on, so it is safe in a commit message. | |
| version="${BASH_REMATCH[1]}" | |
| manifest_version="$(jq -r '.["."]' .release-please-manifest.json)" | |
| # The re-rendered PR is retitled to the version it now carries; stopping here keeps | |
| # that from producing an endless chain of Release-As commits. | |
| if [[ "$version" == "$manifest_version" ]]; then | |
| echo "This release PR already carries $version; nothing to do." | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git fetch origin scalar-next | |
| git checkout -B scalar-next origin/scalar-next | |
| git commit --allow-empty -m "chore: release $version" -m "Release-As: $version" | |
| # Plain (non-force) push: losing a race against a regeneration push fails loudly | |
| # rather than discarding it, and the retitle can simply be repeated. | |
| git push origin scalar-next | |
| echo "Pushed Release-As: $version to scalar-next. The release PR will be re-rendered at that version." |