Manual Release #35
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 | |
| 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_installer: | |
| needs: [prepare, package_manylinux_2_28, package_manylinux_2_34] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Download common files artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-common-${{ needs.prepare.outputs.version }} | |
| path: release_common | |
| - name: Restore cached models | |
| run: | | |
| mkdir -p models | |
| cp -r release_common/models/* models/ | |
| # Ensure universal_config is also consistent if modified in prepare (though it's just a copy) | |
| cp release_common/universal_config.json . | |
| - name: Download manylinux_2_28 package | |
| if: ${{ inputs.include_manylinux_2_28 }} | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: package-manylinux-2_28-${{ needs.prepare.outputs.version }} | |
| path: packages | |
| - name: Download manylinux_2_34 package | |
| if: ${{ inputs.include_manylinux_2_34 }} | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: package-manylinux-2_34-${{ needs.prepare.outputs.version }} | |
| path: packages | |
| - name: Select payload root | |
| run: | | |
| set -eo pipefail | |
| PAYLOAD_ROOT="" | |
| if [ "${{ inputs.include_manylinux_2_34 }}" = "true" ]; then | |
| PKG_234=$(find packages -name "opd-mcp-*-manylinux_2_34*.zip" -type f | head -1) | |
| if [ -n "$PKG_234" ]; then | |
| echo "Using manylinux_2_34 package: $PKG_234" | |
| unzip -q "$PKG_234" -d . | |
| PAYLOAD_ROOT="$(pwd)/release_package_manylinux_2_34/opd-mcp" | |
| fi | |
| fi | |
| if [ -z "$PAYLOAD_ROOT" ] && [ "${{ inputs.include_manylinux_2_28 }}" = "true" ]; then | |
| PKG_228=$(find packages -name "opd-mcp-*-manylinux_2_28*.zip" -type f | head -1) | |
| if [ -n "$PKG_228" ]; then | |
| echo "Using manylinux_2_28 package: $PKG_228" | |
| unzip -q "$PKG_228" -d . | |
| PAYLOAD_ROOT="$(pwd)/release_package_manylinux_2_28/opd-mcp" | |
| fi | |
| fi | |
| if [ -z "$PAYLOAD_ROOT" ]; then | |
| echo "Error: No manylinux payload available for installer build" >&2 | |
| exit 1 | |
| fi | |
| echo "PAYLOAD_ROOT=$PAYLOAD_ROOT" >> "$GITHUB_ENV" | |
| - name: Build Installer | |
| run: | | |
| chmod +x scripts/build_installer.sh | |
| ./scripts/build_installer.sh "$PAYLOAD_ROOT" | |
| # Rename to include version | |
| mv build-output/opd-mcp-installer.sh build-output/opd-mcp-${{ needs.prepare.outputs.version }}-installer.sh | |
| - name: Upload installer artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: installer-${{ needs.prepare.outputs.version }} | |
| path: build-output/opd-mcp-${{ needs.prepare.outputs.version }}-installer.sh | |
| 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_installer, 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 installer artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: installer-${{ needs.prepare.outputs.version }} | |
| path: packages | |
| - 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 | |
| INSTALLER=$(find packages -name "*-installer.sh" -type f | head -1) | |
| if [ -z "$INSTALLER" ]; then | |
| echo "Error: Could not find installer artifact" | |
| exit 1 | |
| fi | |
| FILES="$INSTALLER" | |
| 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 artifact" | |
| exit 1 | |
| fi | |
| FILES+=$'\n'"$PKG_228" | |
| echo "package_228=$PKG_228" >> "$GITHUB_OUTPUT" | |
| fi | |
| 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 artifact" | |
| exit 1 | |
| fi | |
| FILES+=$'\n'"$PKG_234" | |
| echo "package_234=$PKG_234" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [[ -z "$FILES" ]] || [[ "$FILES" == "$INSTALLER" && "${{ inputs.include_manylinux_2_28 }}" != "true" && "${{ inputs.include_manylinux_2_34 }}" != "true" ]]; then | |
| echo "Error: No manylinux builds were selected" | |
| exit 1 | |
| fi | |
| echo "installer=$INSTALLER" >> "$GITHUB_OUTPUT" | |
| 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 }} |