11name : Assign `Next Release` Milestone
22
33on :
4- workflow_dispatch :
54 push :
65 branches :
76 - main
109 - ' LICENSE'
1110 - ' .gitignore'
1211 - ' .github/**'
12+ workflow_dispatch :
13+ inputs :
14+ dry_run :
15+ description : ' Run in test mode (no changes made)'
16+ required : false
17+ default : ' false'
18+ type : boolean
1319
1420jobs :
1521 assign-milestone :
@@ -36,81 +42,91 @@ jobs:
3642 script : |
3743 const { owner, repo } = context.repo;
3844
45+ const isDryRun = '${{ github.event.inputs.dry_run }}' === 'true';
46+
47+ if (isDryRun) {
48+ console.log('🧪 RUNNING IN DRY RUN MODE - No changes will be made');
49+ }
50+
3951 // Get the "next release" milestone
4052 const milestones = await github.rest.issues.listMilestones({
41- owner,
42- repo,
43- state: 'open'
53+ owner,
54+ repo,
55+ state: 'open'
4456 });
4557
4658 const nextReleaseMilestone = milestones.data.find(
47- milestone => milestone.title.toLowerCase() === 'Next Release '
59+ milestone => milestone.title.toLowerCase() === 'next release '
4860 );
4961
5062 if (!nextReleaseMilestone) {
51- console.log('❌ Next Release milestone not found');
52- return;
63+ console.log('❌ "next release" milestone not found');
64+ return;
5365 }
5466
5567 console.log(`Found milestone: ${nextReleaseMilestone.title} (ID: ${nextReleaseMilestone.number})`);
5668
5769 // Get all closed issues
5870 const issues = await github.rest.issues.listForRepo({
59- owner,
60- repo,
61- state: 'closed',
62- per_page: 100
71+ owner,
72+ repo,
73+ state: 'closed',
74+ per_page: 100
6375 });
6476
6577 console.log(`Found ${issues.data.length} closed issues`);
6678
6779 // Process each issue
6880 for (const issue of issues.data) {
69- // Skip pull requests (they appear in issues API but have pull_request property)
70- if (issue.pull_request) {
71- continue;
72- }
73-
74- // Skip if already has the next release milestone
75- if (issue.milestone && issue.milestone.number === nextReleaseMilestone.number) {
76- console.log(`Issue #${issue.number} already has Next Release milestone`);
77- continue;
78- }
79-
80- // Check if issue was closed by a merged PR
81- const timelineEvents = await github.rest.issues.listEventsForTimeline({
82- owner,
83- repo,
84- issue_number: issue.number
85- });
86-
87- const wasMergedToMain = timelineEvents.data.some(event => {
88- return event.event === 'closed' &&
89- event.commit_id &&
90- event.source &&
91- event.source.issue &&
92- event.source.issue.pull_request &&
93- event.source.issue.pull_request.merged_at;
94- });
95-
96- if (wasMergedToMain) {
97- console.log(`Assigning milestone to issue #${issue.number}: ${issue.title}`);
98-
99- try {
100- await github.rest.issues.update({
81+ // Skip pull requests (they appear in issues API but have pull_request property)
82+ if (issue.pull_request) {
83+ continue;
84+ }
85+
86+ // Skip if already has the next release milestone
87+ if (issue.milestone && issue.milestone.number === nextReleaseMilestone.number) {
88+ console.log(`Issue #${issue.number} already has next release milestone`);
89+ continue;
90+ }
91+
92+ // Check if issue was closed by a merged PR
93+ const timelineEvents = await github.rest.issues.listEventsForTimeline({
10194 owner,
10295 repo,
103- issue_number: issue.number,
104- milestone: nextReleaseMilestone.number
105- });
106-
107- console.log(`✅ Successfully assigned milestone to issue #${issue.number}`);
108- } catch (error) {
109- console.error(`❌ Failed to assign milestone to issue #${issue.number}:`, error.message);
96+ issue_number: issue.number
97+ });
98+
99+ const wasMergedToMain = timelineEvents.data.some(event => {
100+ return event.event === 'closed' &&
101+ event.commit_id &&
102+ event.source &&
103+ event.source.issue &&
104+ event.source.issue.pull_request &&
105+ event.source.issue.pull_request.merged_at;
106+ });
107+
108+ if (wasMergedToMain) {
109+ console.log(`${isDryRun ? '🧪 [DRY RUN] Would assign' : 'Assigning'} milestone to issue #${issue.number}: ${issue.title}`);
110+
111+ if (!isDryRun) {
112+ try {
113+ await github.rest.issues.update({
114+ owner,
115+ repo,
116+ issue_number: issue.number,
117+ milestone: nextReleaseMilestone.number
118+ });
119+
120+ console.log(`✅ Successfully assigned milestone to issue #${issue.number}`);
121+ } catch (error) {
122+ console.error(`❌ Failed to assign milestone to issue #${issue.number}:`, error.message);
123+ }
124+ } else {
125+ console.log(`🧪 [DRY RUN] Skipped actual assignment for issue #${issue.number}`);
126+ }
127+ } else {
128+ console.log(`Issue #${issue.number} was not merged to main, skipping`);
110129 }
111- } else {
112- console.log(`Issue #${issue.number} was not merged to main, skipping`);
113- }
114130 }
115131
116- console.log(' Milestone assignment complete!');
132+ console.log(`${isDryRun ? '🧪 [DRY RUN] Test complete!' : ' Milestone assignment complete!'}` );
0 commit comments