diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ea948c3 --- /dev/null +++ b/.github/workflows/release.yml @@ -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 \ No newline at end of file diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..cc0038e --- /dev/null +++ b/.releaserc.json @@ -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" + ] +} \ No newline at end of file diff --git a/.semrel/prepare.mjs b/.semrel/prepare.mjs new file mode 100644 index 0000000..5681689 --- /dev/null +++ b/.semrel/prepare.mjs @@ -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}`); \ No newline at end of file