From 79d342f084005c7f9233dc0e7610d5d23cea0d15 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Mon, 9 Mar 2026 14:06:30 +0000 Subject: [PATCH] feat: Add reusable PR auto-labeler workflow Adds a workflow_call workflow that parses checked checkboxes in PR descriptions and applies/removes corresponding GitHub labels. Label mapping is configurable via JSON input so any repo can use its own PR template fields. - Passes label_mapping via env var to avoid JS interpolation issues - Handles 403 gracefully for fork PRs with insufficient permissions - Includes test workflow with sample rippled-style mappings Co-Authored-By: Claude Opus 4.6 --- .github/workflows/pr-auto-labeler.yml | 78 ++++++++++++++++++++++ .github/workflows/test-pr-auto-labeler.yml | 24 +++++++ 2 files changed, 102 insertions(+) create mode 100644 .github/workflows/pr-auto-labeler.yml create mode 100644 .github/workflows/test-pr-auto-labeler.yml diff --git a/.github/workflows/pr-auto-labeler.yml b/.github/workflows/pr-auto-labeler.yml new file mode 100644 index 0000000..044625c --- /dev/null +++ b/.github/workflows/pr-auto-labeler.yml @@ -0,0 +1,78 @@ +name: PR Auto-Labeler + +on: + workflow_call: + inputs: + label_mapping: + description: > + JSON object mapping regex patterns (matched against PR body) to label names. + Example: {"- \\[x\\] Bug fix": "bug fix", "- \\[x\\] New feature": "new feature"} + required: true + type: string + +defaults: + run: + shell: bash + +jobs: + label: + runs-on: ubuntu-latest + + permissions: + pull-requests: write + + steps: + - name: Auto-label PR based on checked boxes + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + LABEL_MAPPING: ${{ inputs.label_mapping }} + with: + script: | + const body = context.payload.pull_request.body || ''; + const mapping = JSON.parse(process.env.LABEL_MAPPING); + + const toAdd = []; + const toRemove = []; + + for (const [pattern, label] of Object.entries(mapping)) { + const regex = new RegExp(pattern, 'i'); + if (regex.test(body)) { + toAdd.push(label); + } else { + toRemove.push(label); + } + } + + core.info(`Labels to add: ${toAdd.join(', ') || 'none'}`); + core.info(`Labels to remove: ${toRemove.join(', ') || 'none'}`); + + try { + if (toAdd.length) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: toAdd, + }); + } + + for (const label of toRemove) { + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + name: label, + }); + } catch (e) { + if (e.status !== 404) throw e; + } + } + } catch (e) { + if (e.status === 403) { + core.warning('Insufficient permissions to modify labels. ' + + 'Ensure the caller workflow grants pull-requests: write.'); + } else { + throw e; + } + } diff --git a/.github/workflows/test-pr-auto-labeler.yml b/.github/workflows/test-pr-auto-labeler.yml new file mode 100644 index 0000000..634b9f6 --- /dev/null +++ b/.github/workflows/test-pr-auto-labeler.yml @@ -0,0 +1,24 @@ +on: + pull_request: + types: [opened, edited, reopened] + workflow_dispatch: + +permissions: + pull-requests: write + +jobs: + test-pr-auto-labeler: + uses: ./.github/workflows/pr-auto-labeler.yml + with: + label_mapping: >- + { + "- \\[x\\] Bug fix": "BugFix", + "- \\[x\\] New feature": "NewFeature", + "- \\[x\\] Breaking change": "BreakingChange", + "- \\[x\\] Refactor": "Refactor", + "- \\[x\\] Performance": "Performance", + "- \\[x\\] Tests": "Tests", + "- \\[x\\] Documentation update": "Documentation", + "- \\[x\\] Chore": "Chore", + "- \\[x\\] Release": "Release" + }