#!/usr/bin/env bash # ============================================================ # PedScribe release helper — one command to tag a new version. # # Usage: # scripts/release.sh 6.1.1 # bump to 6.1.1 # scripts/release.sh 6.1.1 --push # also git push + tag push # # What it does: # 1. Updates version in root package.json and package-lock.json (requires Node) # 2. Commits the version bump # 3. (optional) git push + push the new tag # # It does NOT: # - Build the Docker image (run `./scripts/build-image.sh`/`docker compose up -d --no-build` yourself # or wire it to a deploy script / CI hook) # - Touch anything Android. The Capacitor project and its APK build were # removed; if they come back, restore the version-bump lines from git # history along with them. # ============================================================ set -euo pipefail VERSION="${1:-}" PUSH=false for arg in "${@:2}"; do case "$arg" in --push) PUSH=true ;; esac done if [[ -z "$VERSION" ]]; then echo "usage: $0 [--push]" echo " example: $0 6.1.1 --push" exit 1 fi if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "ERROR: version must be semver X.Y.Z (e.g. 6.1.1). got: $VERSION" >&2 exit 1 fi ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" if [[ -n "$(git status --porcelain)" ]]; then echo "ERROR: working tree has uncommitted changes. commit or stash first." >&2 git status --short >&2 exit 1 fi echo "==> Bumping to v$VERSION" # Parse every manifest before writing; change metadata only, never resolve dependencies. # Replace the lock atomically so its two version fields cannot be partially updated. node - "$VERSION" <<'NODE' const fs = require('node:fs'); const version = process.argv[2]; const files = ['package.json', 'package-lock.json']; const updates = files.map(file => [file, JSON.parse(fs.readFileSync(file, 'utf8'))]); for (const [file, data] of updates) { data.version = version; if (file === 'package-lock.json') data.packages[''].version = version; } for (const [file, data] of updates) { fs.writeFileSync(file + '.tmp', JSON.stringify(data, null, 2) + '\n'); fs.renameSync(file + '.tmp', file); } NODE echo " updated package.json and package-lock.json" # Commit git add package.json package-lock.json git commit -m "Release v${VERSION}" echo " committed" # Tag git tag -a "v${VERSION}" -m "Release v${VERSION}" echo " tagged v${VERSION}" if $PUSH; then # This repo's remote is "forgejo", not "origin". Prefer forgejo, fall back # to origin, otherwise use the only remote there is — so this keeps working # if the remote is ever renamed. Pushing the tag is what makes CI attach the # signed APK to a Forgejo release for Obtainium; the branch push alone only # builds it as a 30-day workflow artifact. REMOTE="" for candidate in forgejo origin; do if git remote get-url "$candidate" >/dev/null 2>&1; then REMOTE="$candidate"; break; fi done if [[ -z "$REMOTE" ]]; then REMOTE=$(git remote | head -1) fi if [[ -z "$REMOTE" ]]; then echo "ERROR: no git remote configured — cannot push. Commit and tag are still local." >&2 exit 1 fi git push "$REMOTE" HEAD git push "$REMOTE" "v${VERSION}" echo " pushed to $REMOTE" fi echo "==> Done. v$VERSION."