ci(semantic-release): auto versioning via semantic-release
- .releaserc.json: commit-analyzer + release-notes-generator + changelog + exec (prepare) + git + github plugins. - .semrel/prepare.mjs: syncs next release version into package.json AND flake.nix before the release commit — guarantees a fresh Nix store path on every deploy (fixes the trap where a source change without a version bump reused the same store path and CI silently deployed stale code). - release.yml: runs semantic-release on main push (GITHUB_TOKEN, isolated /tmp/sr-tools install so package.json/bun.lock stay clean). - Release commit (no [skip ci]) triggers deploy.yml → auto build+deploy. - Baseline tag v1.1.1 backfilled at current HEAD.
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
name: Semantic Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
if: github.actor != 'dependabot[bot]'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
# semantic-release needs the full history + tags to analyse commits
|
||||||
|
persist-credentials: true
|
||||||
|
|
||||||
|
- uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: "1.3.14"
|
||||||
|
|
||||||
|
- name: Install semantic-release (isolated, no package.json pollution)
|
||||||
|
run: |
|
||||||
|
npm install --prefix /tmp/sr-tools \
|
||||||
|
semantic-release@^24 \
|
||||||
|
@semantic-release/changelog@^6 \
|
||||||
|
@semantic-release/exec@^6 \
|
||||||
|
@semantic-release/git@^10
|
||||||
|
echo "/tmp/sr-tools/node_modules/.bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Run semantic-release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: semantic-release
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"branches": ["main"],
|
||||||
|
"plugins": [
|
||||||
|
"@semantic-release/commit-analyzer",
|
||||||
|
"@semantic-release/release-notes-generator",
|
||||||
|
[
|
||||||
|
"@semantic-release/changelog",
|
||||||
|
{
|
||||||
|
"changelogFile": "CHANGELOG.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@semantic-release/exec",
|
||||||
|
{
|
||||||
|
"prepareCmd": "node ./.semrel/prepare.mjs '${nextRelease.version}' && bunx biome check package.json flake.nix CHANGELOG.md 2>/dev/null || true"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@semantic-release/git",
|
||||||
|
{
|
||||||
|
"assets": ["package.json", "flake.nix", "CHANGELOG.md"],
|
||||||
|
"message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@semantic-release/github"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Prepare script for semantic-release: synchronises the package version
|
||||||
|
* across package.json and flake.nix before the release commit.
|
||||||
|
*
|
||||||
|
* getVersion() keeps package.json as the source of truth when semver has
|
||||||
|
* not run yet (e.g. first bootstrap) — after that, nextRelease.version
|
||||||
|
* is passed as argv[2] and everything is aligned to it.
|
||||||
|
*
|
||||||
|
* Fixes the TeleUploader Nix deploy trap: a source change without a
|
||||||
|
* version bump made Nix reuse the same store path, so CI reported success
|
||||||
|
* while the VPS profile stayed on the old build. By bumping flake.nix's
|
||||||
|
* `version` on every release, the Nix derivation always changes and the
|
||||||
|
* store path is always fresh.
|
||||||
|
*/
|
||||||
|
import { readFileSync, writeFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
const nextVersion = process.argv[2];
|
||||||
|
|
||||||
|
// --- package.json ---
|
||||||
|
const pkgPath = 'package.json';
|
||||||
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
||||||
|
if (nextVersion) pkg.version = nextVersion;
|
||||||
|
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
||||||
|
|
||||||
|
// --- flake.nix ---
|
||||||
|
const flakePath = 'flake.nix';
|
||||||
|
let flake = readFileSync(flakePath, 'utf8');
|
||||||
|
const target = nextVersion ?? pkg.version;
|
||||||
|
const replaced = flake.replace(/version = "\d+\.\d+\.\d+";/, `version = "${target}";`);
|
||||||
|
if (!replaced.includes(`version = "${target}";`)) {
|
||||||
|
throw new Error(`prepare.mjs: could not update version in ${flakePath} (pattern not found)`);
|
||||||
|
}
|
||||||
|
writeFileSync(flakePath, replaced);
|
||||||
|
|
||||||
|
console.log(`prepare.mjs: versions synced to ${target}`);
|
||||||
Reference in New Issue
Block a user