Release #1
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: | |
| version: | |
| description: 'Release version (e.g., 1.3). Leave empty to auto-increment minor. Do not add `v` into version name as it is prepended automatically.' | |
| required: false | |
| type: string | |
| description: | |
| description: 'Custom release description (prepended before auto-generated changelog).' | |
| required: false | |
| type: string | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Validate branch | |
| id: branch | |
| run: | | |
| BRANCH="${{ github.ref_name }}" | |
| echo "Validating branch: ${BRANCH}" | |
| if [[ ! "$BRANCH" =~ ^release-([0-9]+)\.x$ ]]; then | |
| echo "::error::Must run on a release-X.x branch (e.g., release-1.x)" | |
| exit 1 | |
| fi | |
| echo "major=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine version | |
| id: version | |
| env: | |
| MAJOR: ${{ steps.branch.outputs.major }} | |
| run: | | |
| if [[ -n "${{ inputs.version }}" ]]; then | |
| VERSION="${{ inputs.version }}" | |
| if [[ ! "$VERSION" =~ ^${MAJOR}\.[0-9]+$ ]]; then | |
| echo "::error::Version $VERSION does not match branch major version $MAJOR" | |
| exit 1 | |
| fi | |
| else | |
| LATEST=$(git tag -l "v${MAJOR}.*" --sort=-v:refname | head -n1) | |
| if [[ -z "$LATEST" ]]; then | |
| VERSION="${MAJOR}.0" | |
| else | |
| MINOR="${LATEST##*.}" | |
| VERSION="${MAJOR}.$((MINOR + 1))" | |
| fi | |
| fi | |
| TAG="v$VERSION" | |
| if git rev-parse "refs/tags/$TAG" &>/dev/null; then | |
| echo "::error::Tag $TAG already exists" | |
| exit 1 | |
| fi | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "Releasing: $TAG" | |
| - name: Create and push tag | |
| env: | |
| TAG: ${{ steps.version.outputs.tag }} | |
| run: | | |
| git tag "$TAG" | |
| git push origin "$TAG" | |
| - name: Update floating major tag | |
| env: | |
| TAG: ${{ steps.version.outputs.tag }} | |
| MAJOR: ${{ steps.branch.outputs.major }} | |
| run: | | |
| git tag -f "v${MAJOR}" "$TAG" | |
| git push origin "v${MAJOR}" --force | |
| - name: Generate release notes and create release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TAG: ${{ steps.version.outputs.tag }} | |
| MAJOR: ${{ steps.branch.outputs.major }} | |
| run: | | |
| PREV_TAG=$(git tag -l "v${MAJOR}.*" --sort=-v:refname | grep -v "^${TAG}$" | head -n1) | |
| API_ARGS=(-f tag_name="$TAG") | |
| if [[ -n "$PREV_TAG" ]]; then | |
| API_ARGS+=(-f previous_tag_name="$PREV_TAG") | |
| fi | |
| AUTO_NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes \ | |
| "${API_ARGS[@]}" --jq '.body') | |
| DESCRIPTION="${{ inputs.description }}" | |
| if [[ -n "$DESCRIPTION" ]]; then | |
| printf '%s\n\n---\n\n%s' "$DESCRIPTION" "$AUTO_NOTES" > release-notes.md | |
| else | |
| echo "$AUTO_NOTES" > release-notes.md | |
| fi | |
| gh release create "$TAG" \ | |
| --title "$TAG" \ | |
| --notes-file release-notes.md |