diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 875a9cb6..afb7b699 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -139,7 +139,7 @@ jobs: COMMIT_TEXT=$(git log $RANGE --pretty=format:"%s%n%b" 2>/dev/null || true) ISSUES_FROM_COMMITS=$(printf '%s\n' "$COMMIT_TEXT" | \ - perl -ne 'if (/(fixes|closes|resolves|refs?)(.*)/i) { print "$2\n"; }' | \ + perl -ne 'while (/\b(?:fixes|closes|resolves|refs?)\s+((?:#[0-9]+\b(?:\s*(?:,|and)?\s*#[0-9]+\b)*))/ig) { print "$1\n"; }' | \ grep -oE '#[0-9]+' || true) PR_CANDIDATES=$(printf '%s\n' "$COMMIT_TEXT" | \ @@ -155,7 +155,7 @@ jobs: done ISSUES_FROM_PRS=$(printf '%s\n' "$PR_TEXT" | \ - perl -ne 'if (/(fixes|closes|resolves|refs?)(.*)/i) { print "$2\n"; }' | \ + perl -ne 'while (/\b(?:fixes|closes|resolves|refs?)\s+((?:#[0-9]+\b(?:\s*(?:,|and)?\s*#[0-9]+\b)*))/ig) { print "$1\n"; }' | \ grep -oE '#[0-9]+' || true) ISSUES=$(printf '%s\n%s\n' "$ISSUES_FROM_COMMITS" "$ISSUES_FROM_PRS" | \ diff --git a/.github/workflows/label-pending-release.yml b/.github/workflows/label-pending-release.yml index 7452f85d..3b10a329 100644 --- a/.github/workflows/label-pending-release.yml +++ b/.github/workflows/label-pending-release.yml @@ -38,7 +38,7 @@ jobs: $PR_BODY $COMMIT_TEXT" PR_ISSUES=$(printf '%s\n' "$PR_TEXT" | \ - perl -ne 'if (/(fixes|closes|resolves|refs?)(.*)/i) { print "$2\n"; }' | \ + perl -ne 'while (/\b(?:fixes|closes|resolves|refs?)\s+((?:#[0-9]+\b(?:\s*(?:,|and)?\s*#[0-9]+\b)*))/ig) { print "$1\n"; }' | \ grep -oE '#[0-9]+' || true) if [[ -n "$PR_ISSUES" ]]; then ALL_REFERENCED_ISSUES=$(printf '%s\n%s\n' "$ALL_REFERENCED_ISSUES" "$PR_ISSUES") diff --git a/scripts/github/stable-release-issue-cleanup-lib.mjs b/scripts/github/stable-release-issue-cleanup-lib.mjs index 64d7057d..959b2e35 100644 --- a/scripts/github/stable-release-issue-cleanup-lib.mjs +++ b/scripts/github/stable-release-issue-cleanup-lib.mjs @@ -2,8 +2,10 @@ import { readFileSync } from 'node:fs'; import { spawnSync } from 'node:child_process'; const ISSUE_REF_PATTERN = /#([0-9]+)/g; -const ACTION_VERB_PATTERN = /\b(fixes|closes|resolves|refs?)\b(.*)/gi; -const RESOLVE_VERB_PATTERN = /\b(fixes|closes|resolves)\b(.*)/gi; +const ACTION_VERB_PATTERN = + /\b(?:fixes|closes|resolves|refs?)\b\s+(#\d+\b(?:\s*(?:,|and)?\s*#\d+\b)*)/gi; +const RESOLVE_VERB_PATTERN = + /\b(?:fixes|closes|resolves)\b\s+(#\d+\b(?:\s*(?:,|and)?\s*#\d+\b)*)/gi; const PR_REF_PATTERN = /(?:Merge pull request #|\(#)([0-9]+)/g; const STABLE_TAG_PATTERN = /^v[0-9]+\.[0-9]+\.[0-9]+$/; @@ -14,7 +16,7 @@ export function extractIssueNumbers(text, { includeRefs = true } = {}) { pattern.lastIndex = 0; while ((actionMatch = pattern.exec(text || '')) !== null) { - const tail = actionMatch[2] || ''; + const tail = actionMatch[1] || ''; let issueMatch; ISSUE_REF_PATTERN.lastIndex = 0; while ((issueMatch = ISSUE_REF_PATTERN.exec(tail)) !== null) { diff --git a/tests/unit/github/stable-release-issue-cleanup.test.mjs b/tests/unit/github/stable-release-issue-cleanup.test.mjs index 81cc4f35..de97747d 100644 --- a/tests/unit/github/stable-release-issue-cleanup.test.mjs +++ b/tests/unit/github/stable-release-issue-cleanup.test.mjs @@ -18,6 +18,18 @@ describe('stable release issue cleanup', () => { expect(extractIssueNumbers(text, { includeRefs: false })).toEqual([1340, 1341]); }); + it('ignores unrelated issue references after the action reference sequence', () => { + const text = [ + 'fix: guard release parser', + '', + 'Fixes #12; see #99 for the follow-up', + 'Resolves #13, #14 and #15; related to #100', + ].join('\n'); + + expect(extractIssueNumbers(text, { includeRefs: true })).toEqual([12, 13, 14, 15]); + expect(extractIssueNumbers(text, { includeRefs: false })).toEqual([12, 13, 14, 15]); + }); + it('extracts PR numbers from merge and squash commit subjects', () => { const text = [ 'Merge pull request #1392 from kaitranntt/kai/fix/foo',