Manual Release #47
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: Manual Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| bump_type: | |
| description: "Select semantic version bump type" | |
| required: true | |
| default: patch | |
| type: choice | |
| options: | |
| - major | |
| - minor | |
| - patch | |
| include_manylinux_2_28: | |
| description: "Include manylinux_2_28 build" | |
| required: true | |
| default: true | |
| type: boolean | |
| include_manylinux_2_34: | |
| description: "Include manylinux_2_34 build" | |
| required: true | |
| default: true | |
| type: boolean | |
| permissions: | |
| contents: write | |
| actions: write | |
| jobs: | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.next_version }} | |
| last_tag: ${{ steps.version.outputs.last_tag }} | |
| notes_file: ${{ steps.notes.outputs.file }} | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Determine next version | |
| id: version | |
| run: | | |
| set -eo pipefail | |
| git fetch --tags --force | |
| LAST_TAG=$(git describe --tags --abbrev=0 --match "v*" 2>/dev/null || true) | |
| if [ -z "$LAST_TAG" ]; then | |
| # First release: always start at 1.0.0 regardless of bump type | |
| NEXT_VERSION="1.0.0" | |
| echo "last_tag=" >> "$GITHUB_OUTPUT" | |
| echo "next_version=$NEXT_VERSION" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| else | |
| CURRENT=${LAST_TAG#v} | |
| fi | |
| IFS='.' read -r MAJOR MINOR PATCH <<<"$CURRENT" | |
| case "${{ github.event.inputs.bump_type }}" in | |
| major) | |
| MAJOR=$((MAJOR + 1)) | |
| MINOR=0 | |
| PATCH=0 | |
| ;; | |
| minor) | |
| MINOR=$((MINOR + 1)) | |
| PATCH=0 | |
| ;; | |
| *) | |
| PATCH=$((PATCH + 1)) | |
| ;; | |
| esac | |
| NEXT_VERSION="$MAJOR.$MINOR.$PATCH" | |
| echo "last_tag=$LAST_TAG" >> "$GITHUB_OUTPUT" | |
| echo "next_version=$NEXT_VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build release notes from conventional commits | |
| id: notes | |
| run: | | |
| set -eo pipefail | |
| NOTES_FILE="release-notes.md" | |
| LAST_TAG="${{ steps.version.outputs.last_tag }}" | |
| if [ -z "$LAST_TAG" ]; then | |
| RANGE="HEAD" | |
| else | |
| RANGE="$LAST_TAG..HEAD" | |
| fi | |
| echo "## Changes" > "$NOTES_FILE" | |
| HAS_CHANGES=false | |
| PATTERN='^(feat|fix|perf|refactor|docs)(\([^)]+\))?:\ (.+)$' | |
| while IFS= read -r SUBJECT; do | |
| if [[ $SUBJECT =~ $PATTERN ]]; then | |
| TYPE=${BASH_REMATCH[1]} | |
| SUMMARY=${BASH_REMATCH[3]} | |
| case "$TYPE" in | |
| feat) LABEL="Feature";; | |
| fix) LABEL="Fix";; | |
| perf) LABEL="Performance";; | |
| refactor) LABEL="Refactor";; | |
| docs) LABEL="Docs";; | |
| esac | |
| echo "- ${LABEL}: ${SUMMARY}" >> "$NOTES_FILE" | |
| HAS_CHANGES=true | |
| fi | |
| done < <(git log $RANGE --pretty=format:"%s") | |
| if [ "$HAS_CHANGES" = false ]; then | |
| echo "- No user-facing conventional commit changes since last release." >> "$NOTES_FILE" | |
| fi | |
| echo "file=$NOTES_FILE" >> "$GITHUB_OUTPUT" | |
| - name: Prepare common release files and models | |
| run: | | |
| ./scripts/prepare-common.sh "${{ steps.version.outputs.next_version }}" | |
| - name: Upload common files artifact | |
| id: upload | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-common-${{ steps.version.outputs.next_version }} | |
| path: release_common | |
| retention-days: 1 | |
| - name: Upload release notes | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-notes-${{ steps.version.outputs.next_version }} | |
| path: ${{ steps.notes.outputs.file }} | |
| retention-days: 1 | |
| package_manylinux_2_28: | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| container: registry.access.redhat.com/ubi8/ubi:8.10 | |
| steps: | |
| - name: include_manylinux_2_28 flag | |
| run: echo "include_manylinux_2_28=${{ inputs.include_manylinux_2_28 }}" | |
| - name: Check out repository | |
| if: ${{ inputs.include_manylinux_2_28 }} | |
| uses: actions/checkout@v4 | |
| - name: Download common files artifact | |
| if: ${{ inputs.include_manylinux_2_28 }} | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-common-${{ needs.prepare.outputs.version }} | |
| path: release_common | |
| - name: Build manylinux package | |
| id: build_package | |
| if: ${{ inputs.include_manylinux_2_28 }} | |
| run: | | |
| chmod +x scripts/package-manylinux-2_28.sh | |
| scripts/package-manylinux-2_28.sh "${{ needs.prepare.outputs.version }}" | |
| - name: Upload manylinux package artifact | |
| if: ${{ inputs.include_manylinux_2_28 }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: package-manylinux-2_28-${{ needs.prepare.outputs.version }} | |
| path: ${{ steps.build_package.outputs.asset_path }} | |
| retention-days: 1 | |
| package_manylinux_2_34: | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| container: registry.access.redhat.com/ubi9/ubi:9.7 | |
| steps: | |
| - name: include_manylinux_2_34 flag | |
| run: echo "include_manylinux_2_34=${{ inputs.include_manylinux_2_34 }}" | |
| - name: Check out repository | |
| if: ${{ inputs.include_manylinux_2_34 }} | |
| uses: actions/checkout@v4 | |
| - name: Download common files artifact | |
| if: ${{ inputs.include_manylinux_2_34 }} | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-common-${{ needs.prepare.outputs.version }} | |
| path: release_common | |
| - name: Build manylinux package | |
| id: build_package | |
| if: ${{ inputs.include_manylinux_2_34 }} | |
| run: | | |
| chmod +x scripts/package-manylinux-2_34.sh | |
| scripts/package-manylinux-2_34.sh "${{ needs.prepare.outputs.version }}" | |
| - name: Upload manylinux package artifact | |
| if: ${{ inputs.include_manylinux_2_34 }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: package-manylinux-2_34-${{ needs.prepare.outputs.version }} | |
| path: ${{ steps.build_package.outputs.asset_path }} | |
| retention-days: 1 | |
| release: | |
| needs: [prepare, package_manylinux_2_28, package_manylinux_2_34] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Download manylinux_2_28 package | |
| uses: actions/download-artifact@v4 | |
| if: ${{ inputs.include_manylinux_2_28 }} | |
| with: | |
| name: package-manylinux-2_28-${{ needs.prepare.outputs.version }} | |
| path: packages | |
| - name: Download manylinux_2_34 package | |
| uses: actions/download-artifact@v4 | |
| if: ${{ inputs.include_manylinux_2_34 }} | |
| with: | |
| name: package-manylinux-2_34-${{ needs.prepare.outputs.version }} | |
| path: packages | |
| - name: Download release notes | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-notes-${{ needs.prepare.outputs.version }} | |
| path: . | |
| - name: Find artifacts | |
| id: find_artifacts | |
| run: | | |
| set -eo pipefail | |
| FILES="" | |
| if [[ "${{ inputs.include_manylinux_2_34 }}" == "true" ]]; then | |
| PKG_234=$(find packages -name "opd-mcp-*-manylinux_2_34*.zip" -type f | head -1) | |
| if [ -z "$PKG_234" ]; then | |
| echo "Error: Missing manylinux_2_34 package" >&2 | |
| exit 1 | |
| fi | |
| FILES+="$PKG_234" | |
| echo "package_234=$PKG_234" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ "${{ inputs.include_manylinux_2_28 }}" == "true" ]]; then | |
| PKG_228=$(find packages -name "opd-mcp-*-manylinux_2_28*.zip" -type f | head -1) | |
| if [ -z "$PKG_228" ]; then | |
| echo "Error: Missing manylinux_2_28 package" >&2 | |
| exit 1 | |
| fi | |
| if [ -n "$FILES" ]; then FILES+=$'\n'; fi | |
| FILES+="$PKG_228" | |
| echo "package_228=$PKG_228" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ -z "$FILES" ]]; then | |
| echo "Error: No artifacts to release" >&2 | |
| exit 1 | |
| fi | |
| printf 'files<<EOF\n%s\nEOF\n' "$FILES" >> "$GITHUB_OUTPUT" | |
| - name: Create and push tag | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -eo pipefail | |
| git config user.name "github-actions" | |
| git config user.email "github-actions@users.noreply.github.com" | |
| git tag -a "v${{ needs.prepare.outputs.version }}" -m "Release v${{ needs.prepare.outputs.version }}" | |
| git push origin "v${{ needs.prepare.outputs.version }}" | |
| - name: Create GitHub release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: v${{ needs.prepare.outputs.version }} | |
| name: Release v${{ needs.prepare.outputs.version }} | |
| body_path: release-notes.md | |
| files: ${{ steps.find_artifacts.outputs.files }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Delete previous releases and artifacts | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| CURRENT_TAG: v${{ needs.prepare.outputs.version }} | |
| run: | | |
| set -eo pipefail | |
| echo "Deleting previous releases (keeping tags)..." | |
| gh release list --limit 100 --json tagName --jq '.[] | .tagName' | while read -r TAG; do | |
| if [ "$TAG" != "$CURRENT_TAG" ]; then | |
| echo "Deleting release $TAG" | |
| gh release delete "$TAG" --yes | |
| fi | |
| done | |
| echo "Deleting Actions Artifacts..." | |
| gh api "repos/$GITHUB_REPOSITORY/actions/artifacts" --paginate -q '.artifacts[].id' | while read -r ARTIFACT_ID; do | |
| echo "Deleting artifact $ARTIFACT_ID" | |
| gh api "repos/$GITHUB_REPOSITORY/actions/artifacts/$ARTIFACT_ID" -X DELETE | |
| done |