Skip to content

Update Directory.Build.props #3

Update Directory.Build.props

Update Directory.Build.props #3

name: milestone-release
on:
push:
tags:
- '*'
milestone:
types: [created, edited, closed, opened]
issues:
types: [opened, edited, closed, reopened, deleted, milestoned, demilestoned]
pull_request:
types: [opened, edited, closed, reopened, milestoned, demilestoned]
workflow_dispatch:
inputs:
milestone:
description: 'Milestone title to rebuild (leave empty to rebuild all)'
required: false
type: string
permissions:
contents: write
jobs:
sync-release:
runs-on: ubuntu-latest
steps:
- name: Sync Release with Milestone
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
// Helper: Find release by tag name
async function findRelease(tagName) {
for await (const response of github.paginate.iterator(
github.rest.repos.listReleases,
{ owner, repo, per_page: 100 }
)) {
const release = response.data.find(r => r.tag_name === tagName);
if (release) return release;
}
return null;
}
// Helper: Find milestone by title
async function findMilestone(title) {
for await (const response of github.paginate.iterator(
github.rest.issues.listMilestones,
{ owner, repo, state: 'all', per_page: 100 }
)) {
const milestone = response.data.find(m => m.title === title);
if (milestone) return milestone;
}
return null;
}
// Helper: Generate release body from milestone
async function generateBody(milestoneNumber) {
const items = [];
for await (const response of github.paginate.iterator(
github.rest.issues.listForRepo,
{ owner, repo, milestone: milestoneNumber, state: 'all', per_page: 100 }
)) {
items.push(...response.data);
}
items.sort((a, b) => a.number - b.number);
return items.map(item => {
const checkbox = item.state === 'closed' ? '[x]' : '[ ]';
return `- ${checkbox} [#${item.number}](${item.html_url}) ${item.title}`;
}).join('\n') || 'No issues in this milestone yet.';
}
// Helper: Update existing release only
async function updateReleaseIfExists(milestone) {
const release = await findRelease(milestone.title);
if (!release) {
console.log(`No release found for ${milestone.title}, skipping`);
return;
}
const body = await generateBody(milestone.number);
await github.rest.repos.updateRelease({
owner, repo,
release_id: release.id,
body: body
});
console.log(`Updated release: ${milestone.title}`);
}
// Handle tag push events
if (context.eventName === 'push' && context.ref.startsWith('refs/tags/')) {
const tagName = context.ref.replace('refs/tags/', '');
// Tag deleted
if (context.payload.deleted) {
const release = await findRelease(tagName);
if (release) {
await github.rest.repos.deleteRelease({
owner, repo, release_id: release.id
});
console.log(`Deleted release for tag: ${tagName}`);
}
return;
}
// Tag created - create release
const milestone = await findMilestone(tagName);
const body = milestone
? await generateBody(milestone.number)
: '';
await github.rest.repos.createRelease({
owner, repo,
tag_name: tagName,
name: tagName,
body: body,
draft: false
});
console.log(`Created release for tag: ${tagName}`);
return;
}
// Handle workflow_dispatch - update only
if (context.eventName === 'workflow_dispatch') {
const inputMilestone = context.payload.inputs?.milestone;
const milestones = [];
for await (const response of github.paginate.iterator(
github.rest.issues.listMilestones,
{ owner, repo, state: 'all', per_page: 100 }
)) {
milestones.push(...response.data);
}
for (const ms of milestones) {
if (!inputMilestone || ms.title === inputMilestone) {
await updateReleaseIfExists(ms);
}
}
return;
}
// Handle milestone/issue/PR events - update only
let milestone = context.payload.milestone;
if (!milestone && context.payload.issue?.milestone) {
milestone = context.payload.issue.milestone;
}
if (!milestone && context.payload.pull_request?.milestone) {
milestone = context.payload.pull_request.milestone;
}
if (!milestone) {
console.log('No milestone associated with this event');
return;
}
await updateReleaseIfExists(milestone);