Adds an automatic compare link to the weekly release notes.

This commit is contained in:
Max Wilfinger
2026-01-07 15:43:31 +01:00
committed by Chris Hennes
parent d397254275
commit 4b13f5257f
2 changed files with 142 additions and 1 deletions

View File

@@ -1,6 +1,7 @@
> [!IMPORTANT]
> Bleeding edge FreeCAD development builds for testing bugfixes, regressions, and recently implemented features. Do not use in a production environment.
**Changes since last weekly:** <!--DIFF_LINK-->
### How-to use
@@ -8,7 +9,7 @@
2. Unpack the bundle to any folder on your system
3. Launch the application
- **Windows**
Run `\bin\FreeCAD.exe` in the extracted directory
Run `\FreeCAD.exe` in the extracted directory
- **macOS**
Launch `/FreeCAD.app` in the extracted directory
- **Linux**

View File

@@ -0,0 +1,140 @@
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}`);