From f16161d38a828a842df4f606abe9f95517d2f513 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Sun, 6 Sep 2026 00:36:01 +0700 Subject: [PATCH] ci(semantic-release): auto versioning via semantic-release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .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. --- .github/workflows/release.yml | 43 +++++++++++++++++++++++++++++++++++ .releaserc.json | 27 ++++++++++++++++++++++ .semrel/prepare.mjs | 36 +++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .releaserc.json create mode 100644 .semrel/prepare.mjs 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