Added workflow for the PR #3
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
name: Auto Label PR from Linked Issue | |
on: | |
pull_request: | |
types: [opened, edited, synchronize, reopened] | |
permissions: | |
pull-requests: write | |
issues: read | |
jobs: | |
label-pr: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Extract Issue Numbers from PR Body | |
id: extract-issues | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
result-encoding: string | |
script: | | |
const prBody = context.payload.pull_request.body || ''; | |
const prTitle = context.payload.pull_request.title || ''; | |
// Regex patterns to find linked issues | |
// Matches: #123, fixes #123, closes #123, resolves #123, etc. | |
const patterns = [ | |
/(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi, | |
/#(\d+)/g | |
]; | |
const issueNumbers = new Set(); | |
// Search in PR body and title | |
const textToSearch = prBody + ' ' + prTitle; | |
patterns.forEach(pattern => { | |
const matches = [...textToSearch.matchAll(pattern)]; | |
matches.forEach(match => { | |
issueNumbers.add(match[1]); | |
}); | |
}); | |
const issues = Array.from(issueNumbers); | |
console.log('Found linked issues:', issues); | |
// Return as JSON string | |
return JSON.stringify(issues); | |
- name: Get Labels from Linked Issues | |
id: get-labels | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
result-encoding: string | |
script: | | |
const issueNumbers = JSON.parse('${{ steps.extract-issues.outputs.result }}'); | |
// Labels to exclude from being applied to PRs | |
const excludedLabels = ['recode', 'hacktoberfest-accepted']; | |
if (!issueNumbers || issueNumbers.length === 0) { | |
console.log('No linked issues found'); | |
return JSON.stringify([]); | |
} | |
const allLabels = new Set(); | |
for (const issueNumber of issueNumbers) { | |
try { | |
const issue = await github.rest.issues.get({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: parseInt(issueNumber) | |
}); | |
console.log(`Issue #${issueNumber} labels:`, issue.data.labels.map(l => l.name)); | |
issue.data.labels.forEach(label => { | |
// Only add label if it's not in the excluded list | |
if (!excludedLabels.includes(label.name.toLowerCase())) { | |
allLabels.add(label.name); | |
} else { | |
console.log(`Excluding label: ${label.name}`); | |
} | |
}); | |
} catch (error) { | |
console.log(`Could not fetch issue #${issueNumber}:`, error.message); | |
} | |
} | |
const labels = Array.from(allLabels); | |
console.log('All labels to apply:', labels); | |
return JSON.stringify(labels); | |
- name: Apply Labels to PR | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const labels = ${{ steps.get-labels.outputs.result }}; | |
if (!labels || labels.length === 0) { | |
console.log('No labels to apply'); | |
return; | |
} | |
try { | |
await github.rest.issues.addLabels({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: context.payload.pull_request.number, | |
labels: labels | |
}); | |
console.log(`Successfully applied ${labels.length} labels to PR #${context.payload.pull_request.number}`); | |
// Add a comment to the PR | |
await github.rest.issues.createComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: context.payload.pull_request.number, | |
body: `🏷️ Labels automatically applied from linked issue(s): ${labels.map(l => `\`${l}\``).join(', ')}` | |
}); | |
} catch (error) { | |
console.error('Error applying labels:', error.message); | |
core.setFailed(`Failed to apply labels: ${error.message}`); | |
} |