diff --git a/README.md b/README.md index a466bd4..e7b87ad 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,9 @@ If you want `git push origin master` to build/sign/notarize/upload automatically git config core.hooksPath hooks ``` -The hook (see `hooks/pre-push`) watches for pushes that include `refs/heads/master`, runs `scripts/build_release.sh`, and automatically commits the updated `Sparkle/appcast.xml` so it travels with your release. To skip the automation temporarily, prepend `SKIP_RELEASE=1` to your `git push` command. +The hook (see `hooks/pre-push`) watches for pushes that include `refs/heads/master`, automatically bumps `marketing_version` (incrementing the last component), runs `scripts/build_release.sh`, stages `version.json`, `iKeyMon.xcodeproj/project.pbxproj`, and `Sparkle/appcast.xml`, then creates a commit `chore: release `. It performs its own `git push` behind the scenes and cancels the original push command so you don't upload the same refs twice—once you see “Release … pushed. Original push cancelled”, you're done (Git will report the original push failed; that's expected). To skip the automation temporarily, prepend `SKIP_RELEASE=1` to your `git push` command. + +The bumping logic lives in `scripts/bump_version.sh` (feel free to run it manually if you need to create a release without pushing). ### Versioning workflow diff --git a/hooks/pre-push b/hooks/pre-push index c9c3861..1c3d4b9 100755 --- a/hooks/pre-push +++ b/hooks/pre-push @@ -2,6 +2,7 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +REMOTE_NAME="${1:-origin}" if [[ -n "${SKIP_RELEASE:-}" ]]; then echo "⚙️ SKIP_RELEASE set — skipping automated release build." @@ -9,11 +10,14 @@ if [[ -n "${SKIP_RELEASE:-}" ]]; then fi should_release=false +release_local_ref="" +release_remote_ref="" while read -r local_ref local_sha remote_ref remote_sha; do [[ -z "${local_ref:-}" ]] && continue if [[ "$local_ref" == "refs/heads/master" || "$remote_ref" == "refs/heads/master" ]]; then should_release=true - break + release_local_ref="$local_ref" + release_remote_ref="${remote_ref:-refs/heads/master}" fi done @@ -21,21 +25,29 @@ if [[ "$should_release" != true ]]; then exit 0 fi -echo "🚀 Pre-push hook detected master — running release build..." +echo "🚀 Detected push to master — bumping version and building release..." +NEW_VERSION="$("$ROOT_DIR/scripts/bump_version.sh")" +echo "🔢 marketing_version -> ${NEW_VERSION}" +"$ROOT_DIR/scripts/sync_version.sh" + +git -C "$ROOT_DIR" add "$ROOT_DIR/version.json" "$ROOT_DIR/iKeyMon.xcodeproj/project.pbxproj" + "$ROOT_DIR/scripts/build_release.sh" -APPCAST_PATH="$ROOT_DIR/Sparkle/appcast.xml" -if ! git -C "$ROOT_DIR" diff --quiet "$APPCAST_PATH"; then - git -C "$ROOT_DIR" add "$APPCAST_PATH" - VERSION="$(python3 - "$ROOT_DIR/version.json" <<'PY' -import json, sys -with open(sys.argv[1], "r", encoding="utf-8") as handle: - data = json.load(handle) -print(data.get("marketing_version", "dev")) -PY -)" - git -C "$ROOT_DIR" commit -m "chore: update Sparkle appcast for ${VERSION}" >/dev/null - echo "📝 Committed updated Sparkle appcast for ${VERSION}." +git -C "$ROOT_DIR" add "$ROOT_DIR/version.json" "$ROOT_DIR/iKeyMon.xcodeproj/project.pbxproj" "$ROOT_DIR/Sparkle/appcast.xml" + +if git -C "$ROOT_DIR" diff --cached --quiet; then + echo "⚠️ No release changes detected; skipping release commit." +else + git -C "$ROOT_DIR" commit -m "chore: release ${NEW_VERSION}" + echo "📝 Committed release ${NEW_VERSION}." fi -echo "✅ Release build complete — continuing push." +echo "📤 Pushing release commit..." +if SKIP_RELEASE=1 git -C "$ROOT_DIR" push "$REMOTE_NAME" "${release_local_ref:-refs/heads/master}:${release_remote_ref:-refs/heads/master}"; then + echo "✅ Release ${NEW_VERSION} pushed. Original push cancelled (already done)." + exit 1 +else + echo "❌ Failed to push release ${NEW_VERSION}. Please resolve manually." + exit 1 +fi diff --git a/scripts/bump_version.sh b/scripts/bump_version.sh new file mode 100755 index 0000000..92c454c --- /dev/null +++ b/scripts/bump_version.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VERSION_FILE="$ROOT_DIR/version.json" + +new_version="$(python3 - "$VERSION_FILE" <<'PY' +import json, sys, pathlib +path = pathlib.Path(sys.argv[1]) +data = json.loads(path.read_text()) +current = data.get("marketing_version") +if not current: + raise SystemExit("marketing_version missing in version.json") +parts = current.split(".") +if len(parts) != 3 or not all(part.isdigit() for part in parts): + raise SystemExit(f"Invalid marketing_version format: {current}") +parts[-1] = str(int(parts[-1]) + 1) +data["marketing_version"] = ".".join(parts) +path.write_text(json.dumps(data, indent=2) + "\n") +print(data["marketing_version"]) +PY +)" + +echo "$new_version"