Skip to content

Commit fb0cc3a

Browse files
committed
refs and cleanup
1 parent 6237ba5 commit fb0cc3a

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
name: milestone-release
2+
3+
on:
4+
milestone:
5+
types: [created, edited, deleted, closed, opened]
6+
issues:
7+
types: [opened, edited, closed, reopened, deleted, milestoned, demilestoned]
8+
pull_request:
9+
types: [opened, edited, closed, reopened, milestoned, demilestoned]
10+
workflow_dispatch:
11+
inputs:
12+
milestone:
13+
description: 'Milestone title to rebuild (leave empty to rebuild all)'
14+
required: false
15+
type: string
16+
17+
permissions:
18+
contents: write
19+
20+
jobs:
21+
sync-release:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Sync Release with Milestone
25+
uses: actions/github-script@v7
26+
with:
27+
script: |
28+
const { owner, repo } = context.repo;
29+
30+
async function syncMilestone(milestone) {
31+
const milestoneTitle = milestone.title;
32+
const milestoneNumber = milestone.number;
33+
34+
// Find existing release by tag_name (includes drafts)
35+
let release = null;
36+
for await (const response of github.paginate.iterator(
37+
github.rest.repos.listReleases,
38+
{ owner, repo, per_page: 100 }
39+
)) {
40+
release = response.data.find(r => r.tag_name === milestoneTitle);
41+
if (release) break;
42+
}
43+
44+
// Fetch all issues and PRs in milestone
45+
const items = [];
46+
for await (const response of github.paginate.iterator(
47+
github.rest.issues.listForRepo,
48+
{ owner, repo, milestone: milestoneNumber, state: 'all', per_page: 100 }
49+
)) {
50+
items.push(...response.data);
51+
}
52+
53+
// Sort by number and generate body
54+
items.sort((a, b) => a.number - b.number);
55+
const body = items.map(item => {
56+
const checkbox = item.state === 'closed' ? '[x]' : '[ ]';
57+
return `- ${checkbox} [#${item.number}](${item.html_url}) ${item.title}`;
58+
}).join('\n');
59+
60+
// Determine if release should be draft or published
61+
const isDraft = milestone.state === 'open';
62+
63+
if (release) {
64+
await github.rest.repos.updateRelease({
65+
owner, repo,
66+
release_id: release.id,
67+
name: milestoneTitle,
68+
body: body || 'No issues in this milestone yet.',
69+
draft: isDraft
70+
});
71+
console.log(`Updated release: ${milestoneTitle}`);
72+
} else {
73+
await github.rest.repos.createRelease({
74+
owner, repo,
75+
tag_name: milestoneTitle,
76+
name: milestoneTitle,
77+
body: body || 'No issues in this milestone yet.',
78+
draft: isDraft
79+
});
80+
console.log(`Created release: ${milestoneTitle}`);
81+
}
82+
}
83+
84+
// Handle manual trigger - rebuild all or specific milestone
85+
if (context.eventName === 'workflow_dispatch') {
86+
const inputMilestone = context.payload.inputs?.milestone;
87+
const milestones = [];
88+
for await (const response of github.paginate.iterator(
89+
github.rest.issues.listMilestones,
90+
{ owner, repo, state: 'all', per_page: 100 }
91+
)) {
92+
milestones.push(...response.data);
93+
}
94+
95+
for (const ms of milestones) {
96+
if (!inputMilestone || ms.title === inputMilestone) {
97+
await syncMilestone(ms);
98+
}
99+
}
100+
return;
101+
}
102+
103+
// Get milestone from event
104+
let milestone = context.payload.milestone;
105+
if (!milestone && context.payload.issue?.milestone) {
106+
milestone = context.payload.issue.milestone;
107+
}
108+
if (!milestone && context.payload.pull_request?.milestone) {
109+
milestone = context.payload.pull_request.milestone;
110+
}
111+
if (!milestone) {
112+
console.log('No milestone associated with this event');
113+
return;
114+
}
115+
116+
const eventAction = context.payload.action;
117+
118+
// Handle milestone deleted
119+
if (context.eventName === 'milestone' && eventAction === 'deleted') {
120+
const milestoneTitle = milestone.title;
121+
let release = null;
122+
for await (const response of github.paginate.iterator(
123+
github.rest.repos.listReleases,
124+
{ owner, repo, per_page: 100 }
125+
)) {
126+
release = response.data.find(r => r.tag_name === milestoneTitle);
127+
if (release) break;
128+
}
129+
if (release) {
130+
await github.rest.repos.deleteRelease({
131+
owner, repo, release_id: release.id
132+
});
133+
console.log(`Deleted release for milestone: ${milestoneTitle}`);
134+
}
135+
return;
136+
}
137+
138+
await syncMilestone(milestone);

0 commit comments

Comments
 (0)