Every release commit in the history reads `Release X.Y.Z` and none of them carries the message this script would have written, so nobody has ever passed it `--commit` — and the reason is in the message it wrote: a Co-Authored-By trailer naming an assistant, which no commit in this repository carries and none should. The trailer goes, and so does the paragraph above it: the script's own header already says why it exists, and a release commit is the one place a one-line subject is the whole convention.
107 lines
4.1 KiB
Bash
Executable File
107 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Set the project's version in the few places that must state it, from one
|
|
# source, and optionally commit the result.
|
|
#
|
|
# tools/set-version.sh --from-tag # take it from the latest git tag
|
|
# tools/set-version.sh 0.5.0 # or state it
|
|
# tools/set-version.sh --from-tag --commit
|
|
#
|
|
# Why this exists: the workspace read 0.1.0 for two releases, so every binary
|
|
# reported a version two releases stale, and the APK reported none at all. A
|
|
# version that has to be edited in several files by hand is a version that will
|
|
# be wrong in at least one of them.
|
|
#
|
|
# The number of places is kept deliberately small. Only two files state the
|
|
# version; everything else derives it:
|
|
#
|
|
# Cargo.toml the source of truth for every crate in the workspace
|
|
# packaging/PKGBUILD pacman needs it before anything is built
|
|
#
|
|
# AndroidManifest.xml states NO version on purpose — `package.sh` passes
|
|
# it to `aapt2 link` from Cargo.toml, so the APK cannot
|
|
# drift from the binary inside it.
|
|
set -euo pipefail
|
|
|
|
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "${REPO}"
|
|
|
|
COMMIT=0
|
|
VERSION=""
|
|
for arg in "$@"; do
|
|
case "${arg}" in
|
|
--commit) COMMIT=1 ;;
|
|
--from-tag) VERSION="$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//')" ;;
|
|
-h|--help) sed -n '2,20p' "${BASH_SOURCE[0]}"; exit 0 ;;
|
|
-*) echo "error: unknown flag ${arg}" >&2; exit 2 ;;
|
|
*) VERSION="${arg#v}" ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "${VERSION}" ]]; then
|
|
echo "error: no version given, and no tag to take one from" >&2
|
|
echo "usage: tools/set-version.sh [--from-tag | <version>] [--commit]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
# Rejected rather than coerced. A version is a contract with whoever reads a
|
|
# bug report, and "0.4" or "v0.4.0-wip" silently becoming something else is
|
|
# worse than being told to type it again.
|
|
if ! [[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "error: '${VERSION}' is not MAJOR.MINOR.PATCH" >&2
|
|
exit 2
|
|
fi
|
|
|
|
CURRENT="$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -1)"
|
|
echo "==> ${CURRENT:-unknown} -> ${VERSION}"
|
|
|
|
# 1. The workspace. Every member inherits this through `version.workspace`.
|
|
# Anchored to the [workspace.package] table so a dependency's version
|
|
# string cannot be hit by accident.
|
|
python3 - "${VERSION}" <<'PY'
|
|
import re, sys, pathlib
|
|
version = sys.argv[1]
|
|
p = pathlib.Path("Cargo.toml")
|
|
s = p.read_text()
|
|
m = re.search(r"(\[workspace\.package\][^\[]*?\nversion = \")([^\"]+)(\")", s, re.S)
|
|
if not m:
|
|
sys.exit("error: no version under [workspace.package] in Cargo.toml")
|
|
p.write_text(s[:m.start(2)] + version + s[m.end(2):])
|
|
print(f" Cargo.toml {m.group(2)} -> {version}")
|
|
PY
|
|
|
|
# 2. pacman. `pkgrel` returns to 1: it counts packaging revisions *of one
|
|
# version*, so carrying it across a version bump would claim this is the
|
|
# second package of a version nobody has packaged yet.
|
|
python3 - "${VERSION}" <<'PY'
|
|
import re, sys, pathlib
|
|
version = sys.argv[1]
|
|
p = pathlib.Path("packaging/PKGBUILD")
|
|
s = p.read_text()
|
|
old = re.search(r"^pkgver=(.*)$", s, re.M)
|
|
s = re.sub(r"^pkgver=.*$", f"pkgver={version}", s, count=1, flags=re.M)
|
|
s = re.sub(r"^pkgrel=.*$", "pkgrel=1", s, count=1, flags=re.M)
|
|
p.write_text(s)
|
|
print(f" packaging/PKGBUILD {old.group(1) if old else '?'} -> {version}")
|
|
PY
|
|
|
|
# 3. Cargo.lock. The workspace members appear in it by version, so leaving it
|
|
# alone makes `--locked` builds fail — which is exactly what CI uses.
|
|
cargo update --workspace --offline >/dev/null 2>&1 \
|
|
|| cargo update --workspace >/dev/null 2>&1 \
|
|
|| echo " warning: could not refresh Cargo.lock; run 'cargo update --workspace'" >&2
|
|
echo " Cargo.lock refreshed"
|
|
|
|
echo "==> Android takes its version from Cargo.toml at link time; nothing to edit"
|
|
|
|
if [[ "${COMMIT}" == "1" ]]; then
|
|
git add Cargo.toml Cargo.lock packaging/PKGBUILD
|
|
if git diff --cached --quiet; then
|
|
echo "==> nothing changed; no commit made"
|
|
else
|
|
git commit -q -m "Release ${VERSION}"
|
|
echo "==> committed: $(git log --oneline -1)"
|
|
fi
|
|
else
|
|
echo "==> not committed (pass --commit to commit)"
|
|
fi
|