Release #15
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 | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| description: Release type | |
| required: true | |
| type: choice | |
| options: | |
| - prep | |
| - rc | |
| - stable | |
| version: | |
| description: "Version number (e.g. 0.11.0)" | |
| required: true | |
| type: string | |
| rc_number: | |
| description: "RC number (e.g. 1) — required for RC releases, must be empty for prep" | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| environment: release | |
| env: | |
| POETRY: poetry | |
| steps: | |
| - name: Restrict to release managers | |
| if: github.actor != 'yyyyyyyan' && github.actor != 'maxbube' | |
| run: | | |
| echo "::error::Only release managers (yyyyyyyan, maxbube) can trigger this workflow." | |
| exit 1 | |
| - name: Validate inputs | |
| env: | |
| RELEASE_TYPE: ${{ inputs.release_type }} | |
| VERSION: ${{ inputs.version }} | |
| RC_NUMBER: ${{ inputs.rc_number }} | |
| run: | | |
| if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "::error::version must match X.Y.Z (e.g. 0.11.0)" | |
| exit 1 | |
| fi | |
| if [ "$RELEASE_TYPE" = "rc" ]; then | |
| if [ -z "$RC_NUMBER" ]; then | |
| echo "::error::rc_number is required when release_type is 'rc'" | |
| exit 1 | |
| fi | |
| if ! [[ "$RC_NUMBER" =~ ^[1-9][0-9]*$ ]]; then | |
| echo "::error::rc_number must be a positive integer" | |
| exit 1 | |
| fi | |
| fi | |
| if [ "$RELEASE_TYPE" = "prep" ] && [ -n "$RC_NUMBER" ]; then | |
| echo "::error::rc_number must be empty when release_type is 'prep'" | |
| exit 1 | |
| fi | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: ${{ inputs.release_type == 'stable' && format('release/v{0}', inputs.version) || 'main' }} | |
| fetch-depth: 0 | |
| token: ${{ secrets.RELEASE_PAT }} | |
| - name: Configure git user | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| - name: Checkout release branch if it exists | |
| if: inputs.release_type == 'rc' | |
| env: | |
| VERSION: ${{ inputs.version }} | |
| run: | | |
| set -euo pipefail | |
| ls_remote_status=0 | |
| git ls-remote --exit-code --heads origin "release/v${VERSION}" >/dev/null 2>&1 || ls_remote_status=$? | |
| if [ "$ls_remote_status" -eq 0 ]; then | |
| git fetch origin "release/v${VERSION}:release/v${VERSION}" | |
| git checkout "release/v${VERSION}" | |
| echo "Gates will run against release/v${VERSION} (cherries included)." | |
| elif [ "$ls_remote_status" -eq 2 ]; then | |
| echo "release/v${VERSION} does not exist yet (rc1-fresh path); gates run against main." | |
| else | |
| echo "::error::git ls-remote failed (exit ${ls_remote_status}); cannot determine whether release/v${VERSION} exists. Aborting so gates don't silently run against the wrong target." | |
| exit 1 | |
| fi | |
| - name: Setup Python | |
| uses: ./.github/actions/setup-python-job | |
| with: | |
| python-version: "3.11" | |
| - name: Pre-tag lint gate (all-files pre-commit) | |
| run: make run-pre-commit | |
| - name: Pre-tag security audit (bandit + pip-audit) | |
| run: make audit | |
| - name: Pre-tag migration check | |
| env: | |
| CASDOOR__CLIENT_SECRET: notatoken | |
| CASDOOR__CLIENT_ID: notatoken | |
| run: make checkmigrations | |
| - name: Run release | |
| env: | |
| GH_TOKEN: ${{ secrets.RELEASE_PAT }} | |
| GH_PR_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| RELEASE_TYPE: ${{ inputs.release_type }} | |
| VERSION: ${{ inputs.version }} | |
| RC_NUMBER: ${{ inputs.rc_number }} | |
| JIRA_VERSION_CREATE_WEBHOOK_URL: ${{ secrets.JIRA_VERSION_CREATE_WEBHOOK_URL }} | |
| JIRA_VERSION_CREATE_WEBHOOK_SECRET: ${{ secrets.JIRA_VERSION_CREATE_WEBHOOK_SECRET }} | |
| JIRA_VERSION_RELEASE_WEBHOOK_URL: ${{ secrets.JIRA_VERSION_RELEASE_WEBHOOK_URL }} | |
| JIRA_VERSION_RELEASE_WEBHOOK_SECRET: ${{ secrets.JIRA_VERSION_RELEASE_WEBHOOK_SECRET }} | |
| run: | | |
| if [ "$RELEASE_TYPE" = "rc" ]; then | |
| make release-rc VERSION="$VERSION" RC="$RC_NUMBER" SIGN_VIA_API=1 | |
| elif [ "$RELEASE_TYPE" = "prep" ]; then | |
| make release-prep VERSION="$VERSION" SIGN_VIA_API=1 | |
| else | |
| make release-stable VERSION="$VERSION" SIGN_VIA_API=1 | |
| fi | |
| - name: Assert back-merge ancestor invariant | |
| if: ${{ inputs.release_type == 'stable' }} | |
| env: | |
| VERSION: ${{ inputs.version }} | |
| run: | | |
| set -euo pipefail | |
| git fetch origin main | |
| if ! git merge-base --is-ancestor "v${VERSION}" origin/main; then | |
| echo "::error::Post-release invariant failed: v${VERSION} is NOT an ancestor of origin/main. The back-merge in cmd_stable did not land. Investigate before re-running." | |
| exit 1 | |
| fi | |
| echo "OK: v${VERSION} is an ancestor of origin/main." |