1515 runs-on : ubuntu-latest
1616
1717 steps :
18+ # Step 1: Checkout the repo
1819 - name : Checkout
1920 uses : actions/checkout@v4
2021
22+ # Step 2: Extract linked issues from PR
2123 - name : Extract linked issue(s) from PR
2224 id : extract-issues
2325 uses : actions/github-script@v7
2830 const prTitle = context.payload.pull_request.title || '';
2931 const prBody = context.payload.pull_request.body || '';
3032
31- // Regex patterns for issue references
33+ // Regex patterns to find linked issues
3234 const patterns = [
3335 /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi,
3436 /#(\d+)/g
@@ -46,80 +48,78 @@ jobs:
4648 core.setOutput('issues', JSON.stringify(Array.from(issueNumbers)));
4749 core.setOutput('pr', prNumber.toString());
4850
49- - name : Sync Issue Metadata to PR
50- if : steps.extract-issues.outputs.issues != '' && steps.extract-issues.outputs.issues != '[]'
51- uses : actions/github-script@v7
52- with :
53- github-token : ${{ secrets.GITHUB_TOKEN }}
54- script : |
55- // Safely parse outputs
56- const issuesOutput = '${{ steps.extract-issues.outputs.issues }}' || '[]';
57- let issueNumbers;
58- try {
59- issueNumbers = JSON.parse(issuesOutput);
60- } catch (err) {
61- console.error('Failed to parse issues output:', issuesOutput);
62- issueNumbers = [];
63- }
64- const prNumber = parseInt('${{ steps.extract-issues.outputs.pr }}');
65-
66-
67- if (issueNumbers.length === 0) {
68- console.log("No linked issues found");
69- return;
70- }
71-
72- for (const issueNumber of issueNumbers) {
73- try {
74- // Fetch issue
75- const { data : issue } = await github.rest.issues.get({
76- owner : context.repo.owner,
77- repo : context.repo.repo,
78- issue_number : parseInt(issueNumber)
79- });
80-
81- console.log(`Syncing metadata from Issue # ${issueNumber} to PR #${prNumber}`);
82-
83- // --- Sync Labels ---
84- const issueLabels = issue.labels.map(l => l.name);
85- const { data : pr } = await github.rest.pulls.get({
86- owner : context.repo.owner,
87- repo : context.repo.repo,
88- pull_number : prNumber
89- });
90- const currentPRLabels = pr.labels.map(l => l.name);
91- const combinedLabels = Array.from(new Set([...currentPRLabels, ...issueLabels]));
92-
93- if (combinedLabels.length > 0) {
94- await github.rest.issues.setLabels({
95- owner : context.repo.owner,
96- repo : context.repo.repo,
97- issue_number : prNumber,
98- labels : combinedLabels
99- });
100- console.log(`Labels applied : ${combinedLabels.join(', ')}`);
101- }
102-
103- // --- Sync Milestone ---
104- if (issue.milestone) {
105- await github.rest.issues.update({
106- owner : context.repo.owner,
107- repo : context.repo.repo,
108- issue_number : prNumber,
109- milestone : issue.milestone.number
110- });
111- console.log(`Milestone synced : ${issue.milestone.title}`);
112- }
113-
114- // --- Add comment on PR ---
115- await github.rest.issues.createComment({
116- owner : context.repo.owner,
117- repo : context.repo.repo,
118- issue_number : prNumber,
119- body : ` ✅ Synchronized metadata from Issue #${issueNumber}:\n - Labels: ${issueLabels.length > 0 ? issueLabels.join(', ') : 'None'}\n - Milestone: ${issue.milestone ? issue.milestone.title : 'None'}`
120- });
121-
122- } catch (error) {
123- console.error(`Error syncing issue # ${issueNumber} to PR #${prNumber}:`, error.message);
124- }
125- }
51+ - name: Sync Issue Metadata to PR
52+ if : steps.extract-issues.outputs.issues != '' && steps.extract-issues.outputs.issues != '[]'
53+ uses : actions/github-script@v7
54+ with :
55+ github-token : ${{ secrets.GITHUB_TOKEN }}
56+ issues : ${{ steps.extract-issues.outputs.issues }}
57+ pr : ${{ steps.extract-issues.outputs.pr }}
58+ script : |
59+ // Safely parse workflow outputs
60+ const issuesOutput = inputs.issues || '[]';
61+ const prNumber = parseInt(inputs.pr);
62+
63+ let issueNumbers;
64+ try {
65+ issueNumbers = JSON.parse(issuesOutput);
66+ } catch (err) {
67+ console.error('Failed to parse issues output:', issuesOutput);
68+ issueNumbers = [];
69+ }
70+
71+ if (issueNumbers.length === 0) {
72+ console.log("No linked issues found");
73+ return;
74+ }
75+
76+ for (const issueNumber of issueNumbers) {
77+ try {
78+ const { data: issue } = await github.rest.issues.get({
79+ owner: context.repo.owner,
80+ repo: context.repo.repo,
81+ issue_number: parseInt(issueNumber)
82+ });
83+
84+ console.log(`Syncing metadata from Issue #${issueNumber} to PR #${prNumber}`);
85+
86+ const issueLabels = issue.labels.map(l => l.name);
87+ const { data: pr } = await github.rest.pulls.get({
88+ owner: context.repo.owner,
89+ repo: context.repo.repo,
90+ pull_number: prNumber
91+ });
92+ const currentPRLabels = pr.labels.map(l => l.name);
93+ const combinedLabels = Array.from(new Set([...currentPRLabels, ...issueLabels]));
94+
95+ if (combinedLabels.length > 0) {
96+ await github.rest.issues.setLabels({
97+ owner: context.repo.owner,
98+ repo: context.repo.repo,
99+ issue_number: prNumber,
100+ labels: combinedLabels
101+ });
102+ console.log(`Labels applied: ${combinedLabels.join(', ')}`);
103+ }
104+
105+ if (issue.milestone) {
106+ await github.rest.issues.update({
107+ owner: context.repo.owner,
108+ repo: context.repo.repo,
109+ issue_number: prNumber,
110+ milestone: issue.milestone.number
111+ });
112+ console.log(`Milestone synced: ${issue.milestone.title}`);
113+ }
114+
115+ await github.rest.issues.createComment({
116+ owner: context.repo.owner,
117+ repo: context.repo.repo,
118+ issue_number: prNumber,
119+ body: `✅ Synchronized metadata from Issue #${issueNumber}:\n- Labels: ${issueLabels.length > 0 ? issueLabels.join(', ') : 'None'}\n- Milestone: ${issue.milestone ? issue.milestone.title : 'None'}`
120+ });
121+
122+ } catch (error) {
123+ console.error(`Error syncing issue #${issueNumber} to PR #${prNumber}:`, error.message);
124+ }
125+ }
0 commit comments