Skip to content

Commit 06f5101

Browse files
committed
chore: updated sync label compute
Signed-off-by: Manish Dait <daitmanish88@gmail.com>
1 parent f0669f1 commit 06f5101

1 file changed

Lines changed: 62 additions & 164 deletions

File tree

Lines changed: 62 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,105 @@
1-
name: Compute Linked Issue Labels
1+
name: Sync Linked Issue Labels - Compute
22

33
on:
44
pull_request:
5-
types: [opened, edited, reopened, synchronize, ready_for_review]
6-
workflow_dispatch:
7-
inputs:
8-
pr_number:
9-
description: "PR number to sync labels for"
10-
required: true
11-
type: number
12-
dry-run-enabled:
13-
description: "Dry run (log only, do not apply labels)"
14-
required: false
15-
type: boolean
16-
default: true
5+
types: [opened, edited, reopened, synchronize]
176

187
permissions:
19-
actions: write
20-
pull-requests: read
218
issues: read
229
contents: read
2310

11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
13+
cancel-in-progress: true
14+
2415
jobs:
25-
compute-labels:
26-
concurrency:
27-
group: sync-issue-labels-compute-pr-${{ github.event.pull_request.number || github.event.inputs.pr_number }}
28-
cancel-in-progress: true
16+
sync:
2917
runs-on: ubuntu-latest
30-
outputs:
31-
pr_number: ${{ steps.compute.outputs.pr_number }}
32-
dry_run: ${{ steps.compute.outputs.dry_run }}
33-
is_fork_pr: ${{ steps.compute.outputs.is_fork_pr }}
3418
steps:
35-
- name: Harden the runner
36-
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
19+
- name: Harden Runner
20+
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0
3721
with:
3822
egress-policy: audit
3923

40-
- name: Compute linked issue labels
24+
- name: Compute Labels
4125
id: compute
42-
env:
43-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44-
PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
45-
DRY_RUN: 'true'
46-
REQUESTED_DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && format('{0}', github.event.inputs['dry-run-enabled']) || 'true' }}
47-
IS_FORK_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork || 'false' }}
48-
MAX_LINKED_ISSUES: '20'
4926
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
5027
with:
51-
result-encoding: json
5228
script: |
53-
const MAX_LINKED_ISSUES = Number(process.env.MAX_LINKED_ISSUES || "20");
54-
55-
function extractLabels(labelData) {
56-
const result = [];
57-
for (const item of labelData) {
58-
const name = typeof item === "string" ? item : item && item.name;
59-
if (name && name.trim()) result.push(name.trim());
60-
}
61-
return result;
62-
}
63-
64-
function extractLinkedIssueNumbers(prBody, owner, repo) {
65-
const numbers = new Set();
66-
const closingRefRegex = /(?:fix|fixes|fixed|close|closes|closed|resolve|resolves|resolved)\s+(?:([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+))?#(\d+)\b/gi;
67-
const lines = String(prBody || "").split(/\r?\n/);
68-
for (const line of lines) {
69-
let m;
70-
while ((m = closingRefRegex.exec(line)) !== null) {
71-
const refOwner = (m[1] || "").toLowerCase();
72-
const refRepo = (m[2] || "").toLowerCase();
73-
if (refOwner && refRepo && (refOwner !== owner.toLowerCase() || refRepo !== repo.toLowerCase())) continue;
74-
numbers.add(Number(m[3]));
75-
}
76-
}
77-
const all = Array.from(numbers);
78-
if (all.length > MAX_LINKED_ISSUES) {
79-
console.log(`[sync] Limiting linked issue refs from ${all.length} to ${MAX_LINKED_ISSUES}.`);
80-
}
81-
return all.slice(0, MAX_LINKED_ISSUES);
82-
}
83-
84-
const prNumber = Number(process.env.PR_NUMBER);
85-
if (!prNumber) {
86-
core.setOutput('has_labels', 'false');
87-
core.setOutput('labels', '[]');
88-
core.setOutput('pr_number', '');
89-
core.setOutput('dry_run', 'true');
90-
core.setOutput('is_fork_pr', String(process.env.IS_FORK_PR || 'false'));
91-
core.setOutput('source_event', context.eventName);
92-
return;
93-
}
29+
const prNumber = context.payload.pull_request.number;
30+
console.log(`--- Processing PR #${prNumber} ---`);
9431
9532
const { data: prData } = await github.rest.pulls.get({
9633
owner: context.repo.owner, repo: context.repo.repo, pull_number: prNumber
9734
});
9835
99-
const prAuthor = (prData.user && prData.user.login) || "";
36+
const prAuthor = prData.user.login;
10037
if (/\[bot\]$/i.test(prAuthor) || /dependabot/i.test(prAuthor)) {
101-
console.log(`[sync] Skipping bot-authored PR from ${prAuthor}.`);
102-
core.setOutput('has_labels', 'false');
103-
core.setOutput('labels', '[]');
104-
core.setOutput('pr_number', String(prNumber));
105-
core.setOutput('dry_run', 'true');
106-
core.setOutput('is_fork_pr', String(process.env.IS_FORK_PR || 'false'));
107-
core.setOutput('source_event', context.eventName);
38+
console.log(`Skipping bot-authored PR (Author: ${prAuthor})`);
10839
return;
10940
}
11041
111-
const linkedIssues = extractLinkedIssueNumbers(prData.body || "", context.repo.owner, context.repo.repo);
112-
if (!linkedIssues.length) {
113-
console.log("[sync] No linked issue references found in PR body.");
114-
core.setOutput('has_labels', 'false');
115-
core.setOutput('labels', '[]');
116-
core.setOutput('pr_number', String(prNumber));
117-
core.setOutput('dry_run', 'true');
118-
core.setOutput('is_fork_pr', String(process.env.IS_FORK_PR || 'false'));
119-
core.setOutput('source_event', context.eventName);
42+
const regex = /(?:fix|fixes|fixed|close|closes|closed|resolve|resolves|resolved)[:\s]+\s*#(\d+)\b/gi;
43+
const issueNumbers = new Set();
44+
let match;
45+
while ((match = regex.exec(prData.body || "")) !== null) {
46+
issueNumbers.add(Number(match[1]));
47+
}
48+
49+
if (issueNumbers.size === 0) {
50+
console.log("No linked issues found in the PR description.");
12051
return;
12152
}
12253
123-
console.log(`[sync] Linked issues: ${linkedIssues.map(n => '#' + n).join(', ')}`);
54+
console.log(`Detected linked issues: #${Array.from(issueNumbers).join(', #')}`);
12455
125-
const allLabels = [];
126-
for (const num of linkedIssues) {
56+
const discoveredLabels = new Set();
57+
for (const num of issueNumbers) {
12758
try {
128-
const { data } = await github.rest.issues.get({
59+
const { data: issue } = await github.rest.issues.get({
12960
owner: context.repo.owner, repo: context.repo.repo, issue_number: num
13061
});
131-
if (data.pull_request) { console.log(`[sync] Skipping #${num}: is a PR reference.`); continue; }
132-
const labels = extractLabels(data.labels || []);
133-
console.log(`[sync] Issue #${num} labels: ${labels.length ? labels.join(', ') : '(none)'}`);
134-
allLabels.push(...labels);
135-
} catch (err) {
136-
if (err && err.status === 404) { console.log(`[sync] Issue #${num} not found. Skipping.`); continue; }
137-
throw err;
62+
if (!issue.pull_request) {
63+
const names = (issue.labels || []).map(l => typeof l === 'string' ? l : l.name);
64+
console.log(`Found labels on issue #${num}: [${names.join(', ')}]`);
65+
names.forEach(l => discoveredLabels.add(l));
66+
} else {
67+
console.log(`Skipping #${num} because it is a Pull Request, not an Issue.`);
68+
}
69+
} catch (e) {
70+
console.log(`Error fetching labels for issue #${num}: ${e.message}`);
13871
}
13972
}
14073
141-
const existing = extractLabels(prData.labels || []);
142-
const existingSet = new Set(existing);
143-
const deduped = Array.from(new Set(allLabels));
144-
const toAdd = deduped.filter(l => !existingSet.has(l));
145-
146-
console.log(`[sync] Existing: ${existing.length ? existing.join(', ') : '(none)'}`);
147-
console.log(`[sync] To add: ${toAdd.length ? toAdd.join(', ') : '(none)'}`);
74+
const currentLabels = (prData.labels || []).map(l => typeof l === 'string' ? l : l.name);
75+
console.log(`Current PR labels: [${currentLabels.join(', ')}]`);
14876
149-
const labels = toAdd;
150-
const hasLabels = labels.length > 0;
151-
core.setOutput('has_labels', String(hasLabels));
152-
core.setOutput('labels', JSON.stringify(labels));
153-
core.setOutput('pr_number', String(prNumber));
154-
core.setOutput('dry_run', String(process.env.REQUESTED_DRY_RUN || 'true'));
155-
core.setOutput('is_fork_pr', String(process.env.IS_FORK_PR || 'false'));
156-
core.setOutput('source_event', context.eventName);
157-
return { has_labels: hasLabels, labels, pr_number: String(prNumber), dry_run: process.env.REQUESTED_DRY_RUN, is_fork_pr: process.env.IS_FORK_PR, source_event: context.eventName };
77+
const newLabels = Array.from(discoveredLabels).filter(label => !currentLabels.includes(label));
78+
79+
if (newLabels.length > 0) {
80+
console.log(`New labels to be added: [${newLabels.join(', ')}]`);
81+
} else {
82+
console.log("No new labels discovered (all found labels are already present).");
83+
}
15884
159-
- name: Write labels artifact payload
160-
env:
161-
LABELS_JSON: ${{ steps.compute.outputs.labels }}
162-
PR_NUMBER: ${{ steps.compute.outputs.pr_number }}
163-
IS_FORK_PR: ${{ steps.compute.outputs.is_fork_pr }}
164-
DRY_RUN: ${{ steps.compute.outputs.dry_run }}
165-
SOURCE_EVENT: ${{ steps.compute.outputs.source_event }}
166-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
167-
with:
168-
script: |
16985
const fs = require('fs');
170-
const parsed = JSON.parse(process.env.LABELS_JSON || '[]');
171-
const payload = {
172-
pr_number: Number(process.env.PR_NUMBER || 0),
173-
labels: Array.isArray(parsed) ? parsed : [],
174-
is_fork_pr: /^true$/i.test(process.env.IS_FORK_PR || ''),
175-
dry_run: /^true$/i.test(process.env.DRY_RUN || ''),
176-
source_event: process.env.SOURCE_EVENT || '',
177-
};
178-
fs.writeFileSync('labels.json', JSON.stringify(payload));
179-
console.log(`Wrote labels artifact payload for PR #${payload.pr_number}: ${payload.labels.length} labels`);
180-
181-
- name: Upload labels artifact
86+
const result = { pr_number: prNumber, labels: newLabels };
87+
fs.writeFileSync('labels.json', JSON.stringify(result));
88+
console.log(`Calculated labels: ${newLabels.join(', ')}`);
89+
90+
- name: Check for Labels File
91+
id: check_file
92+
run: |
93+
if [ -f labels.json ]; then
94+
echo "exists=true" >> $GITHUB_OUTPUT
95+
else
96+
echo "exists=false" >> $GITHUB_OUTPUT
97+
fi
98+
99+
- name: Upload Artifact
100+
if: steps.check_file.outputs.exists == 'true'
182101
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
183102
with:
184-
name: pr-labels-${{ steps.compute.outputs.pr_number }}
103+
name: pr-labels-data
185104
path: labels.json
186-
retention-days: 1
187-
188-
dispatch-add:
189-
needs: compute-labels
190-
if: ${{ needs.compute-labels.outputs.is_fork_pr != 'true' }}
191-
runs-on: ubuntu-latest
192-
steps:
193-
- name: Trigger add workflow
194-
uses: step-security/workflow-dispatch@acca1a315af3bf7f33dd116d3cb405cb83f5cbdc # v1.2.8
195-
with:
196-
workflow: .github/workflows/sync-issue-labels-add.yml
197-
repo: ${{ github.repository }}
198-
ref: main
199-
token: ${{ secrets.GH_ACCESS_TOKEN }}
200-
inputs: >-
201-
{
202-
"upstream_run_id":"${{ github.run_id }}",
203-
"pr_number":"${{ needs.compute-labels.outputs.pr_number }}",
204-
"dry_run":"${{ needs.compute-labels.outputs.dry_run }}",
205-
"is_fork_pr":"${{ needs.compute-labels.outputs.is_fork_pr }}"
206-
}
207-
105+
retention-days: 1

0 commit comments

Comments
 (0)