Skip to content

Auto-close empty milestones #338

Auto-close empty milestones

Auto-close empty milestones #338

# .github/workflows/auto-close-milestone.yml
name: Auto-close empty milestones
on:
pull_request:
types: [closed] # 병합 완료 PR
issues:
types: [closed] # 수동으로 닫힌 이슈
schedule:
- cron: '0 3 * * *' # 매일 03:00 UTC 점검
jobs:
close-empty:
# PR 이벤트일 때는 merged == true 인 경우만 실행
if: github.event_name != 'pull_request' || github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
issues: write # milestone · issue 수정
steps:
- uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
/**
* PR/Issue/Schedule → 점검 대상 마일스톤 배열 만들기
*/
let targets = [];
if (context.eventName === 'schedule') {
// 모든 열려 있는 마일스톤
const { data } = await github.rest.issues.listMilestones({ owner, repo, state: 'open' });
targets = data;
} else {
const src = context.payload.issue ?? context.payload.pull_request;
if (src && src.milestone) {
targets = [src.milestone];
}
}
if (targets.length === 0) {
return console.log('⏭️ No milestone to check');
}
/**
* 마일스톤마다 열린 이슈/PR 남아있는지 확인 → 없으면 state: closed
*/
for (const ms of targets) {
const { data: openItems } = await github.rest.issues.listForRepo({
owner,
repo,
state: 'open',
milestone: ms.number,
per_page: 1 // 1개만 찾아도 열려있음이 확인됨
});
if (openItems.length === 0) {
await github.rest.issues.updateMilestone({
owner,
repo,
milestone_number: ms.number,
state: 'closed'
});
console.log(`✅ Closed milestone #${ms.number} (${ms.title})`);
} else {
console.log(`🟡 Milestone #${ms.number} still has open items`);
}
}