1- name : Sync PR data from Linked Issues
1+ name : PR Issue Sync
22
33on :
4- pull_request_target :
5- types : [opened, edited, synchronize]
4+ workflow_dispatch : {} # manual trigger only (disables automatic runs)
65
76jobs :
87 sync-metadata :
@@ -28,66 +27,69 @@ jobs:
2827 }
2928 core.setOutput("issues", JSON.stringify(matches));
3029
31- - name : Post or Update data Comment
32- if : steps.extract.outputs.issues && steps.extract.outputs.issues != '[]'
30+ - name : Sync metadata
3331 uses : actions/github-script@v7
3432 with :
3533 github-token : ${{ secrets.GITHUB_TOKEN }}
3634 script : |
37- const issues = JSON.parse(`${{ steps.extract.outputs.issues }}`);
38- const prNumber = context.payload.pull_request.number;
39-
40- let combinedLabels = [];
41- let combinedAssignees = [];
42- let combinedMilestones = [];
43-
44- for (const number of issues) {
35+ const data = ${{ fromJSON(format('{{\"issues\":{0},\"pr\":{1}}}', steps.extract.outputs.issues, github.event.pull_request.number)) }};
36+ const prNumber = data.pr;
37+ const issueNumbers = data.issues || [];
38+
39+ if (issueNumbers.length === 0) {
40+ console.log("No linked issues found");
41+ return;
42+ }
43+
44+ for (const issueNumber of issueNumbers) {
4545 try {
46- const issue = await github.rest.issues.get({
47- ...context.repo,
48- issue_number: number
46+ // Fetch issue
47+ const { data: issue } = await github.rest.issues.get({
48+ owner: context.repo.owner,
49+ repo: context.repo.repo,
50+ issue_number: parseInt(issueNumber)
4951 });
50-
51- combinedLabels.push(...issue.data.labels.map(l => l.name));
52- combinedAssignees.push(...issue.data.assignees.map(a => a.login));
53- if (issue.data.milestone) combinedMilestones.push(issue.data.milestone.title);
54-
55- } catch (err) {
56- console.log(`Could not fetch issue #${number}: ${err.message}`);
52+
53+ console.log(`Syncing metadata from Issue # ${issueNumber} to PR #${prNumber}`);
54+
55+ // --- Sync Labels ---
56+ const issueLabels = issue.labels.map(l => l.name);
57+ const { data : pr } = await github.rest.pulls.get({
58+ owner : context.repo.owner,
59+ repo : context.repo.repo,
60+ pull_number : prNumber
61+ });
62+ const currentPRLabels = pr.labels.map(l => l.name);
63+ const combinedLabels = Array.from(new Set([...currentPRLabels, ...issueLabels]));
64+
65+ await github.rest.issues.addLabels({
66+ owner : context.repo.owner,
67+ repo : context.repo.repo,
68+ issue_number : prNumber,
69+ labels : combinedLabels
70+ });
71+ console.log(`Labels applied : ${combinedLabels.join(', ')}`);
72+
73+ // --- Sync Milestone ---
74+ if (issue.milestone) {
75+ await github.rest.issues.update({
76+ owner : context.repo.owner,
77+ repo : context.repo.repo,
78+ issue_number : prNumber,
79+ milestone : issue.milestone.number
80+ });
81+ console.log(`Milestone synced : ${issue.milestone.title}`);
82+ }
83+
84+ // --- Optionally : Add a comment on PR ---
85+ await github.rest.issues.createComment({
86+ owner : context.repo.owner,
87+ repo : context.repo.repo,
88+ issue_number : prNumber,
89+ body : ` ✅ Synchronized metadata from Issue #${issueNumber}:\n Labels: ${issueLabels.join(', ')}\n Milestone: ${issue.milestone ? issue.milestone.title : 'None'}`
90+ });
91+
92+ } catch (error) {
93+ console.error(`Error syncing issue # ${issueNumber} to PR #${prNumber}:`, error.message);
5794 }
5895 }
59-
60- // Deduplicate
61- combinedLabels = [...new Set(combinedLabels)];
62- combinedAssignees = [...new Set(combinedAssignees)];
63- combinedMilestones = [...new Set(combinedMilestones)];
64-
65- const commentBody =
66- `### Synced data from Linked Issues\n\n` +
67- `**Labels:**\n${combinedLabels.length ? combinedLabels.map(l => `- ${l}`).join("\n") : "- None"}\n\n` +
68- `**Assignees:**\n${combinedAssignees.length ? combinedAssignees.map(a => `- ${a}`).join("\n") : "- None"}\n\n` +
69- `**Milestones:**\n${combinedMilestones.length ? combinedMilestones.map(m => `- ${m}`).join("\n") : "- None"}\n`;
70-
71- // Get existing comments
72- const comments = await github.rest.issues.listComments({
73- ...context.repo,
74- issue_number: prNumber
75- });
76-
77- // Find existing workflow comment
78- const existingComment = comments.data.find(c => c.body.includes("### Synced data from Linked Issues"));
79-
80- if (existingComment) {
81- await github.rest.issues.updateComment({
82- ...context.repo,
83- comment_id: existingComment.id,
84- body: commentBody
85- });
86- } else {
87- // Create new comment
88- await github.rest.issues.createComment({
89- ...context.repo,
90- issue_number: prNumber,
91- body: commentBody
92- });
93- }
0 commit comments