From 59494e9a86f3529d29def834311f77959656a908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Fr=C3=B3es=20Franco?= Date: Sun, 7 Jun 2026 15:06:33 +0000 Subject: [PATCH] ci(release): stamp the version in-pipeline from a dispatch input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Releasing no longer means hand-editing the version across four files. The Release workflow is now workflow_dispatch with a `version` input: Actions → Run workflow → version: 0.4.0 A new `prepare` job validates the semver, runs scripts/set-version.sh to stamp it into the workspace Cargo.toml, the two internal path-dep pins, g:nuwiki_version, and the Cargo.lock entries for our crates; gates on `cargo test --workspace`; then commits "chore(release): X.Y.Z" and pushes the vX.Y.Z tag. The build matrix and release job run off that freshly pushed tag (checkout ref = the new tag), so the tagged commit carries the real version. scripts/set-version.sh is the single source of truth for where the version lives — run it locally with the same arg to bump by hand. It patches Cargo.lock via awk so it needs no cargo/toolchain. The push:tags trigger is removed (the pipeline now creates the tag itself, so a tag-push trigger would double-fire). Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/release.yaml | 110 ++++++++++++++++++++++++++++++---- scripts/set-version.sh | 57 ++++++++++++++++++ 2 files changed, 154 insertions(+), 13 deletions(-) create mode 100755 scripts/set-version.sh diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index e441163..ac05433 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -1,9 +1,17 @@ name: Release +# Releases are cut from the Actions UI: "Run workflow" → enter the version +# (e.g. 0.4.0). The `prepare` job stamps that version into every hardcoded +# spot (scripts/set-version.sh), commits "chore(release): X.Y.Z", and pushes +# the vX.Y.Z tag. The build matrix + release job then run off that tag. No +# more hand-editing versions across Cargo.toml/plugin/nuwiki.vim. on: - push: - tags: - - 'v*' + workflow_dispatch: + inputs: + version: + description: 'Release version, no leading v (e.g. 0.4.0)' + required: true + type: string permissions: contents: write @@ -14,8 +22,77 @@ env: RUSTFLAGS: -D warnings jobs: + prepare: + name: bump + tag + runs-on: ubuntu-latest + outputs: + version: ${{ steps.stamp.outputs.version }} + tag: ${{ steps.stamp.outputs.tag }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + # Push the release commit + tag back with a write-capable token. + token: ${{ secrets.RELEASE_TOKEN }} + + - uses: dtolnay/rust-toolchain@1.83 + + - name: Cache cargo state + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-prepare-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-prepare- + + - name: Validate version + stamp + id: stamp + env: + VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + ver="${VERSION#v}" # tolerate a leading v + if ! printf '%s' "$ver" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$'; then + echo "::error::'$ver' is not a semver version (expected e.g. 0.4.0)" + exit 1 + fi + tag="v$ver" + if git rev-parse -q --verify "refs/tags/$tag" >/dev/null \ + || git ls-remote --exit-code --tags origin "$tag" >/dev/null 2>&1; then + echo "::error::tag $tag already exists" + exit 1 + fi + bash scripts/set-version.sh "$ver" + echo "version=$ver" >> "$GITHUB_OUTPUT" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + + # Gate: never tag code that doesn't build/test. If this fails nothing + # is committed or pushed, so the release simply doesn't happen. + - name: Test + run: cargo test --workspace + + - name: Commit, tag, push + env: + VER: ${{ steps.stamp.outputs.version }} + TAG: ${{ steps.stamp.outputs.tag }} + run: | + set -euo pipefail + git config user.name "gitea-actions" + git config user.email "gitea-actions@users.noreply.code.gfran.co" + git add Cargo.toml Cargo.lock \ + crates/nuwiki-lsp/Cargo.toml crates/nuwiki-ls/Cargo.toml \ + plugin/nuwiki.vim + git commit -m "chore(release): $VER" + git tag -a "$TAG" -m "nuwiki $VER" + git push origin "HEAD:${GITHUB_REF_NAME}" + git push origin "$TAG" + build: name: build ${{ matrix.target }} + needs: prepare runs-on: ubuntu-latest strategy: fail-fast: false @@ -38,6 +115,9 @@ jobs: rustflags: "-D warnings -C linker=rust-lld -C link-self-contained=yes" steps: - uses: actions/checkout@v4 + with: + # Build the just-tagged commit, not the branch tip. + ref: ${{ needs.prepare.outputs.tag }} - uses: dtolnay/rust-toolchain@1.83 with: @@ -72,10 +152,11 @@ jobs: - name: Package archive id: package + env: + VERSION: ${{ needs.prepare.outputs.version }} run: | set -euo pipefail - version="${GITHUB_REF_NAME#v}" - archive="nuwiki-ls-${version}-${{ matrix.target }}.tar.gz" + archive="nuwiki-ls-${VERSION}-${{ matrix.target }}.tar.gz" tar -czf "$archive" -C "target/${{ matrix.target }}/release" nuwiki-ls # Use a stable name without version so /releases/latest/download/nuwiki-ls-{target}.tar.gz always resolves. stable="nuwiki-ls-${{ matrix.target }}.tar.gz" @@ -91,11 +172,12 @@ jobs: release: name: gitea release - needs: build + needs: [prepare, build] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: + ref: ${{ needs.prepare.outputs.tag }} fetch-depth: 0 - name: Download all build artifacts @@ -116,18 +198,20 @@ jobs: - name: Generate release notes id: notes + env: + TAG: ${{ needs.prepare.outputs.tag }} run: | set -euo pipefail - # Get commits since the previous tag (or the first commit if this is the first release). - prev_tag=$(git tag --sort=-version:refname | awk "NR==2" || echo "") + git fetch --tags --force origin >/dev/null 2>&1 || true + # Previous tag = newest tag that isn't the one we just cut. + prev_tag=$(git tag --sort=-version:refname | grep -vxF "$TAG" | head -n1 || echo "") if [ -n "$prev_tag" ]; then - log=$(git log --oneline "$prev_tag"..HEAD) + log=$(git log --oneline "$prev_tag..$TAG") else - log=$(git log --oneline --max-parents=0..HEAD) + log=$(git log --oneline "$TAG") fi - # Write notes to a file for later use. cat > release-notes.txt < e.g. $0 0.4.0" >&2 + exit 2 +fi + +root="$(cd "$(dirname "$0")/.." && pwd)" +cd "$root" + +# 1. Workspace package version (root Cargo.toml, [workspace.package]). +sed -i -E "s/^version = \"[0-9][^\"]*\"/version = \"$ver\"/" Cargo.toml + +# 2-3. Internal path-dep pins must track the workspace version. +sed -i -E "s|(nuwiki-core = \{ path = \"\.\./nuwiki-core\", version = )\"[^\"]*\"|\1\"$ver\"|" \ + crates/nuwiki-lsp/Cargo.toml +sed -i -E "s|(nuwiki-lsp = \{ path = \"\.\./nuwiki-lsp\", version = )\"[^\"]*\"|\1\"$ver\"|" \ + crates/nuwiki-ls/Cargo.toml + +# 4. VimL client version global (the Lua client reads this same g: var). +sed -i -E "s/(let g:nuwiki_version = ')[^']*(')/\1$ver\2/" plugin/nuwiki.vim + +# 5. Cargo.lock entries for our own crates. Each [[package]] block lists the +# name line immediately followed by the version line; rewrite only those. +for crate in nuwiki-core nuwiki-lsp nuwiki-ls; do + awk -v c="$crate" -v v="$ver" ' + $0 == "name = \"" c "\"" { + print + if (getline > 0) { sub(/version = "[^"]*"/, "version = \"" v "\"") } + print + next + } + { print } + ' Cargo.lock > Cargo.lock.tmp && mv Cargo.lock.tmp Cargo.lock +done + +echo "Set version to $ver"