name: Release # This workflow automates the complete release process for AutoLibrary application # It is triggered when a new release branch is created (release/vX.Y.Z or release/vX.Y.Z-rc*) # # Workflow Steps: # START > # 1. Extract Version: # Extracts version number from branch name: # - release/v1.0.0 -> v1.0.0 (stable release) # - release/v1.0.0-rc1 -> v1.0.0 (release candidate) # 2. Update Version: # Updates version information in 'ALVersionInfo.py' with build metadata and archives # the updated version file as an artifact. # # for more information, please refer to the comment in the workflow 'update-version.yml' # 3. Commit Release: # Commits version changes to release branch and creates the release tag. # 4. Build: # Compiles the application for Windows and macOS platforms using PyInstaller, and # archives the built artifacts. # - Windows: AutoLibrary.-windows-x86_64.zip # - macOS: AutoLibrary.-macos-arm64.dmg # 5. Release: # Creates GitHub release with generated artifacts and release notes # < END # # 6. Merge back: # Merges release branch back to main branch, and clean/delete the release branch # # The workflow ensures version consistency between source code, built artifacts, and GitHub releases # while maintaining proper commit history and tag management. # # IMPORTANT: This workflow only triggers on branch CREATION, not on pushes to release branches. # If you need to fix issues on a release branch, delete the tag, merge fixes to main, # and create a new release branch. on: push: branches: - 'release/v*' jobs: # # Start : # virtual job that indicates the start of the release process # start: runs-on: ubuntu-latest steps: - name: Start release run: | echo "✓ Starting release" echo "Branch: ${{ github.ref_name }}" echo "Ref: ${{ github.ref }}" # # Extract version : # this job extracts version from branch name # extract-version: needs: - start runs-on: ubuntu-latest outputs: tag_name: ${{ steps.extract.outputs.tag_name }} version: ${{ steps.extract.outputs.version }} is_rc: ${{ steps.extract.outputs.is_rc }} steps: - name: Extract version from branch name id: extract run: | BRANCH_NAME="${{ github.ref_name }}" # Validate branch name starts with 'release/v' if ! echo "$BRANCH_NAME" | grep -qE '^release/v'; then echo "✗ Error: Branch '$BRANCH_NAME' does not start with 'release/v'" echo "✗ This workflow should only be triggered by release branches" exit 1 fi # Extract version from branch name: # - release/v1.0.0 -> v1.0.0 (stable) # - release/v1.0.0-rc1 -> v1.0.0 (release candidate) # - release/v1.0.0-alpha.1 -> v1.0.0-alpha.1 (pre-release) if echo "$BRANCH_NAME" | grep -qE '^release/v[0-9]+\.[0-9]+\.[0-9]+$'; then # Stable release: release/v1.0.0 -> v1.0.0 TAG_NAME=$(echo "$BRANCH_NAME" | sed -E 's|^release/(v[0-9]+\.[0-9]+\.[0-9]+)$|\1|') IS_RC=false elif echo "$BRANCH_NAME" | grep -qE '^release/v[0-9]+\.[0-9]+\.[0-9]+-'; then # Pre-release: release/v1.0.0-rc1 -> v1.0.0-rc1 TAG_NAME=$(echo "$BRANCH_NAME" | sed -E 's|^release/(v[0-9]+\.[0-9]+\.[0-9]+-.*)$|\1|') IS_RC=true else echo "✗ Error: Branch '$BRANCH_NAME' does not match expected format" echo "✗ Expected format: release/vX.Y.Z or release/vX.Y.Z-rcX" exit 1 fi VERSION="${TAG_NAME#v}" echo "TAG_NAME=$TAG_NAME" >> $GITHUB_OUTPUT echo "VERSION=$VERSION" >> $GITHUB_OUTPUT echo "IS_RC=$IS_RC" >> $GITHUB_OUTPUT echo "✓ Branch: $BRANCH_NAME" echo "✓ Tag: $TAG_NAME" echo "✓ Version: $VERSION" echo "✓ Is RC: $IS_RC" # # Update version : # this job updates the version in the file 'ALVersionInfo.py' # update-version: needs: - extract-version uses: ./.github/workflows/update-version.yml permissions: contents: write with: tag_name: ${{ needs.extract-version.outputs.tag_name }} ref: ${{ github.ref }} # # Commit release : # this job commits the updated version file to main and creates # the release tag (not moving an existing tag) # commit-release: needs: - extract-version - update-version if: ${{ needs.update-version.outputs.has_changes == 'true' }} uses: ./.github/workflows/commit-release.yml permissions: contents: write with: tag_name: ${{ needs.extract-version.outputs.tag_name }} version: ${{ needs.extract-version.outputs.version }} file_path: src/gui/ALVersionInfo.py create_tag: 'true' is_rc: ${{ needs.extract-version.outputs.is_rc }} ref: ${{ github.ref }} # # Build : # this job builds the application artifacts and archives them build: needs: - update-version - commit-release if: always() && needs.update-version.result == 'success' && (needs.commit-release.result == 'success' || needs.commit-release.result == 'skipped') uses: ./.github/workflows/build.yml permissions: contents: write with: tag_name: ${{ needs.update-version.outputs.tag_name }} version: ${{ needs.update-version.outputs.version }} is_test: 'false' # # Release : # this job creates a GitHub release and uploads the archive files release: runs-on: ubuntu-latest needs: - build - extract-version if: always() && needs.build.result == 'success' permissions: contents: write steps: - name: Download Windows artifacts uses: actions/download-artifact@v6 with: name: AutoLibrary.${{ needs.extract-version.outputs.tag_name }}-windows-x86_64 path: artifacts/ - name: Download macOS artifacts uses: actions/download-artifact@v6 with: name: AutoLibrary.${{ needs.extract-version.outputs.tag_name }}-macos-arm64 path: artifacts/ - name: Create release id: create_release uses: softprops/action-gh-release@v2 with: tag_name: ${{ needs.extract-version.outputs.tag_name }} name: AutoLibrary ${{ needs.extract-version.outputs.tag_name }} files: | artifacts/AutoLibrary.${{ needs.extract-version.outputs.tag_name }}-windows-x86_64.zip artifacts/AutoLibrary.${{ needs.extract-version.outputs.tag_name }}-macos-arm64.dmg draft: false prerelease: ${{ needs.extract-version.outputs.is_rc == 'true' }} generate_release_notes: true body: | --- **完整更新日志见下方自动生成的 Release Notes** env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # End : # virtual job that indicates the end of the release process # end: needs: - release runs-on: ubuntu-latest steps: - name: End release run: | echo "✓ Ending release" # # Merge Back : # this job merges the release branch to main after successful release # merge-back: needs: - release - extract-version - commit-release if: ${{ needs.release.result == 'success' && (needs.commit-release.result == 'success' || needs.commit-release.result == 'skipped') }} runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout code uses: actions/checkout@v6 with: fetch-depth: 0 - name: Merge release branch to main run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" # Use the release branch name from the original trigger BRANCH_NAME="${{ needs.extract-version.outputs.tag_name }}" # Extract branch name: v1.0.0 -> release/v1.0.0 if [[ ! "$BRANCH_NAME" =~ ^release/ ]]; then BRANCH_NAME="release/${BRANCH_NAME}" fi MAIN_BRANCH=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5) if [ -z "$MAIN_BRANCH" ]; then MAIN_BRANCH="main" fi echo "Merging ${BRANCH_NAME} to ${MAIN_BRANCH}..." echo "Current commit info:" git log --oneline -3 # Fetch all branches including the release branch git fetch origin ${BRANCH_NAME} git fetch origin ${MAIN_BRANCH} # Checkout main branch git checkout ${MAIN_BRANCH} # Show branch status before merge echo "========================================" echo "Branch status before merge" echo "========================================" git log --oneline --graph --all -5 echo "========================================" echo "Diff: ${MAIN_BRANCH} vs origin/${BRANCH_NAME}" echo "========================================" git diff ${MAIN_BRANCH} origin/${BRANCH_NAME} --stat || echo "No differences found" # Force create a merge commit even if there are no changes # This ensures the release history is properly recorded git merge origin/${BRANCH_NAME} \ --no-ff \ -m "chore(release): merge ${BRANCH_NAME} to ${MAIN_BRANCH} [auto release commit]" # Show merge result echo "========================================" echo "Merge result" echo "========================================" git log --oneline --graph -3 # Push to main git push origin ${MAIN_BRANCH} echo "✓ Successfully merged ${BRANCH_NAME} to ${MAIN_BRANCH}" - name: Delete release branch run: | BRANCH_NAME="${{ needs.extract-version.outputs.tag_name }}" # Extract branch name: v1.0.0 -> release/v1.0.0 if [[ ! "$BRANCH_NAME" =~ ^release/ ]]; then BRANCH_NAME="release/${BRANCH_NAME}" fi echo "Deleting release branch: ${BRANCH_NAME}" git push origin --delete ${BRANCH_NAME} echo "✓ Deleted branch ${BRANCH_NAME}" - name: Release cleanup summary run: | BRANCH_NAME="${{ github.ref_name }}" TAG_NAME="${{ needs.extract-version.outputs.tag_name }}" MAIN_BRANCH=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5) if [ -z "$MAIN_BRANCH" ]; then MAIN_BRANCH="main" fi echo "## Release Cleanup Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "========================================" >> $GITHUB_STEP_SUMMARY echo "✓ Release completed successfully!" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Actions Performed:" >> $GITHUB_STEP_SUMMARY echo "- Merged \`${BRANCH_NAME}\` to \`${MAIN_BRANCH}\`" >> $GITHUB_STEP_SUMMARY echo "- Deleted release branch \`${BRANCH_NAME}\`" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Release Details:" >> $GITHUB_STEP_SUMMARY echo "- Tag: \`${TAG_NAME}\`" >> $GITHUB_STEP_SUMMARY echo "- Version: \`${{ needs.extract-version.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY echo "- Release Type: $([ "${{ needs.extract-version.outputs.is_rc }}" = "true" ] && echo "Release Candidate" || echo "Stable Release")" >> $GITHUB_STEP_SUMMARY