chore: release 5.2.3 (#17) #25
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
| # ============================================================================= | |
| # Release Please Workflow for open-runtime/grpc-dart Fork | |
| # ============================================================================= | |
| # Automates releases using Conventional Commits: | |
| # - feat: β Minor version bump | |
| # - fix: β Patch version bump | |
| # - feat!/fix!/BREAKING CHANGE: β Major version bump | |
| # | |
| # Flow: | |
| # 1. Creates/updates a Release PR when conventional commits land | |
| # 2. Claude enhances the PR (via enhance-release-pr.yml) | |
| # 3. When merged: Creates tag and GitHub Release | |
| # 4. Dart packages can consume via tag_pattern: "^v" | |
| # ============================================================================= | |
| name: Release Please | |
| on: | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: # Allow manual triggering | |
| # Only one Release Please run per branch at a time | |
| concurrency: | |
| group: release-please-${{ github.ref_name }} | |
| cancel-in-progress: false # Don't cancel - Release Please should complete | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| actions: write | |
| jobs: | |
| # ============================================================================= | |
| # Step 1: Handle Untagged Merged Release PRs FIRST | |
| # ============================================================================= | |
| # This MUST run before release-please to prevent "untagged PRs outstanding" abort | |
| # Release Please aborts if it finds merged PRs with "autorelease: pending" label | |
| # By running this first, we ensure all untagged PRs are tagged before Release Please checks | |
| handle-untagged-releases: | |
| name: π§ Handle Untagged Releases (Pre-Check) | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_created: ${{ steps.process.outputs.release_created }} | |
| tag_name: ${{ steps.process.outputs.tag_name }} | |
| version: ${{ steps.process.outputs.version }} | |
| steps: | |
| - name: π₯ Checkout source code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: π Find and process untagged merged release PRs | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo "π Checking for untagged merged release PRs..." | |
| # Find merged PRs with autorelease: pending label | |
| PENDING_PRS=$(gh pr list --repo ${{ github.repository }} \ | |
| --state merged \ | |
| --label "autorelease: pending" \ | |
| --json number,title,mergeCommit \ | |
| --jq '.[] | select(.title | test("^chore: release "))') | |
| if [ -z "$PENDING_PRS" ]; then | |
| echo "β No untagged merged release PRs found" | |
| exit 0 | |
| fi | |
| echo "π Found untagged release PRs:" | |
| echo "$PENDING_PRS" | |
| # Process each pending PR | |
| echo "$PENDING_PRS" | jq -c '.' | while read -r pr; do | |
| PR_NUM=$(echo "$pr" | jq -r '.number') | |
| PR_TITLE=$(echo "$pr" | jq -r '.title') | |
| MERGE_SHA=$(echo "$pr" | jq -r '.mergeCommit.oid') | |
| # Extract version from title "chore: release X.Y.Z" | |
| VERSION=$(echo "$PR_TITLE" | grep -oP '\d+\.\d+\.\d+') | |
| if [ -z "$VERSION" ]; then | |
| echo "β οΈ Could not extract version from PR #$PR_NUM title: $PR_TITLE" | |
| continue | |
| fi | |
| TAG_NAME="v$VERSION" | |
| echo "π¦ Processing PR #$PR_NUM: $PR_TITLE" | |
| echo " Version: $VERSION" | |
| echo " SHA: $MERGE_SHA" | |
| echo " Tag: $TAG_NAME" | |
| if gh api repos/${{ github.repository }}/git/refs/tags/$TAG_NAME 2>/dev/null; then | |
| echo "β Tag $TAG_NAME already exists" | |
| else | |
| echo "π·οΈ Creating tag $TAG_NAME at $MERGE_SHA" | |
| gh api repos/${{ github.repository }}/git/refs \ | |
| -X POST \ | |
| -f ref="refs/tags/$TAG_NAME" \ | |
| -f sha="$MERGE_SHA" | |
| fi | |
| if gh release view $TAG_NAME --repo ${{ github.repository }} 2>/dev/null; then | |
| echo "β Release $TAG_NAME already exists" | |
| else | |
| echo "π Creating GitHub Release $TAG_NAME" | |
| RELEASE_CREATED=true | |
| LATEST_TAG="$TAG_NAME" | |
| LATEST_VERSION="$VERSION" | |
| CHANGELOG=$(awk -v ver="$VERSION" ' | |
| /^## \[/ { | |
| if (found) exit | |
| if ($0 ~ "## \\[" ver "\\]") found=1 | |
| } | |
| found { print } | |
| ' CHANGELOG.md | tail -n +2) | |
| if [ -z "$CHANGELOG" ]; then | |
| CHANGELOG="Release v$VERSION - See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/CHANGELOG.md) for details." | |
| fi | |
| { | |
| echo "## π¦ Installation" | |
| echo "" | |
| echo "**Dart 3.9+ (recommended):**" | |
| echo "\`\`\`yaml" | |
| echo "dependencies:" | |
| echo " grpc:" | |
| echo " git:" | |
| echo " url: https://github.com/open-runtime/grpc-dart" | |
| echo " tag_pattern: \"^v\"" | |
| echo " version: ^$VERSION" | |
| echo "\`\`\`" | |
| echo "" | |
| echo "**Direct tag reference:**" | |
| echo "\`\`\`yaml" | |
| echo "dependencies:" | |
| echo " grpc:" | |
| echo " git:" | |
| echo " url: https://github.com/open-runtime/grpc-dart" | |
| echo " ref: v$VERSION" | |
| echo "\`\`\`" | |
| echo "" | |
| echo "---" | |
| echo "" | |
| echo "$CHANGELOG" | |
| } > /tmp/release_notes.md | |
| gh release create $TAG_NAME \ | |
| --repo ${{ github.repository }} \ | |
| --title "v$VERSION" \ | |
| --notes-file /tmp/release_notes.md | |
| fi | |
| echo "π·οΈ Updating PR #$PR_NUM label to autorelease: tagged" | |
| gh pr edit $PR_NUM \ | |
| --repo ${{ github.repository }} \ | |
| --remove-label "autorelease: pending" \ | |
| --add-label "autorelease: tagged" || true | |
| echo "β Processed PR #$PR_NUM" | |
| done < <(echo "$PENDING_PRS" | jq -c '.') | |
| # Output the latest release info (if any was created) | |
| if [ "$RELEASE_CREATED" = "true" ]; then | |
| echo "release_created=true" >> $GITHUB_OUTPUT | |
| echo "tag_name=$LATEST_TAG" >> $GITHUB_OUTPUT | |
| echo "version=$LATEST_VERSION" >> $GITHUB_OUTPUT | |
| else | |
| echo "release_created=false" >> $GITHUB_OUTPUT | |
| fi | |
| # ============================================================================= | |
| # Step 2: Run Release Please (after untagged PRs are handled) | |
| # ============================================================================= | |
| release-please: | |
| name: π¦ Release Please | |
| needs: handle-untagged-releases | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_created: ${{ steps.release.outputs.release_created }} | |
| releases_created: ${{ steps.release.outputs.releases_created }} | |
| tag_name: ${{ steps.release.outputs.tag_name }} | |
| version: ${{ steps.release.outputs.version }} | |
| major: ${{ steps.release.outputs.major }} | |
| minor: ${{ steps.release.outputs.minor }} | |
| patch: ${{ steps.release.outputs.patch }} | |
| sha: ${{ steps.release.outputs.sha }} | |
| pr: ${{ steps.release.outputs.pr }} | |
| prs_created: ${{ steps.release.outputs.prs_created }} | |
| steps: | |
| - name: π₯ Checkout source code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: π Final Check for Untagged PRs (Race Condition Protection) | |
| id: final_check | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo "π Final check for untagged merged release PRs (race condition protection)..." | |
| PENDING_PRS=$(gh pr list --repo ${{ github.repository }} \ | |
| --state merged \ | |
| --label "autorelease: pending" \ | |
| --json number,title,mergeCommit \ | |
| --jq '.[] | select(.title | test("^chore: release "))') | |
| if [ -z "$PENDING_PRS" ]; then | |
| echo "β No untagged merged release PRs found" | |
| exit 0 | |
| fi | |
| echo "β οΈ Found untagged PRs merged during workflow run - processing now..." | |
| while IFS= read -r pr; do | |
| PR_NUM=$(echo "$pr" | jq -r '.number') | |
| PR_TITLE=$(echo "$pr" | jq -r '.title') | |
| MERGE_SHA=$(echo "$pr" | jq -r '.mergeCommit.oid') | |
| VERSION=$(echo "$PR_TITLE" | sed -E 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/' || echo "") | |
| if [ -z "$VERSION" ]; then | |
| echo "β οΈ Could not extract version from PR #$PR_NUM title: $PR_TITLE" | |
| continue | |
| fi | |
| TAG_NAME="v$VERSION" | |
| echo "π¦ Processing PR #$PR_NUM: $PR_TITLE" | |
| # Create tag if missing | |
| if ! gh api repos/${{ github.repository }}/git/refs/tags/$TAG_NAME 2>/dev/null; then | |
| echo "π·οΈ Creating tag $TAG_NAME at $MERGE_SHA" | |
| gh api repos/${{ github.repository }}/git/refs \ | |
| -X POST \ | |
| -f ref="refs/tags/$TAG_NAME" \ | |
| -f sha="$MERGE_SHA" | |
| fi | |
| # Create release if missing | |
| if ! gh release view $TAG_NAME --repo ${{ github.repository }} 2>/dev/null; then | |
| echo "π Creating GitHub Release $TAG_NAME" | |
| CHANGELOG=$(awk -v ver="$VERSION" ' | |
| /^## \[/ { | |
| if (found) exit | |
| if ($0 ~ "## \\[" ver "\\]") found=1 | |
| } | |
| found { print } | |
| ' CHANGELOG.md | tail -n +2) | |
| if [ -z "$CHANGELOG" ]; then | |
| CHANGELOG="Release v$VERSION - See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/CHANGELOG.md) for details." | |
| fi | |
| { | |
| echo "## π¦ Installation" | |
| echo "" | |
| echo "**Dart 3.9+ (recommended):**" | |
| echo "\`\`\`yaml" | |
| echo "dependencies:" | |
| echo " grpc:" | |
| echo " git:" | |
| echo " url: https://github.com/open-runtime/grpc-dart" | |
| echo " tag_pattern: \"^v\"" | |
| echo " version: ^$VERSION" | |
| echo "\`\`\`" | |
| echo "" | |
| echo "---" | |
| echo "" | |
| echo "$CHANGELOG" | |
| } > /tmp/release_notes.md | |
| gh release create $TAG_NAME \ | |
| --repo ${{ github.repository }} \ | |
| --title "v$VERSION" \ | |
| --notes-file /tmp/release_notes.md | |
| fi | |
| # Update label | |
| gh pr edit $PR_NUM \ | |
| --repo ${{ github.repository }} \ | |
| --remove-label "autorelease: pending" \ | |
| --add-label "autorelease: tagged" || true | |
| echo "β Processed PR #$PR_NUM" | |
| done < <(echo "$PENDING_PRS" | jq -c '.') | |
| echo "β Final check complete - all untagged PRs processed" | |
| - name: π€ Run Release Please | |
| id: release | |
| uses: googleapis/release-please-action@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| config-file: release-please-config.json | |
| manifest-file: .release-please-manifest.json | |
| target-branch: ${{ github.ref_name }} | |
| - name: π Log Release Info | |
| if: ${{ steps.release.outputs.releases_created == 'true' }} | |
| run: | | |
| echo "π Release created!" | |
| echo "π¦ Version: ${{ steps.release.outputs.version }}" | |
| echo "π·οΈ Tag: ${{ steps.release.outputs.tag_name }}" | |
| echo "π https://github.com/${{ github.repository }}/releases/tag/${{ steps.release.outputs.tag_name }}" | |
| # ============================================================================= | |
| # Step 3: Verify Release | |
| # ============================================================================= | |
| # ============================================================================= | |
| # Verify Release | |
| # ============================================================================= | |
| verify-release: | |
| name: β Verify Release | |
| needs: [release-please, handle-untagged-releases] | |
| if: | | |
| needs.release-please.outputs.releases_created == 'true' || | |
| (needs.handle-untagged-releases.outputs.release_created == 'true' && needs.handle-untagged-releases.result == 'success') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: π₯ Checkout source code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.release-please.outputs.releases_created == 'true' && needs.release-please.outputs.tag_name || needs.handle-untagged-releases.outputs.tag_name }} | |
| - name: π― Install Dart SDK | |
| uses: dart-lang/setup-dart@v1 | |
| with: | |
| sdk: stable | |
| - name: π Get dependencies | |
| run: dart pub get | |
| - name: π Verify package | |
| run: | | |
| echo "π¦ Verifying package version..." | |
| VERSION=$(grep '^version:' pubspec.yaml | awk '{print $2}') | |
| EXPECTED="${{ needs.release-please.outputs.releases_created == 'true' && needs.release-please.outputs.version || needs.handle-untagged-releases.outputs.version }}" | |
| if [ "$VERSION" != "$EXPECTED" ]; then | |
| echo "β Version mismatch: pubspec.yaml has $VERSION, expected $EXPECTED" | |
| exit 1 | |
| fi | |
| echo "β Version verified: $VERSION" | |
| - name: π§Ή Run analysis | |
| run: dart analyze --fatal-infos . | |
| - name: π§ͺ Run tests | |
| run: dart test --platform vm | |
| - name: π Release Summary | |
| run: | | |
| VERSION="${{ needs.release-please.outputs.releases_created == 'true' && needs.release-please.outputs.version || needs.handle-untagged-releases.outputs.version }}" | |
| echo "## π Release v$VERSION Verified!" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### π¦ Installation" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Dart 3.9+ with tag_pattern (recommended):**" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`yaml" >> $GITHUB_STEP_SUMMARY | |
| echo "dependencies:" >> $GITHUB_STEP_SUMMARY | |
| echo " grpc:" >> $GITHUB_STEP_SUMMARY | |
| echo " git:" >> $GITHUB_STEP_SUMMARY | |
| echo " url: https://github.com/open-runtime/grpc-dart" >> $GITHUB_STEP_SUMMARY | |
| echo " tag_pattern: \"^v\"" >> $GITHUB_STEP_SUMMARY | |
| echo " version: ^$VERSION" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### π Links" >> $GITHUB_STEP_SUMMARY | |
| echo "- [GitHub Release](https://github.com/${{ github.repository }}/releases/tag/v$VERSION)" >> $GITHUB_STEP_SUMMARY | |
| echo "- [Changelog](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/CHANGELOG.md)" >> $GITHUB_STEP_SUMMARY |