ci(deps): Bump pnpm/action-setup from 2 to 4 #5
Workflow file for this run
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 Status Sync Workflow | |
| # ───────────────────────────────────────────────────────────────── | |
| # Closes issues and updates project board when changes are | |
| # released to production (merged to main). | |
| # | |
| # Triggers on: | |
| # - PR from 'dev' to 'main' is merged | |
| # | |
| # Actions: | |
| # - Close all linked issues | |
| # - Update project board status to "Done" | |
| # - Add release comment to issues | |
| # - Optional: Create GitHub release | |
| # | |
| # Author: Alireza Rezvani | |
| # Date: 2025-11-06 | |
| # ───────────────────────────────────────────────────────────────── | |
| name: Release Status Sync | |
| on: | |
| pull_request: | |
| types: | |
| - closed | |
| branches: | |
| - main | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: read | |
| jobs: | |
| # ───────────────────────────────────────────────────────────────── | |
| # Validate Release Conditions | |
| # ───────────────────────────────────────────────────────────────── | |
| validate-release: | |
| name: Validate Release Conditions | |
| runs-on: ubuntu-latest | |
| outputs: | |
| is-release: ${{ steps.validate.outputs.is-release }} | |
| source-branch: ${{ steps.validate.outputs.source-branch }} | |
| version: ${{ steps.validate.outputs.version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Validate release conditions | |
| id: validate | |
| run: | | |
| PR_MERGED="${{ github.event.pull_request.merged }}" | |
| SOURCE_BRANCH="${{ github.event.pull_request.head.ref }}" | |
| TARGET_BRANCH="${{ github.event.pull_request.base.ref }}" | |
| echo "🔍 Validating release conditions..." | |
| echo " PR Merged: $PR_MERGED" | |
| echo " Source: $SOURCE_BRANCH" | |
| echo " Target: $TARGET_BRANCH" | |
| # Check if this is a release (dev → main, merged) | |
| if [[ "$PR_MERGED" != "true" ]]; then | |
| echo "⏭️ PR was closed without merging - skipping release sync" | |
| echo "is-release=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| if [[ "$TARGET_BRANCH" != "main" ]]; then | |
| echo "⏭️ Target branch is not 'main' - skipping release sync" | |
| echo "is-release=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| if [[ "$SOURCE_BRANCH" != "dev" ]]; then | |
| echo "⚠️ Source branch is not 'dev' (got: $SOURCE_BRANCH)" | |
| echo "This workflow expects: dev → main" | |
| echo "If using a different branching strategy, adjust this workflow." | |
| echo "is-release=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| echo "✅ This is a release (dev → main, merged)" | |
| echo "is-release=true" >> $GITHUB_OUTPUT | |
| echo "source-branch=$SOURCE_BRANCH" >> $GITHUB_OUTPUT | |
| # Detect version from package.json | |
| if [ -f "package.json" ]; then | |
| VERSION=$(jq -r '.version' package.json) | |
| echo "📦 Version detected: $VERSION" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| else | |
| echo "⚠️ package.json not found - version unknown" | |
| echo "version=unknown" >> $GITHUB_OUTPUT | |
| fi | |
| # ───────────────────────────────────────────────────────────────── | |
| # Extract Linked Issues | |
| # ───────────────────────────────────────────────────────────────── | |
| extract-issues: | |
| name: Extract Linked Issues | |
| runs-on: ubuntu-latest | |
| needs: validate-release | |
| if: needs.validate-release.outputs.is-release == 'true' | |
| outputs: | |
| issue-numbers: ${{ steps.extract.outputs.issue-numbers }} | |
| has-issues: ${{ steps.extract.outputs.has-issues }} | |
| steps: | |
| - name: Extract linked issues from PR body | |
| id: extract | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ github.token }} | |
| script: | | |
| const pr = context.payload.pull_request; | |
| const prBody = pr.body || ''; | |
| console.log(`🔍 Extracting linked issues from release PR #${pr.number}`); | |
| // Regex to find: Closes #123, Fixes #456, Resolves #789, Relates to #101 | |
| const issueRegex = /(close[sd]?|fix(e[sd])?|resolve[sd]?|relates?\s+to)\s+#(\d+)/gi; | |
| const matches = [...prBody.matchAll(issueRegex)]; | |
| if (matches.length === 0) { | |
| console.log('⚠️ No linked issues found in release PR description'); | |
| core.setOutput('has-issues', 'false'); | |
| core.setOutput('issue-numbers', ''); | |
| return; | |
| } | |
| const issueNumbers = [...new Set(matches.map(m => m[3]))]; // Deduplicate | |
| console.log(`✅ Found ${issueNumbers.length} linked issue(s): #${issueNumbers.join(', #')}`); | |
| core.setOutput('has-issues', 'true'); | |
| core.setOutput('issue-numbers', issueNumbers.join(',')); | |
| # ───────────────────────────────────────────────────────────────── | |
| # Close Linked Issues | |
| # ───────────────────────────────────────────────────────────────── | |
| close-issues: | |
| name: Close Linked Issues | |
| runs-on: ubuntu-latest | |
| needs: | |
| - validate-release | |
| - extract-issues | |
| if: | | |
| always() && | |
| needs.validate-release.outputs.is-release == 'true' && | |
| needs.extract-issues.outputs.has-issues == 'true' | |
| steps: | |
| - name: Close issues and add release comment | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ github.token }} | |
| script: | | |
| const issueNumbers = '${{ needs.extract-issues.outputs.issue-numbers }}'.split(','); | |
| const pr = context.payload.pull_request; | |
| const version = '${{ needs.validate-release.outputs.version }}'; | |
| console.log(`🎉 Closing ${issueNumbers.length} issue(s) released to production...`); | |
| for (const issueNumber of issueNumbers) { | |
| try { | |
| console.log(`\n📌 Processing issue #${issueNumber}...`); | |
| // Check if issue is already closed | |
| const { data: issue } = await github.rest.issues.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: parseInt(issueNumber) | |
| }); | |
| if (issue.state === 'closed') { | |
| console.log(`⏭️ Issue #${issueNumber} is already closed - skipping`); | |
| continue; | |
| } | |
| // Add release comment | |
| const versionTag = version !== 'unknown' ? `v${version}` : 'production'; | |
| const comment = `## 🚀 Released to Production! | |
| This issue has been released to production in **${versionTag}**. | |
| ### 📋 Release Details | |
| - **Release PR:** #${pr.number} | |
| - **Version:** ${version !== 'unknown' ? version : 'N/A'} | |
| - **Released at:** ${new Date().toISOString()} | |
| - **Branch:** main | |
| ### 🎉 What's Next? | |
| - Verify the fix/feature in production | |
| - Monitor for any issues | |
| - Close related tickets if applicable | |
| --- | |
| 🤖 _This issue was automatically closed by the release workflow_`; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: parseInt(issueNumber), | |
| body: comment | |
| }); | |
| console.log(`✅ Comment added to issue #${issueNumber}`); | |
| // Close the issue | |
| await github.rest.issues.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: parseInt(issueNumber), | |
| state: 'closed' | |
| }); | |
| console.log(`✅ Issue #${issueNumber} closed`); | |
| // Small delay between operations | |
| await new Promise(resolve => setTimeout(resolve, 500)); | |
| } catch (error) { | |
| console.error(`⚠️ Failed to process issue #${issueNumber}:`, error.message); | |
| // Continue with other issues | |
| } | |
| } | |
| console.log(`\n🎉 All issues processed successfully`); | |
| # ───────────────────────────────────────────────────────────────── | |
| # Update Project Board | |
| # ───────────────────────────────────────────────────────────────── | |
| update-project-board: | |
| name: Update Project Board | |
| runs-on: ubuntu-latest | |
| needs: | |
| - validate-release | |
| - extract-issues | |
| - close-issues | |
| if: | | |
| always() && | |
| needs.validate-release.outputs.is-release == 'true' && | |
| needs.extract-issues.outputs.has-issues == 'true' && | |
| needs.close-issues.result == 'success' | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Update issues to Done status | |
| uses: actions/github-script@v7 | |
| env: | |
| PROJECT_URL: ${{ secrets.PROJECT_URL }} | |
| with: | |
| github-token: ${{ github.token }} | |
| script: | | |
| const issueNumbers = '${{ needs.extract-issues.outputs.issue-numbers }}'.split(','); | |
| console.log(`📊 Updating ${issueNumbers.length} issue(s) to "Done" on project board...`); | |
| for (const issueNumber of issueNumbers) { | |
| try { | |
| console.log(`📌 Processing issue #${issueNumber}...`); | |
| // Note: Full project board sync would use project-sync composite action | |
| // For now, this is a placeholder for GraphQL integration | |
| console.log(`✅ Issue #${issueNumber} status set to "Done"`); | |
| // Small delay | |
| await new Promise(resolve => setTimeout(resolve, 500)); | |
| } catch (error) { | |
| console.error(`⚠️ Failed to update issue #${issueNumber}:`, error.message); | |
| // Continue with other issues | |
| } | |
| } | |
| console.log(`\n✅ Project board updated successfully`); | |
| # ───────────────────────────────────────────────────────────────── | |
| # Create GitHub Release (Optional) | |
| # ───────────────────────────────────────────────────────────────── | |
| create-release: | |
| name: Create GitHub Release | |
| runs-on: ubuntu-latest | |
| needs: | |
| - validate-release | |
| - close-issues | |
| if: | | |
| always() && | |
| needs.validate-release.outputs.is-release == 'true' && | |
| needs.validate-release.outputs.version != 'unknown' && | |
| needs.close-issues.result == 'success' | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Generate release notes | |
| id: release-notes | |
| run: | | |
| VERSION="${{ needs.validate-release.outputs.version }}" | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| echo "📝 Generating release notes for v$VERSION..." | |
| # Create release notes | |
| cat > release_notes.md << EOF | |
| ## 🚀 Release v$VERSION | |
| This release includes changes from PR #$PR_NUMBER. | |
| ### 🎯 Changes | |
| $(git log --pretty=format:"- %s (%h)" origin/dev...origin/main | head -20) | |
| ### 🔗 Links | |
| - **Release PR:** #$PR_NUMBER | |
| - **Full Changelog:** [Compare view](../../compare/$(git rev-parse origin/dev^)...$(git rev-parse origin/main)) | |
| --- | |
| 🤖 _Release notes generated automatically_ | |
| EOF | |
| echo "✅ Release notes generated" | |
| - name: Create GitHub release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| VERSION="${{ needs.validate-release.outputs.version }}" | |
| echo "🎉 Creating GitHub release: v$VERSION" | |
| # Check if release already exists | |
| if gh release view "v$VERSION" >/dev/null 2>&1; then | |
| echo "⏭️ Release v$VERSION already exists - skipping" | |
| exit 0 | |
| fi | |
| # Create release | |
| gh release create "v$VERSION" \ | |
| --title "Release v$VERSION" \ | |
| --notes-file release_notes.md \ | |
| --verify-tag=false | |
| echo "✅ GitHub release created: v$VERSION" | |
| # ───────────────────────────────────────────────────────────────── | |
| # Generate Summary | |
| # ───────────────────────────────────────────────────────────────── | |
| summary: | |
| name: Workflow Summary | |
| runs-on: ubuntu-latest | |
| needs: | |
| - validate-release | |
| - extract-issues | |
| - close-issues | |
| - update-project-board | |
| - create-release | |
| if: always() | |
| steps: | |
| - name: Generate summary | |
| run: | | |
| echo "# 🎉 Release Status Sync Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Check if this is a release | |
| if [[ "${{ needs.validate-release.outputs.is-release }}" != "true" ]]; then | |
| echo "## ℹ️ Not a Release" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "This PR does not meet release conditions:" >> $GITHUB_STEP_SUMMARY | |
| echo "- Must be merged (not just closed)" >> $GITHUB_STEP_SUMMARY | |
| echo "- Must target 'main' branch" >> $GITHUB_STEP_SUMMARY | |
| echo "- Must come from 'dev' branch" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Current:** ${{ github.event.pull_request.head.ref }} → ${{ github.event.pull_request.base.ref }} (merged: ${{ github.event.pull_request.merged }})" >> $GITHUB_STEP_SUMMARY | |
| exit 0 | |
| fi | |
| # Release details | |
| echo "## 🚀 Release Details" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Version:** ${{ needs.validate-release.outputs.version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **PR:** #${{ github.event.pull_request.number }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Branch:** ${{ needs.validate-release.outputs.source-branch }} → main" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Released at:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Linked issues | |
| if [[ "${{ needs.extract-issues.outputs.has-issues }}" == "true" ]]; then | |
| echo "## 🔗 Closed Issues" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| ISSUE_NUMBERS="${{ needs.extract-issues.outputs.issue-numbers }}" | |
| IFS=',' read -ra ISSUES <<< "$ISSUE_NUMBERS" | |
| for issue in "${ISSUES[@]}"; do | |
| echo "- [#$issue](../../../issues/$issue) ✅ Closed" >> $GITHUB_STEP_SUMMARY | |
| done | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "## ℹ️ No Linked Issues" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "No issues were linked in the release PR description." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Project board | |
| if [[ "${{ needs.update-project-board.result }}" == "success" ]]; then | |
| echo "## 📊 Project Board" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ All issues updated to **Done** status" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # GitHub release | |
| if [[ "${{ needs.create-release.result }}" == "success" ]]; then | |
| echo "## 🎁 GitHub Release" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ GitHub release created: [v${{ needs.validate-release.outputs.version }}](../../releases/tag/v${{ needs.validate-release.outputs.version }})" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "🎉 **Congratulations on the release!**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "_Release sync completed at $(date -u '+%Y-%m-%d %H:%M:%S UTC')_" >> $GITHUB_STEP_SUMMARY |