From 2511393d43b7c2255d734f4b4862d12ffc5dfd99 Mon Sep 17 00:00:00 2001 From: sheikhlimon Date: Tue, 21 Oct 2025 09:42:23 +0600 Subject: [PATCH 1/7] fix: resolve JSON double-encoding in sync metadata workflow --- .github/workflows/autolabel-pr-issue.yml | 57 +++++++++--------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/.github/workflows/autolabel-pr-issue.yml b/.github/workflows/autolabel-pr-issue.yml index 2effb31f..4df86275 100644 --- a/.github/workflows/autolabel-pr-issue.yml +++ b/.github/workflows/autolabel-pr-issue.yml @@ -43,16 +43,17 @@ jobs: } } - return JSON.stringify({ issues: Array.from(issueNumbers), pr: prNumber }); + core.setOutput('issues', JSON.stringify(Array.from(issueNumbers))); + core.setOutput('pr', prNumber.toString()); - name: Sync Issue Metadata to PR + if: steps.extract-issues.outputs.issues != '[]' uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const data = JSON.parse('${{ steps.extract-issues.outputs.result }}'); - const prNumber = data.pr; - const issueNumbers = data.issues || []; + const issueNumbers = JSON.parse('${{ steps.extract-issues.outputs.issues }}'); + const prNumber = parseInt('${{ steps.extract-issues.outputs.pr }}'); if (issueNumbers.length === 0) { console.log("No linked issues found"); @@ -67,9 +68,9 @@ jobs: repo: context.repo.repo, issue_number: parseInt(issueNumber) }); - + 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({ @@ -79,15 +80,17 @@ jobs: }); 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(', ')}`); - + + if (combinedLabels.length > 0) { + await github.rest.issues.setLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + labels: combinedLabels + }); + console.log(`Labels applied: ${combinedLabels.join(', ')}`); + } + // --- Sync Milestone --- if (issue.milestone) { await github.rest.issues.update({ @@ -98,32 +101,14 @@ 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 a 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); } From 3bcfbd8bec99e6d8d808bab61a74c90ff0260583 Mon Sep 17 00:00:00 2001 From: sheikhlimon Date: Tue, 21 Oct 2025 17:31:01 +0600 Subject: [PATCH 2/7] fix: safely parse extracted issue numbers in PR sync workflow --- .github/workflows/autolabel-pr-issue.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/autolabel-pr-issue.yml b/.github/workflows/autolabel-pr-issue.yml index 4df86275..e56b7e83 100644 --- a/.github/workflows/autolabel-pr-issue.yml +++ b/.github/workflows/autolabel-pr-issue.yml @@ -47,13 +47,15 @@ jobs: core.setOutput('pr', prNumber.toString()); - name: Sync Issue Metadata to PR - if: steps.extract-issues.outputs.issues != '[]' + if: steps.extract-issues.outputs.issues != '' && steps.extract-issues.outputs.issues != '[]' uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const issueNumbers = JSON.parse('${{ steps.extract-issues.outputs.issues }}'); - const prNumber = parseInt('${{ steps.extract-issues.outputs.pr }}'); + // Safely parse outputs + const issuesOutput = ${{ steps.extract-issues.outputs.issues }} || '[]'; + const issueNumbers = JSON.parse(issuesOutput); + const prNumber = parseInt(${{ steps.extract-issues.outputs.pr }}); if (issueNumbers.length === 0) { console.log("No linked issues found"); @@ -68,9 +70,9 @@ jobs: repo: context.repo.repo, issue_number: parseInt(issueNumber) }); - + 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({ @@ -80,7 +82,7 @@ jobs: }); const currentPRLabels = pr.labels.map(l => l.name); const combinedLabels = Array.from(new Set([...currentPRLabels, ...issueLabels])); - + if (combinedLabels.length > 0) { await github.rest.issues.setLabels({ owner: context.repo.owner, @@ -90,7 +92,7 @@ jobs: }); console.log(`Labels applied: ${combinedLabels.join(', ')}`); } - + // --- Sync Milestone --- if (issue.milestone) { await github.rest.issues.update({ @@ -101,14 +103,15 @@ jobs: }); console.log(`Milestone synced: ${issue.milestone.title}`); } - - // --- 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}:\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); } From 7aefb94bb2e90d2cc4815a1703ad138023f17741 Mon Sep 17 00:00:00 2001 From: sheikhlimon Date: Tue, 21 Oct 2025 17:35:34 +0600 Subject: [PATCH 3/7] fix: correct JSON parsing in PR metadata sync workflow --- .github/workflows/autolabel-pr-issue.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/autolabel-pr-issue.yml b/.github/workflows/autolabel-pr-issue.yml index e56b7e83..94ff087c 100644 --- a/.github/workflows/autolabel-pr-issue.yml +++ b/.github/workflows/autolabel-pr-issue.yml @@ -53,9 +53,16 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | // Safely parse outputs - const issuesOutput = ${{ steps.extract-issues.outputs.issues }} || '[]'; - const issueNumbers = JSON.parse(issuesOutput); - const prNumber = parseInt(${{ steps.extract-issues.outputs.pr }}); + const issuesOutput = '${{ steps.extract-issues.outputs.issues }}' || '[]'; + let issueNumbers; + try { + issueNumbers = JSON.parse(issuesOutput); + } catch (err) { + console.error('Failed to parse issues output:', issuesOutput); + issueNumbers = []; + } + const prNumber = parseInt('${{ steps.extract-issues.outputs.pr }}'); + if (issueNumbers.length === 0) { console.log("No linked issues found"); From 80ed03930b2c72ca1fc80152e39721d22c00f6da Mon Sep 17 00:00:00 2001 From: sheikhlimon Date: Tue, 21 Oct 2025 18:37:20 +0600 Subject: [PATCH 4/7] fix: v4 --- .github/workflows/autolabel-pr-issue.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/autolabel-pr-issue.yml b/.github/workflows/autolabel-pr-issue.yml index 94ff087c..c52668ad 100644 --- a/.github/workflows/autolabel-pr-issue.yml +++ b/.github/workflows/autolabel-pr-issue.yml @@ -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 @@ -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 @@ -46,23 +48,26 @@ jobs: core.setOutput('issues', JSON.stringify(Array.from(issueNumbers))); core.setOutput('pr', prNumber.toString()); + # Step 3: Sync issue metadata to PR - name: Sync Issue Metadata to PR if: steps.extract-issues.outputs.issues != '' && steps.extract-issues.outputs.issues != '[]' uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} + issues: ${{ steps.extract-issues.outputs.issues }} + pr: ${{ steps.extract-issues.outputs.pr }} script: | - // Safely parse outputs - const issuesOutput = '${{ steps.extract-issues.outputs.issues }}' || '[]'; + // Safely parse workflow outputs + const issuesOutput = inputs.issues || '[]'; + const prNumber = parseInt(inputs.pr); let issueNumbers; + try { issueNumbers = JSON.parse(issuesOutput); } catch (err) { console.error('Failed to parse issues output:', issuesOutput); issueNumbers = []; } - const prNumber = parseInt('${{ steps.extract-issues.outputs.pr }}'); - if (issueNumbers.length === 0) { console.log("No linked issues found"); @@ -71,7 +76,7 @@ jobs: for (const issueNumber of issueNumbers) { try { - // Fetch issue + // Fetch issue details const { data: issue } = await github.rest.issues.get({ owner: context.repo.owner, repo: context.repo.repo, From d639a8706fd7e09542794746ce0fe2f42392254b Mon Sep 17 00:00:00 2001 From: sheikhlimon Date: Tue, 21 Oct 2025 18:49:33 +0600 Subject: [PATCH 5/7] fix: v5 --- .github/workflows/autolabel-pr-issue.yml | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/.github/workflows/autolabel-pr-issue.yml b/.github/workflows/autolabel-pr-issue.yml index c52668ad..7a0c7b59 100644 --- a/.github/workflows/autolabel-pr-issue.yml +++ b/.github/workflows/autolabel-pr-issue.yml @@ -30,7 +30,6 @@ jobs: const prTitle = context.payload.pull_request.title || ''; const prBody = context.payload.pull_request.body || ''; - // Regex patterns to find linked issues const patterns = [ /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi, /#(\d+)/g @@ -54,27 +53,23 @@ jobs: uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} - issues: ${{ steps.extract-issues.outputs.issues }} - pr: ${{ steps.extract-issues.outputs.pr }} script: | - // Safely parse workflow outputs - const issuesOutput = inputs.issues || '[]'; - const prNumber = parseInt(inputs.pr); - let issueNumbers; + const issuesOutput = '${{ steps.extract-issues.outputs.issues }}'; + const prNumber = parseInt('${{ steps.extract-issues.outputs.pr }}'); + let issues = []; try { - issueNumbers = JSON.parse(issuesOutput); - } catch (err) { - console.error('Failed to parse issues output:', issuesOutput); - issueNumbers = []; + issues = JSON.parse(issuesOutput); + } catch (e) { + console.error("Failed to parse issues:", issuesOutput); } - if (issueNumbers.length === 0) { + if (!issues || issues.length === 0) { console.log("No linked issues found"); return; } - for (const issueNumber of issueNumbers) { + for (const issueNumber of issues) { try { // Fetch issue details const { data: issue } = await github.rest.issues.get({ @@ -116,7 +111,7 @@ jobs: console.log(`Milestone synced: ${issue.milestone.title}`); } - // --- Add comment on PR --- + // --- Optionally add comment on PR --- await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, From ee8f3ce4b6a3fa8631878d20bb4f2dfdb9f18972 Mon Sep 17 00:00:00 2001 From: sheikhlimon Date: Tue, 21 Oct 2025 19:03:44 +0600 Subject: [PATCH 6/7] fix(ci): safely sync issue metadata to PR without overwriting labels - Replaced `setLabels()` with `addLabels()` to prevent race conditions where concurrent label additions could be lost. - Labels from linked issues are now added without removing existing PR labels. - Milestones from linked issues continue to be synced. - Adds a comment to the PR summarizing synchronized metadata. --- .github/workflows/autolabel-pr-issue.yml | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/.github/workflows/autolabel-pr-issue.yml b/.github/workflows/autolabel-pr-issue.yml index 7a0c7b59..58eba6eb 100644 --- a/.github/workflows/autolabel-pr-issue.yml +++ b/.github/workflows/autolabel-pr-issue.yml @@ -47,7 +47,7 @@ jobs: core.setOutput('issues', JSON.stringify(Array.from(issueNumbers))); core.setOutput('pr', prNumber.toString()); - # Step 3: Sync issue metadata to PR + # 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 @@ -80,24 +80,16 @@ jobs: console.log(`Syncing metadata from Issue #${issueNumber} to PR #${prNumber}`); - // --- Sync Labels --- + // --- Sync Labels safely using addLabels --- 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])); - - if (combinedLabels.length > 0) { - await github.rest.issues.setLabels({ + if (issueLabels.length > 0) { + await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: prNumber, - labels: combinedLabels + labels: issueLabels }); - console.log(`Labels applied: ${combinedLabels.join(', ')}`); + console.log(`Labels added from issue: ${issueLabels.join(', ')}`); } // --- Sync Milestone --- @@ -111,7 +103,7 @@ jobs: console.log(`Milestone synced: ${issue.milestone.title}`); } - // --- Optionally add comment on PR --- + // --- Add comment on PR --- await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, From 59eadb3a66b94a9a187f1187f814480fcb1838fd Mon Sep 17 00:00:00 2001 From: sheikhlimon Date: Tue, 21 Oct 2025 19:24:53 +0600 Subject: [PATCH 7/7] fix(ci): safely sync issue metadata to PR and fix JSON parsing - Stop direct interpolation of workflow outputs into github-script to avoid malformed JSON errors. - Pass issues and PR number via environment variables and parse from process.env. - Add defensive JSON parsing with cleaning for extra quotes/newlines. - Fix typo in PR comment: use issue.milestone correctly. - Use addLabels() instead of setLabels() to prevent race conditions and preserve existing PR labels. - Sync milestones and add a comment summarizing the synchronized metadata. --- .github/workflows/autolabel-pr-issue.yml | 29 +++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/.github/workflows/autolabel-pr-issue.yml b/.github/workflows/autolabel-pr-issue.yml index 58eba6eb..b2b8c5a9 100644 --- a/.github/workflows/autolabel-pr-issue.yml +++ b/.github/workflows/autolabel-pr-issue.yml @@ -30,6 +30,7 @@ jobs: const prTitle = context.payload.pull_request.title || ''; const prBody = context.payload.pull_request.body || ''; + // Regex patterns to find linked issues const patterns = [ /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi, /#(\d+)/g @@ -51,21 +52,33 @@ jobs: - 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 issuesOutput = '${{ steps.extract-issues.outputs.issues }}'; - const prNumber = parseInt('${{ steps.extract-issues.outputs.pr }}'); + // 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) { - console.error("Failed to parse issues:", issuesOutput); + // 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 (!issues || issues.length === 0) { - console.log("No linked issues found"); + if (!Array.isArray(issues) || issues.length === 0) { + console.log('No linked issues found'); return; } @@ -75,13 +88,13 @@ jobs: 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 safely using addLabels --- - const issueLabels = issue.labels.map(l => l.name); + 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, @@ -112,6 +125,6 @@ jobs: }); } catch (error) { - console.error(`Error syncing issue #${issueNumber} to PR #${prNumber}:`, error.message); + console.error(`Error syncing issue #${issueNumber} to PR #${prNumber}:`, error); } }