#!/bin/bash # Stamp the release version into every file that carries it. # # The git tag is the single source of truth for a release version. The versions # committed in package.json / tauri.conf.json / Cargo.toml are a placeholder for # dev builds; a tagged build overwrites all of them from the tag so they cannot # disagree with each other or with the tag. # # Usage: # ./scripts/set-version.sh 0.5.0 # explicit # JELLYTAU_VERSION=0.5.0 ./scripts/set-version.sh # ./scripts/set-version.sh # derive from git describe (dev builds) # # Accepts the version with or without a leading "v". # # Why a script and not four sed lines in CI: the version lived in four files and # CI only ever rewrote one of them (tauri.conf.json), so a tagged release shipped # a matching installer name and mismatched package metadata. Keeping the write in # one place is what makes "the tag is authoritative" actually true. set -euo pipefail cd "$(dirname "$0")/.." VERSION="${1:-${JELLYTAU_VERSION:-}}" # CI passes "${GITHUB_REF#refs/tags/}" unconditionally, which on an untagged # build is still a full ref ("refs/heads/master"). Treat anything that is not a # bare version as "no version given" and fall through to git describe, so a # branch build gets a sane dev version instead of failing the job. case "$VERSION" in refs/*) VERSION="" ;; esac if [ -z "$VERSION" ]; then # No explicit version: derive from the most recent tag. Dev builds land on # something like 0.5.0 (exact tag) or 0.5.0-3-gabc1234 (ahead of the tag). VERSION="$(git describe --tags --always --match 'v*' 2>/dev/null || echo "0.0.0")" fi # Tags are written v0.5.0; the files carry a bare semver. VERSION="${VERSION#v}" # Validate before writing anything — a malformed version silently propagated # into four files is far worse than a failed script. if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$'; then echo "❌ Not a valid semver: '$VERSION'" >&2 echo " Expected MAJOR.MINOR.PATCH with an optional -prerelease/+build suffix." >&2 exit 1 fi echo "📌 Setting version to $VERSION" # --- The three committed manifests ----------------------------------------- # Anchored to the first "version" key so a dependency's version is never hit. # package.json — the top-level "version", which sits in the first few lines. perl -0pi -e 's/("version"\s*:\s*)"[^"]*"/$1"'"$VERSION"'"/' package.json # tauri.conf.json — likewise; this is the one the bundler reads for installer # names, and the one CI used to patch alone. perl -0pi -e 's/("version"\s*:\s*)"[^"]*"/$1"'"$VERSION"'"/' src-tauri/tauri.conf.json # Cargo.toml — only the [package] version, never a dependency's. Restricted to # the first occurrence of a line-anchored `version = "..."`. perl -0pi -e 's/^(version\s*=\s*)"[^"]*"/$1"'"$VERSION"'"/m' src-tauri/Cargo.toml # Cargo.lock — the jellytau entry. Left alone if the lock has not been generated # yet; the next cargo invocation writes it. Cargo would otherwise rewrite the # lock mid-build and dirty the tree. if [ -f src-tauri/Cargo.lock ]; then perl -0pi -e 's/(name = "jellytau"\nversion = )"[^"]*"/$1"'"$VERSION"'"/' src-tauri/Cargo.lock fi # --- Android versionCode ---------------------------------------------------- # Only when the generated Android project exists (i.e. after `tauri android # init`); on Linux/Windows jobs there is nothing to stamp. # # `tauri android init` derives a versionCode from the semver (0.0.15 -> 15). # That is both tiny and NOT monotonic across our history: earlier local/dev # builds shipped versionCode 1000 (from a 0.1.0 config), so a plain 15 is a # *downgrade* and Android refuses the update. # # The floor has to clear the highest code actually in the field, which is not the # same as the highest this formula has produced. v0.5.2 shipped versionCode # **5002** under an earlier `minor*1000` scheme; the `minor*100` formula that # replaced it yields only 1502 for that same version, and 1503 for 0.5.3 — so # every 0.5.x release built from it was an un-installable downgrade for anyone # already on v0.5.2, which is exactly the failure this block exists to prevent. # The multipliers are widened and the floor raised past 5002 accordingly. # # code = 10000 + major*1000000 + minor*1000 + patch # e.g. 0.0.14 -> 10014, 0.1.0 -> 11000, 0.5.3 -> 15003, 1.0.0 -> 1010000. PROPS="src-tauri/gen/android/app/tauri.properties" if [ -f "$PROPS" ]; then # Strip any -rc1/+build suffix first: it is not numeric, and feeding it to # $(( )) would abort the script under `set -e`. CORE="${VERSION%%-*}" CORE="${CORE%%+*}" MAJ=$(echo "$CORE" | cut -d. -f1) MIN=$(echo "$CORE" | cut -d. -f2) PAT=$(echo "$CORE" | cut -d. -f3) : "${MAJ:=0}" "${MIN:=0}" "${PAT:=0}" CODE=$(( 10000 + MAJ*1000000 + MIN*1000 + PAT )) echo " versionCode=$CODE (from $CORE)" if grep -q '^tauri.android.versionCode=' "$PROPS"; then sed -i "s/^tauri.android.versionCode=.*/tauri.android.versionCode=$CODE/" "$PROPS" else echo "tauri.android.versionCode=$CODE" >> "$PROPS" fi fi # --- Report ----------------------------------------------------------------- echo "✅ Version stamped:" grep -m1 '"version"' package.json | sed 's/^/ package.json: /' grep -m1 '"version"' src-tauri/tauri.conf.json | sed 's/^/ tauri.conf.json: /' grep -m1 '^version' src-tauri/Cargo.toml | sed 's/^/ Cargo.toml: /' [ -f "$PROPS" ] && grep '^tauri.android.versionCode=' "$PROPS" | sed 's/^/ tauri.properties: /' exit 0