#!/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 [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:-}" echo "Usage: release-core.sh --config [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 [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."