|
11 | 11 | REVIEW_AWAITING: 'Awaiting' |
12 | 12 | REVIEW_FEEDBACK: 'Feedback' |
13 | 13 | REVIEW_MERGE: 'Merge' |
| 14 | + MILESTONE_NAME: ${{ vars.DATABASE_CURRENT_MILESTONE }} |
14 | 15 |
|
15 | 16 | on: |
16 | 17 | pull_request: |
|
85 | 86 | } |
86 | 87 | } |
87 | 88 | closingIssuesReferences(first: 10) { |
88 | | - nodes { id, number } |
| 89 | + nodes { id, number, repository { owner { login }, name } } |
89 | 90 | } |
90 | 91 | } |
91 | 92 | } |
@@ -271,13 +272,74 @@ jobs: |
271 | 272 | // UPDATE LINKED ISSUES |
272 | 273 | // ============================================================ |
273 | 274 |
|
274 | | - const linkedIssues = pr.closingIssuesReferences.nodes; |
| 275 | + // Filter to issues in this repo only (closingIssuesReferences can include cross-repo issues) |
| 276 | + const linkedIssues = pr.closingIssuesReferences.nodes.filter(issue => { |
| 277 | + const isLocal = issue.repository.owner.login === context.repo.owner |
| 278 | + && issue.repository.name === context.repo.repo; |
| 279 | + if (!isLocal) console.log(` Skipping cross-repo issue #${issue.number} (${issue.repository.owner.login}/${issue.repository.name})`); |
| 280 | + return isLocal; |
| 281 | + }); |
| 282 | +
|
| 283 | + // Resolve milestone if configured and PR was merged |
| 284 | + let milestoneNumber = null; |
| 285 | + const milestoneName = process.env.MILESTONE_NAME; |
| 286 | + if (pr.merged && milestoneName) { |
| 287 | + try { |
| 288 | + const milestones = await github.paginate(github.rest.issues.listMilestones, { |
| 289 | + owner: context.repo.owner, |
| 290 | + repo: context.repo.repo, |
| 291 | + state: 'open', |
| 292 | + per_page: 100, |
| 293 | + }); |
| 294 | + const milestone = milestones.find(m => m.title === milestoneName); |
| 295 | + if (milestone) { |
| 296 | + milestoneNumber = milestone.number; |
| 297 | + console.log(`Will set milestone "${milestoneName}" (${milestoneNumber}) on linked issues`); |
| 298 | + } else { |
| 299 | + console.log(`Warning: milestone "${milestoneName}" not found, skipping milestone assignment`); |
| 300 | + } |
| 301 | + } catch (e) { |
| 302 | + console.log(`Warning: failed to resolve milestone "${milestoneName}": ${e.message}`); |
| 303 | + } |
| 304 | + } |
| 305 | +
|
275 | 306 | if (linkedIssues.length > 0) { |
276 | 307 | console.log(`Updating ${linkedIssues.length} linked issue(s)...`); |
277 | 308 | for (const issue of linkedIssues) { |
278 | 309 | const itemId = await ensureInProject(issue.id); |
279 | 310 | await updateField(itemId, fieldOptions.status.id, fieldOptions.status[status]); |
280 | | - console.log(` Issue #${issue.number} → ${status}`); |
| 311 | +
|
| 312 | + if (milestoneNumber) { |
| 313 | + try { |
| 314 | + await github.rest.issues.update({ |
| 315 | + owner: context.repo.owner, |
| 316 | + repo: context.repo.repo, |
| 317 | + issue_number: issue.number, |
| 318 | + milestone: milestoneNumber, |
| 319 | + }); |
| 320 | + console.log(` Issue #${issue.number} → ${status}, milestone "${milestoneName}"`); |
| 321 | + } catch (e) { |
| 322 | + console.log(` Warning: failed to set milestone on #${issue.number}: ${e.message}`); |
| 323 | + console.log(` Issue #${issue.number} → ${status}`); |
| 324 | + } |
| 325 | + } else { |
| 326 | + console.log(` Issue #${issue.number} → ${status}`); |
| 327 | + } |
| 328 | + } |
| 329 | + } |
| 330 | +
|
| 331 | + // If no linked issues, set milestone on the PR itself |
| 332 | + if (linkedIssues.length === 0 && milestoneNumber) { |
| 333 | + try { |
| 334 | + await github.rest.issues.update({ |
| 335 | + owner: context.repo.owner, |
| 336 | + repo: context.repo.repo, |
| 337 | + issue_number: prNumber, |
| 338 | + milestone: milestoneNumber, |
| 339 | + }); |
| 340 | + console.log(`No linked issues — set milestone "${milestoneName}" on PR #${prNumber}`); |
| 341 | + } catch (e) { |
| 342 | + console.log(`Warning: failed to set milestone on PR #${prNumber}: ${e.message}`); |
281 | 343 | } |
282 | 344 | } |
283 | 345 |
|
|
0 commit comments