claude-configs/skills/_templates/release-core.sh

171 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Shared Release Script — CalVer tag and push for any project.
# Called by per-project release.sh wrappers.
#
# Usage: release-core.sh --config <config-file> <service> [version]
#
# Config file format (bash-sourceable):
# BASEDIR="/mnt/NV2/Development/my-project"
# declare -A SERVICE_DIRS=([database]="database" [discord]="discord-app")
# declare -A SERVICE_IMAGES=([database]="manticorum67/my-database" [discord]="manticorum67/my-discord")
#
# shellcheck disable=SC2086
set -euo pipefail
# --- Parse args ---
CONFIG_FILE=""
while [[ $# -gt 0 ]]; do
case "$1" in
--config)
CONFIG_FILE="$2"
shift 2
;;
*) break ;;
esac
done
if [[ -z "$CONFIG_FILE" || ! -f "$CONFIG_FILE" ]]; then
echo "Error: config file not found: ${CONFIG_FILE:-<not specified>}"
echo "Usage: release-core.sh --config <config-file> <service> [version]"
exit 1
fi
# shellcheck source=/dev/null
source "$CONFIG_FILE"
# Validate config
if [[ -z "${BASEDIR:-}" ]]; then
echo "Error: BASEDIR not set in config file"
exit 1
fi
# --- Service resolution ---
SERVICE="${1:-}"
VERSION="${2:-}"
if [[ -z "$SERVICE" ]]; then
echo "Usage: release-core.sh --config <config> <service> [version]"
echo ""
echo "Available services: ${!SERVICE_DIRS[*]}"
exit 1
fi
SERVICE_DIR="${SERVICE_DIRS[$SERVICE]:-}"
if [[ -z "$SERVICE_DIR" ]]; then
echo "Error: unknown service '$SERVICE'"
echo "Available services: ${!SERVICE_DIRS[*]}"
exit 1
fi
IMAGE="${SERVICE_IMAGES[$SERVICE]:-}"
REPO_DIR="$BASEDIR/$SERVICE_DIR"
if [[ ! -d "$REPO_DIR/.git" ]]; then
echo "Error: $REPO_DIR is not a git repository"
exit 1
fi
cd "$REPO_DIR"
# --- Validation ---
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [[ "$CURRENT_BRANCH" != "main" ]]; then
echo "Error: not on main branch (currently on '$CURRENT_BRANCH')"
echo "Switch to main before releasing."
exit 1
fi
if ! git diff --quiet HEAD 2>/dev/null; then
echo "Error: uncommitted changes in $REPO_DIR"
echo "Commit or stash changes before releasing."
exit 1
fi
echo "Pulling latest main..."
git pull --ff-only origin main 2>/dev/null || {
echo "Error: could not fast-forward main. Resolve divergence first."
exit 1
}
# --- Version ---
IS_DEV=0
if [[ "$VERSION" == "dev" ]]; then
IS_DEV=1
elif [[ -z "$VERSION" ]]; then
YEAR=$(date +%Y)
MONTH=$(date +%-m)
LATEST=$(git tag --list "${YEAR}.${MONTH}.*" --sort=-version:refname | head -1)
if [[ -n "$LATEST" ]]; then
BUILD=$(echo "$LATEST" | cut -d. -f3)
BUILD=$((BUILD + 1))
else
BUILD=1
fi
VERSION="${YEAR}.${MONTH}.${BUILD}"
fi
if [[ "$IS_DEV" -eq 0 ]]; then
if ! [[ "$VERSION" =~ ^20[0-9]{2}\.[0-9]{1,2}\.[0-9]+$ ]]; then
echo "Error: invalid CalVer format '$VERSION'"
echo "Expected: YYYY.M.BUILD (e.g., 2026.3.42) or 'dev'"
exit 1
fi
if git rev-parse "$VERSION" &>/dev/null; then
echo "Error: tag '$VERSION' already exists"
exit 1
fi
fi
# --- Summary ---
echo ""
echo "=== Release Summary ==="
echo " Service: $SERVICE"
echo " Repo: $REPO_DIR"
echo " Branch: main ($(git rev-parse --short HEAD))"
echo " Version: $VERSION"
if [[ "$IS_DEV" -eq 1 ]]; then
echo " Environment: dev (force-updating tag)"
fi
if [[ -n "$IMAGE" ]]; then
echo " Image: $IMAGE:$VERSION"
fi
echo ""
# --- Confirm ---
if [[ "${SKIP_CONFIRM:-}" != "1" ]]; then
read -r -p "Proceed? [y/N] " REPLY
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 0
fi
fi
# --- Tag and push ---
if [[ "$IS_DEV" -eq 1 ]]; then
echo "Force-updating dev tag..."
git tag -f dev
git push origin dev --force 2>&1
else
echo "Creating tag $VERSION..."
git tag "$VERSION"
echo "Pushing tag..."
git push origin "$VERSION" 2>&1
fi
echo ""
if [[ -n "$IMAGE" ]]; then
echo "CI will build and push: $IMAGE:$VERSION"
fi
echo ""
echo "Done. Tagged $SERVICE as $VERSION."