Files
kb/release-client.sh
steve 528a09ca90 Independent client/engine versioning with compatibility check
Split release.sh into release-client.sh and release-engine.sh for
independent release cadences. Client checks engine version on first
API call and hard-fails if engine is below MinEngineVersion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 15:59:16 +00:00

219 lines
8.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# release-client.sh — Build, tag, and release the Go client
#
# Usage:
# ./release-client.sh --gitea|--github [--dry-run] [--no-increment] [--patch|--minor|--major]
set -euo pipefail
#──────────────────────────────────────────────────────────────────────
# Config
#──────────────────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CLIENT_DIR="$SCRIPT_DIR/client"
VERSION_FILE="$CLIENT_DIR/VERSION"
MIN_ENGINE_FILE="$CLIENT_DIR/MIN_ENGINE_VERSION"
#──────────────────────────────────────────────────────────────────────
# Parse args
#──────────────────────────────────────────────────────────────────────
DRY_RUN=false
INCREMENT=true
BUMP="patch"
FORGE=""
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=true ;;
--no-increment) INCREMENT=false ;;
--minor) BUMP="minor" ;;
--major) BUMP="major" ;;
--patch) BUMP="patch" ;;
--gitea) FORGE="tea" ;;
--github) FORGE="gh" ;;
*)
echo "Unknown argument: $arg"
echo "Usage: $0 --gitea|--github [--dry-run] [--no-increment] [--patch|--minor|--major]"
exit 1
;;
esac
done
if [[ -z "$FORGE" ]]; then
echo "Error: specify --gitea or --github"
echo "Usage: $0 --gitea|--github [--dry-run] [--no-increment] [--patch|--minor|--major]"
exit 1
fi
# Ensure we're on main branch
CURRENT_BRANCH="$(git -C "$SCRIPT_DIR" rev-parse --abbrev-ref HEAD)"
if [[ "$CURRENT_BRANCH" != "main" ]]; then
echo "Error: releases must be made from the main branch (currently on '$CURRENT_BRANCH')"
exit 1
fi
if ! command -v "$FORGE" &>/dev/null; then
echo "Error: '$FORGE' not found in PATH"
exit 1
fi
#──────────────────────────────────────────────────────────────────────
# Version helpers
#──────────────────────────────────────────────────────────────────────
read_version() {
local file="$1"
if [[ ! -f "$file" ]]; then
echo "Error: version file not found: $file" >&2
exit 1
fi
tr -d '[:space:]' < "$file"
}
bump_version() {
local ver="$1" part="$2"
local major minor patch
IFS='.' read -r major minor patch <<< "$ver"
case "$part" in
major) echo "$((major + 1)).0.0" ;;
minor) echo "${major}.$((minor + 1)).0" ;;
patch) echo "${major}.${minor}.$((patch + 1))" ;;
esac
}
write_version() {
local file="$1" ver="$2"
echo "$ver" > "$file"
}
run() {
echo " $ $*"
if [[ "$DRY_RUN" == false ]]; then
"$@"
fi
}
#──────────────────────────────────────────────────────────────────────
# Determine release version
#──────────────────────────────────────────────────────────────────────
CURRENT_VERSION="$(read_version "$VERSION_FILE")"
MIN_ENGINE_VERSION="$(read_version "$MIN_ENGINE_FILE")"
if [[ "$INCREMENT" == true ]]; then
VERSION="$(bump_version "$CURRENT_VERSION" "$BUMP")"
echo "==> Client version bump: $CURRENT_VERSION$VERSION ($BUMP)"
else
VERSION="$CURRENT_VERSION"
echo "==> Client version: $VERSION (no increment)"
fi
TAG="client-v${VERSION}"
echo " Tag: $TAG"
echo " Min engine: v$MIN_ENGINE_VERSION"
echo " Forge CLI: $FORGE"
echo " Dry run: $DRY_RUN"
echo ""
#──────────────────────────────────────────────────────────────────────
# 1. Pre-flight checks
#──────────────────────────────────────────────────────────────────────
echo "==> Pre-flight checks"
if [[ "$DRY_RUN" == false ]]; then
if git -C "$SCRIPT_DIR" rev-parse "$TAG" &>/dev/null; then
echo "Error: tag $TAG already exists"
exit 1
fi
fi
echo " OK"
echo ""
#──────────────────────────────────────────────────────────────────────
# 2. Update version file
#──────────────────────────────────────────────────────────────────────
if [[ "$INCREMENT" == true ]]; then
echo "==> Updating client version to $VERSION"
run write_version "$VERSION_FILE" "$VERSION"
echo ""
fi
#──────────────────────────────────────────────────────────────────────
# 3. Build Go client binaries
#──────────────────────────────────────────────────────────────────────
echo "==> Building Go client binaries ($VERSION, min engine $MIN_ENGINE_VERSION)"
run make -C "$CLIENT_DIR" clean
run make -C "$CLIENT_DIR" all VERSION="$VERSION" MIN_ENGINE_VERSION="$MIN_ENGINE_VERSION"
# Collect release assets
ASSETS=()
if [[ "$DRY_RUN" == false ]]; then
for bin in "$CLIENT_DIR"/dist/kb-*; do
ASSETS+=("$bin")
done
echo " Built ${#ASSETS[@]} binaries"
else
echo " (skipped — dry run)"
fi
echo ""
#──────────────────────────────────────────────────────────────────────
# 4. Commit, tag, and push
#──────────────────────────────────────────────────────────────────────
echo "==> Committing and tagging $TAG"
if [[ "$INCREMENT" == true ]]; then
run git -C "$SCRIPT_DIR" add "$VERSION_FILE"
run git -C "$SCRIPT_DIR" commit -m "Bump client version to $VERSION"
fi
run git -C "$SCRIPT_DIR" tag -a "$TAG" -m "Release $TAG"
run git -C "$SCRIPT_DIR" push origin HEAD
run git -C "$SCRIPT_DIR" push origin "$TAG"
echo ""
#──────────────────────────────────────────────────────────────────────
# 5. Create release with assets
#──────────────────────────────────────────────────────────────────────
echo "==> Creating release via $FORGE"
RELEASE_TITLE="Client $TAG"
RELEASE_NOTES="## Go client v${VERSION}
Requires engine v${MIN_ENGINE_VERSION}+
## Client binaries
Download the binary for your platform from the assets below, rename to \`kb\`, and place on your PATH."
if [[ "$FORGE" == "gh" ]]; then
ASSET_FLAGS=()
for f in "${ASSETS[@]+"${ASSETS[@]}"}"; do
ASSET_FLAGS+=("$f")
done
run gh release create "$TAG" \
--title "$RELEASE_TITLE" \
--notes "$RELEASE_NOTES" \
"${ASSET_FLAGS[@]+"${ASSET_FLAGS[@]}"}"
elif [[ "$FORGE" == "tea" ]]; then
run tea release create \
--tag "$TAG" \
--title "$RELEASE_TITLE" \
--note "$RELEASE_NOTES"
for f in "${ASSETS[@]+"${ASSETS[@]}"}"; do
run tea release asset create "$TAG" "$f"
done
fi
echo ""
echo "==> Release $TAG complete!"
echo ""
echo " Binaries: ${#ASSETS[@]} platform(s) attached to release"
echo " Min engine: v$MIN_ENGINE_VERSION"