feat(release): automate versioned releases on main

This commit is contained in:
2026-09-17 18:41:53 +02:00
parent dd37622bb6
commit 92859f3ccf
39 changed files with 7365 additions and 243 deletions

25
scripts/release/lock.mjs Normal file
View File

@@ -0,0 +1,25 @@
import { randomUUID } from 'node:crypto';
import { setTimeout as sleep } from 'node:timers/promises';
import { git } from './lib.mjs';
export const lockRef = 'refs/heads/automation/release-lock';
export async function acquireLock({ runGit = git, wait = sleep, attempts = 480, ref = lockRef } = {}) {
const lockSha = runGit(['-c', 'user.name=FEDEO Release', '-c', 'user.email=release@fedeo.de',
'commit-tree', 'HEAD^{tree}', '-p', 'HEAD', '-m', `chore(release): lock ${randomUUID()}`]);
for (let attempt = 0; attempt < attempts; attempt++) {
const existing = runGit(['ls-remote', 'origin', ref]);
if (!existing) {
try {
// An empty lease means the ref MUST NOT exist, even if another runner just created it.
runGit(['push', `--force-with-lease=${ref}:`, 'origin', `${lockSha}:${ref}`]);
return () => runGit(['push', `--force-with-lease=${ref}:${lockSha}`, 'origin', `:${ref}`]);
} catch (error) {
if (!runGit(['ls-remote', 'origin', ref])) throw error;
}
}
if (attempt % 4 === 0) console.log('Ein anderer Release läuft; warte auf die Git-Sperre.');
await wait(15_000);
}
throw new Error(`Release-Sperre ${ref} bleibt belegt. Siehe docs/releases.md zur Wiederaufnahme.`);
}