Add workflow to create release discussions from labels
This commit is contained in:
committed by
GitHub
parent
913b79d682
commit
de2b15d89e
163
.github/workflows/.github/workflows/label-to-release-announcement.yml
vendored
Normal file
163
.github/workflows/.github/workflows/label-to-release-announcement.yml
vendored
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
name: Create a release discussions for each new version label
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 * * * *" # every hour
|
||||||
|
workflow_dispatch: # allow manual runs
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
discussions: write
|
||||||
|
issues: read
|
||||||
|
pull-requests: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
reconcile:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Reconcile release/* labels → discussions
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const owner = context.repo.owner;
|
||||||
|
const repo = context.repo.repo;
|
||||||
|
const categoryName = "Releases";
|
||||||
|
|
||||||
|
// 24h cutoff
|
||||||
|
const since = new Date(Date.now() - 24*60*60*1000).toISOString();
|
||||||
|
core.info(`Scanning issues/PRs updated since ${since}`);
|
||||||
|
|
||||||
|
// fetch repo + discussion categories
|
||||||
|
const repoData = await github.graphql(`
|
||||||
|
query($owner:String!, $repo:String!){
|
||||||
|
repository(owner:$owner, name:$repo){
|
||||||
|
id
|
||||||
|
discussionCategories(first:100){ nodes { id name } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, { owner, repo });
|
||||||
|
const repoId = repoData.repository.id;
|
||||||
|
const category = repoData.repository.discussionCategories.nodes.find(c => c.name === categoryName);
|
||||||
|
if (!category) {
|
||||||
|
core.setFailed(`Discussion category "${categoryName}" not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const categoryId = category.id;
|
||||||
|
|
||||||
|
// paginate issues/PRs updated in last 24h
|
||||||
|
for await (const { data: items } of github.paginate.iterator(
|
||||||
|
github.rest.issues.listForRepo,
|
||||||
|
{ owner, repo, state: "all", since, per_page: 100 }
|
||||||
|
)) {
|
||||||
|
for (const item of items) {
|
||||||
|
const releaseLabels = (item.labels || [])
|
||||||
|
.map(l => (typeof l === "string" ? l : l.name)) // always get the name
|
||||||
|
.filter(n => typeof n === "string" && n.startsWith("release/"));
|
||||||
|
if (releaseLabels.length === 0) continue;
|
||||||
|
|
||||||
|
core.info(`#${item.number}: ${releaseLabels.join(", ")}`);
|
||||||
|
|
||||||
|
for (const labelName of releaseLabels) {
|
||||||
|
const version = labelName.substring("release/".length);
|
||||||
|
const titleTarget = `Release: ${version}`;
|
||||||
|
|
||||||
|
// search discussions
|
||||||
|
let discussionId = null;
|
||||||
|
let cursor = null;
|
||||||
|
while (true) {
|
||||||
|
const page = await github.graphql(`
|
||||||
|
query($owner:String!, $repo:String!, $cursor:String){
|
||||||
|
repository(owner:$owner, name:$repo){
|
||||||
|
discussions(first:50, after:$cursor){
|
||||||
|
nodes{
|
||||||
|
id
|
||||||
|
title
|
||||||
|
url
|
||||||
|
category{ name }
|
||||||
|
labels(first:50){ nodes{ name } }
|
||||||
|
}
|
||||||
|
pageInfo{ hasNextPage endCursor }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, { owner, repo, cursor });
|
||||||
|
const nodes = page.repository.discussions.nodes;
|
||||||
|
const byLabel = nodes.find(d =>
|
||||||
|
d.category?.name === categoryName &&
|
||||||
|
d.labels?.nodes?.some(l => l.name === labelName)
|
||||||
|
);
|
||||||
|
if (byLabel) { discussionId = byLabel.id; break; }
|
||||||
|
const byTitle = nodes.find(d =>
|
||||||
|
d.category?.name === categoryName &&
|
||||||
|
d.title === titleTarget
|
||||||
|
);
|
||||||
|
if (byTitle) { discussionId = byTitle.id; break; }
|
||||||
|
if (!page.repository.discussions.pageInfo.hasNextPage) break;
|
||||||
|
cursor = page.repository.discussions.pageInfo.endCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!discussionId) {
|
||||||
|
core.info(`→ Creating discussion for ${labelName}`);
|
||||||
|
const body =
|
||||||
|
`**Release date:** TODO (YYYY-MM-DD)\n\n` +
|
||||||
|
`### Links\n` +
|
||||||
|
`- [Issues and pull requests marked for version ${version}](https://github.com/${owner}/${repo}/issues?q=label%3A${encodeURIComponent(labelName)})\n`;
|
||||||
|
|
||||||
|
const created = await github.graphql(`
|
||||||
|
mutation($repoId:ID!, $catId:ID!, $title:String!, $body:String!){
|
||||||
|
createDiscussion(input:{
|
||||||
|
repositoryId:$repoId,
|
||||||
|
categoryId:$catId,
|
||||||
|
title:$title,
|
||||||
|
body:$body
|
||||||
|
}){ discussion{ id url } }
|
||||||
|
}
|
||||||
|
`, { repoId, catId: categoryId, title: titleTarget, body });
|
||||||
|
|
||||||
|
discussionId = created.createDiscussion.discussion.id;
|
||||||
|
|
||||||
|
// lock the discussion to prevent replies
|
||||||
|
await github.graphql(`
|
||||||
|
mutation($id:ID!){
|
||||||
|
lockLockable(input:{ lockableId:$id }) {
|
||||||
|
clientMutationId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, { id: discussionId });
|
||||||
|
core.info(`🔒 Locked discussion ${discussionId}`);
|
||||||
|
} else {
|
||||||
|
core.info(`→ Found existing discussion for ${labelName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure label exists
|
||||||
|
let labelId;
|
||||||
|
try {
|
||||||
|
await github.rest.issues.getLabel({ owner, repo, name: labelName });
|
||||||
|
} catch (e) {
|
||||||
|
if (e.status === 404) {
|
||||||
|
await github.rest.issues.createLabel({
|
||||||
|
owner, repo, name: labelName, color: "0E8A16"
|
||||||
|
});
|
||||||
|
} else { throw e; }
|
||||||
|
}
|
||||||
|
const labelNode = await github.graphql(`
|
||||||
|
query($owner:String!, $repo:String!, $name:String!){
|
||||||
|
repository(owner:$owner, name:$repo){ label(name:$name){ id } }
|
||||||
|
}
|
||||||
|
`, { owner, repo, name: labelName });
|
||||||
|
labelId = labelNode.repository.label?.id;
|
||||||
|
if (!labelId) continue;
|
||||||
|
|
||||||
|
// add label to discussion
|
||||||
|
await github.graphql(`
|
||||||
|
mutation($id:ID!, $labels:[ID!]!){
|
||||||
|
addLabelsToLabelable(input:{ labelableId:$id, labelIds:$labels }) {
|
||||||
|
clientMutationId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, { id: discussionId, labels: [labelId] });
|
||||||
|
|
||||||
|
core.info(`✓ ${labelName} attached to discussion`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user