name: 🔀 Pull Request Manager on: workflow_dispatch: # Allow to manually call this workflow pull_request_target: types: [opened, synchronize, reopened, edited, labeled, unlabeled, closed] pull_request_review_comment: types: [created] permissions: contents: read pull-requests: write jobs: label-by-size: name: 🏷️ Label PR by Size # This job should run after all others, to prevent possible concurrency issues needs: [label-by-branches, label-by-files, label-copilot-prs, remove-stale-label, check-merge-blocking-labels, write-auto-comments] runs-on: ubuntu-latest # Only needs to run when code is changed if: always() && (github.event.action == 'opened' || github.event.action == 'synchronize') # Override permissions, the labeler needs issues write access permissions: contents: read issues: write pull-requests: write steps: - name: Mint App Token id: app # Create a GitHub App token # https://github.com/marketplace/actions/create-github-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.ST_BOT_APP_ID }} private-key: ${{ secrets.ST_BOT_PRIVATE_KEY }} owner: ${{ github.repository_owner }} - name: Label PR Size # Pull Request Size Labeler # https://github.com/marketplace/actions/pull-request-size-labeler uses: codelytv/pr-size-labeler@v1.10.2 with: GITHUB_TOKEN: ${{ steps.app.outputs.token }} xs_label: '🟩 ⬤○○○○' xs_max_size: '20' s_label: '🟩 ⬤⬤○○○' s_max_size: '100' m_label: '🟨 ⬤⬤⬤○○' m_max_size: '500' l_label: '🟧 ⬤⬤⬤⬤○' l_max_size: '1000' xl_label: '🟥 ⬤⬤⬤⬤⬤' fail_if_xl: 'false' files_to_ignore: | "package-lock.json" "public/lib/*" label-by-branches: name: 🏷️ Label PR by Branches runs-on: ubuntu-latest # Only label once when PR is created or when base branch is changed, to allow manual label removal if: always() && (github.event.action == 'opened' || (github.event.action == 'synchronize' && github.event.changes.base)) steps: - name: Mint App Token id: app # Create a GitHub App token # https://github.com/marketplace/actions/create-github-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.ST_BOT_APP_ID }} private-key: ${{ secrets.ST_BOT_PRIVATE_KEY }} owner: ${{ github.repository_owner }} - name: Checkout Repository # Checkout # https://github.com/marketplace/actions/checkout uses: actions/checkout@v4.2.2 - name: Apply Labels Based on Branch Name and Target Branch # Pull Request Labeler # https://github.com/marketplace/actions/labeler uses: actions/labeler@v5.0.0 with: configuration-path: .github/pr-auto-labels-by-branch.yml repo-token: ${{ steps.app.outputs.token }} label-by-files: name: 🏷️ Label PR by Files needs: [label-by-branches] runs-on: ubuntu-latest # Only needs to run when code is changed if: always() && (github.event.action == 'opened' || github.event.action == 'synchronize') steps: - name: Mint App Token id: app # Create a GitHub App token # https://github.com/marketplace/actions/create-github-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.ST_BOT_APP_ID }} private-key: ${{ secrets.ST_BOT_PRIVATE_KEY }} owner: ${{ github.repository_owner }} - name: Checkout Repository # Checkout # https://github.com/marketplace/actions/checkout uses: actions/checkout@v4.2.2 - name: Apply Labels Based on Changed Files # Pull Request Labeler # https://github.com/marketplace/actions/labeler uses: actions/labeler@v5.0.0 env: GITHUB_TOKEN: ${{ steps.app.outputs.token }} # labeler action needs some handholding with: configuration-path: .github/pr-auto-labels-by-files.yml repo-token: ${{ steps.app.outputs.token }} label-copilot-prs: name: 🏷️ Label Copilot PRs as Vibe Coded runs-on: ubuntu-latest # Only label once when PR is created by Copilot if: > github.event.action == 'opened' && github.event.pull_request.user.login == 'Copilot' steps: - name: Mint App Token id: app # Create a GitHub App token # https://github.com/marketplace/actions/create-github-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.ST_BOT_APP_ID }} private-key: ${{ secrets.ST_BOT_PRIVATE_KEY }} owner: ${{ github.repository_owner }} - name: Add Vibe Coded Label # 🤖 Issues Helper # https://github.com/marketplace/actions/issues-helper uses: actions-cool/issues-helper@v3.6.0 with: actions: 'add-labels' token: ${{ steps.app.outputs.token }} issue-number: ${{ github.event.pull_request.number }} labels: '✨ Vibe Coded' remove-stale-label: name: 🗑️ Remove Stale Label on Comment needs: [label-by-branches, label-by-files, label-copilot-prs] runs-on: ubuntu-latest # Only runs on comments not done by the github actions bot if: always() && (github.event_name == 'pull_request_review_comment' && github.event.sender.type != 'Bot') # Override permissions, issue labeler needs issues write access permissions: contents: read issues: write pull-requests: write steps: - name: Mint App Token if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} id: app # Only run if the PR is from the same repository # This action runs on comments, which will not receive the env vars for this # Create a GitHub App token # https://github.com/marketplace/actions/create-github-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.ST_BOT_APP_ID }} private-key: ${{ secrets.ST_BOT_PRIVATE_KEY }} owner: ${{ github.repository_owner }} - name: Remove Stale Label if: always() # 🤖 Issues Helper # https://github.com/marketplace/actions/issues-helper uses: actions-cool/issues-helper@v3.6.0 with: actions: 'remove-labels' token: ${{ steps.app.outputs.token || github.token }} # Use fallback to GITHUB_TOKEN if app token is not available issue-number: ${{ github.event.pull_request.number }} labels: '⚰️ Stale' check-merge-blocking-labels: name: 🚫 Check Merge Blocking Labels needs: [label-by-branches, label-by-files, label-copilot-prs, remove-stale-label] runs-on: ubuntu-latest # Run, even if the previous jobs were skipped/failed if: always() # Override permissions, as this needs to write a check permissions: checks: write contents: read pull-requests: read steps: - name: Check Merge Blocking # GitHub Script # https://github.com/marketplace/actions/github-script id: label-check uses: actions/github-script@v7.0.1 with: script: | const prLabels = context.payload.pull_request.labels.map(label => label.name); const blockingLabels = [ "⛔ Don't Merge", "🔨 Needs Work", "🔬 Needs Testing", "⛔ Waiting For External/Upstream", "❗ Against Release Branch", "💥💣 Breaking Changes" ]; const hasBlockingLabel = prLabels.some(label => blockingLabels.includes(label)); if (hasBlockingLabel) { console.log("Blocking label detected. Setting warning status."); await github.rest.checks.create({ owner: context.repo.owner, repo: context.repo.repo, name: "PR Label Warning", head_sha: context.payload.pull_request.head.sha, status: "completed", conclusion: "neutral", output: { title: "Potential Merge Issue", summary: "This PR has a merge-blocking label. Proceed with caution." } }); } else { console.log("No merge-blocking labels found."); } write-auto-comments: name: 💬 Post PR Comments Based on Labels needs: [label-by-branches, label-by-files, label-copilot-prs, remove-stale-label, check-merge-blocking-labels] runs-on: ubuntu-latest # Run, even if the previous jobs were skipped/failed if: always() && (github.event_name == 'pull_request_target') steps: - name: Mint App Token id: app # Create a GitHub App token # https://github.com/marketplace/actions/create-github-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.ST_BOT_APP_ID }} private-key: ${{ secrets.ST_BOT_PRIVATE_KEY }} owner: ${{ github.repository_owner }} - name: Checkout Repository # Checkout # https://github.com/marketplace/actions/checkout uses: actions/checkout@v4.2.2 - name: Post PR Comments Based on Labels # Label Commenter for PRs # https://github.com/marketplace/actions/label-commenter uses: peaceiris/actions-label-commenter@v1.10.0 with: config_file: .github/pr-auto-comments.yml github_token: ${{ steps.app.outputs.token }} # This runs on merged PRs to staging, reading the PR body and directly linked issues. Check `issues-updates-on-merge.yml`:`update-linked-issues` for commit-based updates. update-linked-issues: name: 🔗 Mark Linked Issues Done on Staging Merge runs-on: ubuntu-latest if: > always() && github.event_name == 'pull_request_target' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'staging' # Override permissions, We need to be able to write to issues permissions: contents: read issues: write pull-requests: write steps: - name: Mint App Token id: app # Create a GitHub App token # https://github.com/marketplace/actions/create-github-app-token uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.ST_BOT_APP_ID }} private-key: ${{ secrets.ST_BOT_PRIVATE_KEY }} owner: ${{ github.repository_owner }} - name: Extract Linked Issues From PR Description id: extract_issues run: | ISSUES=$(jq -r '.pull_request.body' "$GITHUB_EVENT_PATH" | grep -oiE '(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved) #([0-9]+)' | awk '{print $2}' | tr -d '#' | jq -R -s -c 'split("\n")[:-1]') echo "issues=$ISSUES" >> $GITHUB_ENV - name: Fetch Directly Linked Issues id: fetch_linked_issues run: | PR_NUMBER=${{ github.event.pull_request.number }} REPO=${{ github.repository }} API_URL="https://api.github.com/repos/$REPO/pulls/$PR_NUMBER/issues" ISSUES=$(curl -s -H "Authorization: token ${{ steps.app.outputs.token }}" "$API_URL" | jq -r '.[].number' | jq -R -s -c 'split("\n")[:-1]') echo "linked_issues=$ISSUES" >> $GITHUB_ENV - name: Merge Issue Lists id: merge_issues run: | ISSUES=$(jq -c -n --argjson a "$issues" --argjson b "$linked_issues" '$a + $b | unique') echo "final_issues=$ISSUES" >> $GITHUB_ENV - name: Label Linked Issues id: label_linked_issues env: GH_TOKEN: ${{ steps.app.outputs.token }} run: | for ISSUE in $(echo $final_issues | jq -r '.[]'); do gh issue edit $ISSUE -R ${{ github.repository }} --add-label "✅ Done (staging)" --remove-label "🧑‍💻 In Progress" echo "Added label '✅ Done (staging)' (and removed '🧑‍💻 In Progress' if present) in issue #$ISSUE" done