117 lines
4.1 KiB
YAML
117 lines
4.1 KiB
YAML
name: Mark released issues on close
|
|
|
|
on:
|
|
issues:
|
|
types: [closed]
|
|
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
|
|
jobs:
|
|
mark-released:
|
|
# Run only if closed as completed AND has at least one "status:" label
|
|
if: ${{ github.event.issue.state_reason == 'completed' && contains(toJSON(github.event.issue.labels), 'status:') }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Set "released" label when closed completed and was ready for release
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
// Helpers
|
|
const owner = context.repo.owner;
|
|
const repo = context.repo.repo;
|
|
const issue = context.payload.issue;
|
|
if (!issue) {
|
|
core.info('No issue payload found, skipping.');
|
|
return;
|
|
}
|
|
|
|
// Only proceed if closed as "completed" (not "not_planned")
|
|
const isCompleted = issue.state_reason === 'completed';
|
|
if (!isCompleted) {
|
|
core.info('Issue state_reason is not "completed"; skipping.');
|
|
return;
|
|
}
|
|
|
|
const getName = (l) => typeof l === 'string' ? l : l.name;
|
|
const labels = (issue.labels || []).map(getName);
|
|
|
|
// Status detection (robust to case/spacing)
|
|
const isStatusLabel = (name) => /^status:\s*/i.test(String(name || ''));
|
|
const hasAnyStatus = labels.some(isStatusLabel);
|
|
if (!hasAnyStatus) {
|
|
core.info('Issue has no "status:*" label; skipping.');
|
|
return;
|
|
}
|
|
|
|
const READY_REGEX = /^status:\s*ready\s*for\s*release$/i;
|
|
const hasReady = labels.some((n) => READY_REGEX.test(String(n || '')));
|
|
if (!hasReady) {
|
|
core.info('Issue is not "status: ready for release"; nothing to do.');
|
|
return;
|
|
}
|
|
|
|
const releasedLabel = 'status: released';
|
|
|
|
// Ensure "released" label exists
|
|
try {
|
|
await github.rest.issues.getLabel({ owner, repo, name: releasedLabel });
|
|
} catch (e) {
|
|
if (e.status === 404) {
|
|
core.info(`Label "${releasedLabel}" not found. Creating it.`);
|
|
await github.rest.issues.createLabel({
|
|
owner,
|
|
repo,
|
|
name: releasedLabel,
|
|
color: '184738', // deep green
|
|
description: 'Feature released.'
|
|
});
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
// Remove all status:* labels, add "status: released"
|
|
const filtered = labels.filter((name) => !isStatusLabel(name));
|
|
const newLabels = [...new Set([...filtered, releasedLabel])];
|
|
|
|
await github.rest.issues.update({
|
|
owner,
|
|
repo,
|
|
issue_number: issue.number,
|
|
labels: newLabels
|
|
});
|
|
|
|
core.info(`Issue #${issue.number} relabeled as "${releasedLabel}".`);
|
|
|
|
// === NEW: add release comment with milestone if present ===
|
|
const milestone = issue.milestone;
|
|
if (!milestone || !milestone.title) {
|
|
core.info('Issue has no milestone; no release comment added.');
|
|
return;
|
|
}
|
|
|
|
const commentBody = `Released in version ${milestone.title}.`;
|
|
|
|
// Avoid duplicate comment if the action reruns
|
|
const existingComments = await github.paginate(
|
|
github.rest.issues.listComments,
|
|
{ owner, repo, issue_number: issue.number, per_page: 100 }
|
|
);
|
|
|
|
const alreadyCommented = existingComments.some(c =>
|
|
typeof c.body === 'string' && c.body.trim() === commentBody
|
|
);
|
|
|
|
if (alreadyCommented) {
|
|
core.info('Release comment already present; skipping comment creation.');
|
|
} else {
|
|
await github.rest.issues.createComment({
|
|
owner,
|
|
repo,
|
|
issue_number: issue.number,
|
|
body: commentBody
|
|
});
|
|
core.info('Release comment added.');
|
|
}
|