Skip to content
94 changes: 47 additions & 47 deletions .github/workflows/autolabel-pr-issue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ jobs:
runs-on: ubuntu-latest

steps:
# Step 1: Checkout repository
- name: Checkout
uses: actions/checkout@v4

# Step 2: Extract linked issues from PR
- name: Extract linked issue(s) from PR
id: extract-issues
uses: actions/github-script@v7
Expand All @@ -28,7 +30,7 @@ jobs:
const prTitle = context.payload.pull_request.title || '';
const prBody = context.payload.pull_request.body || '';

// Regex patterns for issue references
// Regex patterns to find linked issues
const patterns = [
/(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi,
/#(\d+)/g
Expand All @@ -43,50 +45,65 @@ jobs:
}
}

return JSON.stringify({ issues: Array.from(issueNumbers), pr: prNumber });
core.setOutput('issues', JSON.stringify(Array.from(issueNumbers)));
core.setOutput('pr', prNumber.toString());

# Step 3: Sync issue metadata to PR safely
- name: Sync Issue Metadata to PR
if: steps.extract-issues.outputs.issues != '' && steps.extract-issues.outputs.issues != '[]'
uses: actions/github-script@v7
env:
ISSUES_JSON: ${{ steps.extract-issues.outputs.issues }}
PR_NUMBER: ${{ steps.extract-issues.outputs.pr }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const data = JSON.parse('${{ steps.extract-issues.outputs.result }}');
const prNumber = data.pr;
const issueNumbers = data.issues || [];
// Read inputs from environment variables to avoid YAML quoting issues
const issuesOutput = process.env.ISSUES_JSON || '[]';
const prNumber = parseInt(process.env.PR_NUMBER, 10);

let issues = [];
try {
issues = JSON.parse(issuesOutput);
} catch (e) {
// Clean extra quotes/newlines
const cleaned = issuesOutput.replace(/^\s*['"]?/, '').replace(/['"]?\s*$/, '');
try {
issues = JSON.parse(cleaned);
} catch (err) {
console.error('Failed to parse issues output:', err.message);
console.error('Raw issues output:', JSON.stringify(issuesOutput));
return;
}
}

if (issueNumbers.length === 0) {
console.log("No linked issues found");
if (!Array.isArray(issues) || issues.length === 0) {
console.log('No linked issues found');
return;
}

for (const issueNumber of issueNumbers) {
for (const issueNumber of issues) {
try {
// Fetch issue
// Fetch issue details
const { data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt(issueNumber)
issue_number: parseInt(issueNumber, 10)
});

console.log(`Syncing metadata from Issue #${issueNumber} to PR #${prNumber}`);

// --- Sync Labels ---
const issueLabels = issue.labels.map(l => l.name);
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
const currentPRLabels = pr.labels.map(l => l.name);
const combinedLabels = Array.from(new Set([...currentPRLabels, ...issueLabels]));

await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: combinedLabels
});
console.log(`Labels applied: ${combinedLabels.join(', ')}`);
// --- Sync Labels safely using addLabels ---
const issueLabels = Array.isArray(issue.labels) ? issue.labels.map(l => l.name) : [];
if (issueLabels.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: issueLabels
});
console.log(`Labels added from issue: ${issueLabels.join(', ')}`);
}

// --- Sync Milestone ---
if (issue.milestone) {
Expand All @@ -99,32 +116,15 @@ jobs:
console.log(`Milestone synced: ${issue.milestone.title}`);
}

// --- Sync Projects (GitHub Projects v2) ---
if(issue.project_cards_url) {
// Fetch project cards of issue
const cardsResponse = await github.rest.projects.listCards({
column_id: issue.project_cards_url.split('/').pop() // last part is column_id
}).catch(()=>({data:[]}));

for(const card of cardsResponse.data || []) {
await github.rest.projects.createCard({
column_id: card.column_id,
content_id: prNumber,
content_type: 'PullRequest'
});
console.log(`Added PR #${prNumber} to project card in column ${card.column_id}`);
}
}

// --- Optionally: Add a comment on PR ---
// --- Add comment on PR ---
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: `✅ Synchronized metadata from Issue #${issueNumber}:\nLabels: ${issueLabels.join(', ')}\nMilestone: ${issue.milestone ? issue.milestone.title : 'None'}`
body: `✅ Synchronized metadata from Issue #${issueNumber}:\n- Labels: ${issueLabels.length > 0 ? issueLabels.join(', ') : 'None'}\n- Milestone: ${issue.milestone ? issue.milestone.title : 'None'}`
});

} catch (error) {
console.error(`Error syncing issue #${issueNumber} to PR #${prNumber}:`, error.message);
console.error(`Error syncing issue #${issueNumber} to PR #${prNumber}:`, error);
}
}
Loading