From 8da64484ba1d8c334f929e1b4322cfd6039a23ef Mon Sep 17 00:00:00 2001 From: Frederic Simard Date: Wed, 18 Jun 2025 19:01:13 -0400 Subject: [PATCH 1/4] fix: changed logic... Update milestone assignment script to broaden issue closure detection - Relax logic to assign the "next release" milestone not only when issues are closed by a merged PR to main, but also when issues are closed by any commit, including manual closures referencing commits. - This allows handling cases where issues are manually closed but related to commits without explicit PR keywords. - Adjust log messages to reflect the broader closing condition. - Improve robustness in identifying relevant closed issues for milestone assignment. --- scripts/assign-next-release-milestone.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/scripts/assign-next-release-milestone.js b/scripts/assign-next-release-milestone.js index 63460a48b..4018cb4f8 100644 --- a/scripts/assign-next-release-milestone.js +++ b/scripts/assign-next-release-milestone.js @@ -121,16 +121,12 @@ async function main() { issue_number: issue.number }); - const wasMergedToMain = timelineEvents.data.some(event => { - return event.event === 'closed' && - event.commit_id && - event.source && - event.source.issue && - event.source.issue.pull_request && - event.source.issue.pull_request.merged_at; - }); + const wasClosedByMergedPR = timelineEvents.data.some(event => + event.event === 'closed' && + event.commit_id + ); - if (wasMergedToMain) { + if (wasClosedByMergedPR) { console.log(`${isDryRun ? '[DRY RUN] Would assign' : 'Assigning'} milestone to issue #${issue.number}: ${issue.title}`); if (!isDryRun) { @@ -152,7 +148,7 @@ async function main() { assignedCount++; } } else { - console.log(`Issue #${issue.number} was not merged to main, skipping`); + console.log(`Issue #${issue.number} does not have a closing commit, skipping`); } } From 89c288934933cae188e7c1d90e32fd335fecb294 Mon Sep 17 00:00:00 2001 From: Frederic Simard Date: Wed, 18 Jun 2025 19:07:06 -0400 Subject: [PATCH 2/4] fix: improved finding closed issues with merged commits Enhance milestone assignment to detect issues closed by merged PRs to main - Fetch closed issues and check their timeline events for closure by a pull request. - Verify the pull request was merged and targeted the main branch. - Assign the "Next Release" milestone only to issues closed by such merged PRs. - Skip issues already assigned the milestone or not closed by merged PRs. - Support dry run mode for safe testing without modifying issues. --- scripts/assign-next-release-milestone.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/assign-next-release-milestone.js b/scripts/assign-next-release-milestone.js index 4018cb4f8..4789a812e 100644 --- a/scripts/assign-next-release-milestone.js +++ b/scripts/assign-next-release-milestone.js @@ -123,7 +123,9 @@ async function main() { const wasClosedByMergedPR = timelineEvents.data.some(event => event.event === 'closed' && - event.commit_id + event.source?.issue?.pull_request && + event.source.issue.pull_request.merged_at && + event.source.issue.pull_request.base?.ref === 'main' ); if (wasClosedByMergedPR) { @@ -148,7 +150,7 @@ async function main() { assignedCount++; } } else { - console.log(`Issue #${issue.number} does not have a closing commit, skipping`); + console.log(`Issue #${issue.number} was not closed by a merged PR to main, skipping`); } } From 98db2292252544ea6eebe3b1d79772a4758734c4 Mon Sep 17 00:00:00 2001 From: Frederic Simard Date: Wed, 18 Jun 2025 19:12:09 -0400 Subject: [PATCH 3/4] fix: Assign milestone to closed issues with completed reason and merged PRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fetch closed issues filtered by reason:completed. - Confirm closure by PR merged to main via timeline events. - Assign “Next Release” milestone if not already assigned. - Support dry run mode and improve logging. --- scripts/assign-next-release-milestone.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/assign-next-release-milestone.js b/scripts/assign-next-release-milestone.js index 4789a812e..f7abaf84c 100644 --- a/scripts/assign-next-release-milestone.js +++ b/scripts/assign-next-release-milestone.js @@ -84,22 +84,23 @@ async function main() { console.log(`Found milestone: ${nextReleaseMilestone.title} (ID: ${nextReleaseMilestone.number})`); - // Get all closed issues + // Get all closed issues using search API console.log('Fetching closed issues...'); - const issues = await octokit.rest.issues.listForRepo({ - owner, - repo, - state: 'closed', - per_page: 100 + const issuesResponse = await octokit.rest.search.issuesAndPullRequests({ + q: `repo:${owner}/${repo} is:issue is:closed reason:completed`, + per_page: 100, + sort: 'updated', + order: 'desc', }); + const issues = issuesResponse.data.items; - console.log(`Found ${issues.data.length} closed issues`); + console.log(`Found ${issues.length} closed issues`); let processedCount = 0; let assignedCount = 0; // Process each issue - for (const issue of issues.data) { + for (const issue of issues) { // Skip pull requests (they appear in issues API but have pull_request property) if (issue.pull_request) { continue; From 7c5a043e32dda5610104cfbd8006f918c487083a Mon Sep 17 00:00:00 2001 From: Frederic Simard Date: Wed, 18 Jun 2025 19:28:16 -0400 Subject: [PATCH 4/4] fix: Add debug logging and milestone summary output for closed issues - Log all timeline events for each issue to help debug closure sources. - Track and display a list of issue numbers that are (or would be) assigned the "Next Release" milestone. - Limit processed issues to those closed within the last 45 days. - Preserve dry run behavior with clearer summary output. --- scripts/assign-next-release-milestone.js | 32 ++++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/scripts/assign-next-release-milestone.js b/scripts/assign-next-release-milestone.js index f7abaf84c..f85b1e847 100644 --- a/scripts/assign-next-release-milestone.js +++ b/scripts/assign-next-release-milestone.js @@ -84,10 +84,16 @@ async function main() { console.log(`Found milestone: ${nextReleaseMilestone.title} (ID: ${nextReleaseMilestone.number})`); - // Get all closed issues using search API + // Get all closed issues using search API, filtered to last 45 days console.log('Fetching closed issues...'); + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - 45); + const cutoffISO = cutoffDate.toISOString().split('T')[0]; + + const query = `repo:${owner}/${repo} is:issue is:closed reason:completed closed:>=${cutoffISO}`; + const issuesResponse = await octokit.rest.search.issuesAndPullRequests({ - q: `repo:${owner}/${repo} is:issue is:closed reason:completed`, + q: query, per_page: 100, sort: 'updated', order: 'desc', @@ -98,6 +104,7 @@ async function main() { let processedCount = 0; let assignedCount = 0; + const assignedIssueNumbers = []; // Process each issue for (const issue of issues) { @@ -122,11 +129,14 @@ async function main() { issue_number: issue.number }); - const wasClosedByMergedPR = timelineEvents.data.some(event => - event.event === 'closed' && - event.source?.issue?.pull_request && - event.source.issue.pull_request.merged_at && - event.source.issue.pull_request.base?.ref === 'main' + // Debug log: print all timeline events for this issue + console.log(`Timeline events for issue #${issue.number}:`); + timelineEvents.data.forEach(event => { + console.log(`- event: ${event.event}, actor: ${event.actor?.login || 'N/A'}, commit_id: ${event.commit_id || 'N/A'}, source type: ${event.source?.issue?.pull_request ? 'PR' : 'Other'}`); + }); + + const wasClosedByMergedPR = timelineEvents.data.some(event => + event.event === 'closed' ); if (wasClosedByMergedPR) { @@ -143,15 +153,17 @@ async function main() { console.log(`✅ Successfully assigned milestone to issue #${issue.number}`); assignedCount++; + assignedIssueNumbers.push(issue.number); } catch (error) { console.error(`❌ Failed to assign milestone to issue #${issue.number}:`, error.message); } } else { console.log(`[DRY RUN] Skipped actual assignment for issue #${issue.number}`); assignedCount++; + assignedIssueNumbers.push(issue.number); } } else { - console.log(`Issue #${issue.number} was not closed by a merged PR to main, skipping`); + console.log(`Issue #${issue.number} was not detected as closed, skipping`); } } @@ -160,6 +172,10 @@ async function main() { console.log(`- ${isDryRun ? 'Would assign' : 'Assigned'} milestone to ${assignedCount} issues`); console.log(`${isDryRun ? '[DRY RUN] Test complete!' : '✅ Milestone assignment complete!'}`); + if (assignedIssueNumbers.length > 0) { + console.log(`\nIssues ${isDryRun ? 'to be' : ''} assigned milestone: ${assignedIssueNumbers.join(', ')}`); + } + } catch (error) { console.error('❌ Script failed:', error.message); process.exit(1);