141 lines
6.0 KiB
YAML
141 lines
6.0 KiB
YAML
name: Weekly compare link to release notes
|
|
|
|
on:
|
|
release:
|
|
types: [published] # run automatically when a (pre-)release is published
|
|
workflow_dispatch: # allow manual runs too
|
|
inputs:
|
|
current_tag:
|
|
description: "Weekly tag (e.g., weekly-2026.01.07). Leave empty to auto-detect latest weekly pre-release."
|
|
required: false
|
|
dry_run:
|
|
description: "Only compute; do not update the release body."
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions:
|
|
contents: write # required to PATCH the release body
|
|
|
|
jobs:
|
|
update-notes:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Inject compare link into weekly release notes
|
|
uses: actions/github-script@v7
|
|
env:
|
|
# Pass manual inputs via env for convenience
|
|
CURRENT_TAG: ${{ github.event.inputs.current_tag }}
|
|
DRY_RUN: ${{ github.event.inputs.dry_run }}
|
|
PLACEHOLDER: "<!--DIFF_LINK-->"
|
|
with:
|
|
script: |
|
|
// Updates the current weekly release notes with a compare link to the previous weekly.
|
|
// Works for both release-published events and manual (workflow_dispatch) runs.
|
|
const owner = context.repo.owner;
|
|
const repo = context.repo.repo;
|
|
|
|
const rx = /^weekly-(\d{4})\.(\d{2})\.(\d{2})$/;
|
|
|
|
// Determine currentTag:
|
|
// 1) Manual input via workflow_dispatch (env.CURRENT_TAG)
|
|
// 2) Tag from release event payload
|
|
// 3) Fallback: newest weekly pre-release
|
|
let currentTag = process.env.CURRENT_TAG || (context.payload?.release?.tag_name) || null;
|
|
|
|
async function detectLatestWeeklyTag() {
|
|
const releases = await github.paginate(github.rest.repos.listReleases, { owner, repo, per_page: 100 });
|
|
const cand = releases.find(r => r.prerelease && typeof r.tag_name === 'string' && rx.test(r.tag_name));
|
|
return cand?.tag_name || null;
|
|
}
|
|
|
|
if (!currentTag) {
|
|
currentTag = await detectLatestWeeklyTag();
|
|
}
|
|
|
|
if (!currentTag || !rx.test(currentTag)) {
|
|
core.info(`No weekly tag detected or tag format mismatch ('${currentTag}'). Skipping.`);
|
|
return;
|
|
}
|
|
|
|
// Resolve the current release object
|
|
let curRel;
|
|
try {
|
|
const { data } = await github.rest.repos.getReleaseByTag({ owner, repo, tag: currentTag });
|
|
curRel = data;
|
|
} catch (e) {
|
|
core.setFailed(`No release for tag ${currentTag}: ${e.message}`);
|
|
return;
|
|
}
|
|
|
|
// If event is a normal release (not pre-release), skip automatically-run case;
|
|
// but allow manual override (manual runs can patch any weekly tag).
|
|
const isManual = context.eventName === 'workflow_dispatch';
|
|
if (!isManual && !curRel.prerelease) {
|
|
core.info('Current release is not a pre-release; skipping (auto run).');
|
|
return;
|
|
}
|
|
|
|
// Helpers
|
|
const toPrevWeeklyTag = (tag) => {
|
|
const [, y, m, d] = tag.match(rx);
|
|
const dt = new Date(Date.UTC(+y, +m - 1, +d));
|
|
const prev = new Date(dt.getTime() - 7 * 24 * 3600 * 1000); // minus 7 days
|
|
const iso = prev.toISOString().slice(0, 10); // YYYY-MM-DD
|
|
return `weekly-${iso.replace(/-/g, '.')}`; // weekly-YYYY.MM.DD
|
|
};
|
|
const ymdKey = (t) => t.replace(rx, '$1$2$3'); // YYYYMMDD
|
|
|
|
async function tagExists(tag) {
|
|
try {
|
|
await github.rest.git.getRef({ owner, repo, ref: `tags/${tag}` });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Compute previous weekly deterministically, then fall back if needed
|
|
let prevTag = toPrevWeeklyTag(currentTag);
|
|
if (!(await tagExists(prevTag))) {
|
|
core.info(`Computed previous tag ${prevTag} not found; scanning older weeklies...`);
|
|
const releases = await github.paginate(github.rest.repos.listReleases, { owner, repo, per_page: 100 });
|
|
const curKey = ymdKey(currentTag);
|
|
const older = releases
|
|
.filter(r => r.prerelease && typeof r.tag_name === 'string' && rx.test(r.tag_name))
|
|
.map(r => ({ tag: r.tag_name, key: ymdKey(r.tag_name) }))
|
|
.filter(x => x.key < curKey)
|
|
.sort((a, b) => b.key.localeCompare(a.key)); // newest older first
|
|
if (!older.length) {
|
|
core.info('No older weekly found; nothing to do.');
|
|
return;
|
|
}
|
|
prevTag = older[0].tag;
|
|
}
|
|
|
|
const compareUrl = `https://github.com/${owner}/${repo}/compare/${prevTag}...${currentTag}`;
|
|
const head = `**Changes since last weekly (${prevTag} → ${currentTag}):**\n${compareUrl}\n`;
|
|
|
|
if (process.env.DRY_RUN === 'true') {
|
|
core.info(`[DRY RUN] Would update release ${currentTag} with: ${compareUrl}`);
|
|
return;
|
|
}
|
|
|
|
// Idempotent body update
|
|
let body = curRel.body || '';
|
|
if (body.includes(compareUrl)) {
|
|
core.info('Compare URL already present; done.');
|
|
return;
|
|
}
|
|
const placeholder = process.env.PLACEHOLDER || '<!--DIFF_LINK-->';
|
|
if (body.includes(placeholder)) {
|
|
body = body.replace(placeholder, compareUrl);
|
|
} else if (/\*\*Changes since last weekly:/i.test(body)) {
|
|
body = body.replace(/\*\*Changes since last weekly:[^\n]*\n?/i, head + '\n');
|
|
} else {
|
|
body += (body.endsWith('\n') ? '\n' : '\n\n') + head;
|
|
}
|
|
|
|
await github.rest.repos.updateRelease({ owner, repo, release_id: curRel.id, body });
|
|
core.info(`Release notes updated with compare link: ${compareUrl}`);
|