screentinker/scripts/bump-version.sh
ScreenTinker f96b65576f
Some checks failed
CI / Unit tests (node --test) (push) Has been cancelled
CI / OpenAPI spec lint (push) Has been cancelled
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Has been cancelled
CI / Boot smoke + version check (push) Has been cancelled
chore(release): guard bump-version.sh against a diverged origin/main
Add a pre-push fast-forward check: fetch origin/main and abort if it has commits not in local HEAD, BEFORE the annotated tag is created. Prevents the beta9 incident where origin/main had advanced by one commit so 'git push origin main' was rejected, but the tag pushed anyway and fired release.yml from a commit not on main. Best-effort fetch — warns and proceeds when offline (the push stays the backstop).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:26:23 -05:00

87 lines
4.4 KiB
Bash
Executable file

#!/bin/bash
# Bump the ScreenTinker version across every source of truth in one commit + tag.
#
# scripts/bump-version.sh major|minor|patch|X.Y.Z
#
# Updates (and commits together): VERSION (root, the value the server reads at
# runtime), server/package.json + package-lock.json, android versionName
# (+versionCode by 1), tizen/config.xml widget version. Then creates an annotated
# tag vX.Y.Z. Does NOT push - prints the push command, so a release fires
# deliberately (pushing the tag is what triggers the release workflow).
set -euo pipefail
cd "$(dirname "$0")/.."
# Require a clean tree so the version commit can't sweep up unrelated changes.
if [ -n "$(git status --porcelain)" ]; then
echo "ERROR: working tree is dirty - commit or stash before bumping." >&2
exit 1
fi
# Pre-push fast-forward guard. This script creates an annotated tag locally; if
# origin/main has advanced past the commit we're bumping from, `git push origin main`
# is rejected as a non-fast-forward - and if the tag gets pushed anyway it fires the
# release workflow from a commit that isn't even on main (the beta9 divergence
# incident). Catch the divergence HERE, before the tag exists, so nothing can fire.
# Best-effort: when the fetch can't run (offline), warn and proceed rather than block
# a local bump - the push itself is still the backstop.
if git fetch --quiet origin main 2>/dev/null; then
if ! git merge-base --is-ancestor FETCH_HEAD HEAD; then
echo "ERROR: origin/main ($(git rev-parse --short FETCH_HEAD)) has commits not in your" >&2
echo " HEAD ($(git rev-parse --short HEAD)) - 'git push origin main' would be rejected." >&2
echo " Merge origin/main into your branch first, then re-run the bump." >&2
exit 1
fi
else
echo "WARNING: could not fetch origin/main - skipping the fast-forward check (offline?)." >&2
echo " Confirm 'git push origin main' will fast-forward before pushing the tag." >&2
fi
CURRENT="$(cat VERSION)"
IFS=. read -r MAJ MIN PAT <<< "$CURRENT"
case "${1:-}" in
major) NEW="$((MAJ + 1)).0.0" ;;
minor) NEW="${MAJ}.$((MIN + 1)).0" ;;
patch) NEW="${MAJ}.${MIN}.$((PAT + 1))" ;;
[0-9]*.[0-9]*.[0-9]*) NEW="$1" ;;
*) echo "usage: $0 major|minor|patch|X.Y.Z (current: $CURRENT)" >&2; exit 1 ;;
esac
echo "Bumping $CURRENT -> $NEW"
# 1) VERSION (source of truth)
printf '%s\n' "$NEW" > VERSION
# 2) server/package.json version + lockfile (only the top-level "version" key;
# dependency entries are "name": "^x.y.z" and won't match "version": "x.y.z").
# The [^"]* tail also matches a pre-release CURRENT value (e.g. 1.9.1-beta1) so a
# beta1->beta2 bump replaces it instead of silently no-op'ing (issue: stale package.json).
sed -i -E "s/(\"version\"[[:space:]]*:[[:space:]]*)\"[0-9]+\.[0-9]+\.[0-9]+[^\"]*\"/\1\"$NEW\"/" server/package.json
( cd server && npm install --package-lock-only >/dev/null )
# 3) android versionName + versionCode (+1). [0-9][^"]* matches a pre-release current
# value too (e.g. 1.9.1-beta1), so beta1->beta2 actually replaces it.
sed -i -E "s/(versionName[[:space:]]*=[[:space:]]*)\"[0-9][^\"]*\"/\1\"$NEW\"/" android/app/build.gradle.kts
CODE="$(grep -oE 'versionCode[[:space:]]*=[[:space:]]*[0-9]+' android/app/build.gradle.kts | grep -oE '[0-9]+$')"
sed -i -E "s/(versionCode[[:space:]]*=[[:space:]]*)[0-9]+/\1$((CODE + 1))/" android/app/build.gradle.kts
# 4) tizen widget version. Skip the <?xml ...?> declaration line - its
# version="1.0" is the XML FORMAT version, not the app version, and it also
# has a leading space before version= so the guard below would otherwise hit
# it (issue #77). The leading-space guard still excludes tizen:application
# required_version="..." (that's "...d_version", no preceding space).
# #80: Tizen requires a strictly-numeric x.y.z widget version, so a pre-release
# suffix (e.g. 1.9.0-rc1) is invalid and the .wgt fails to sign/install. Strip
# the suffix for config.xml only - the full VERSION (with -rc1/-beta.N) still
# drives the server/Android/package.json version.
NUMERIC="${NEW%%-*}"
sed -i -E "/^<\?xml/! s/([[:space:]]version=\")[0-9][^\"]*(\")/\1${NUMERIC}\2/" tizen/config.xml
# 5) commit + annotated tag (no push)
git add VERSION server/package.json server/package-lock.json android/app/build.gradle.kts tizen/config.xml
git commit -q -m "chore(release): v$NEW"
git tag -a "v$NEW" -m "ScreenTinker v$NEW"
echo
echo "Committed + tagged v$NEW (nothing pushed). To release:"
echo " git push origin main && git push origin v$NEW"