tooling: refresh build script from master
This commit is contained in:
parent
bccc39b185
commit
9db54fe69c
@ -1,52 +1,166 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================================
|
||||
# build-and-push.sh
|
||||
#
|
||||
# Purpose:
|
||||
# - Build & push Docker images for each service under ./containers/*
|
||||
# - Branch model: `main` is permanent and tracked by the prod stack
|
||||
# (image `:latest`). `dev` is a short-lived branch tracked by the dev
|
||||
# stack (image `:dev`); it is recreated from `main` at the start of each
|
||||
# cycle and deleted after each release. Tags `vX.Y.Z` cover rollback —
|
||||
# no `release/<version>` snapshot branches.
|
||||
# - Two modes:
|
||||
# t (test) = build & push :dev. Switches to (or creates) the `dev`
|
||||
# branch from `main` if not already on it, commits any
|
||||
# pending changes, and pushes `dev` to origin so the dev
|
||||
# stack picks up the matching compose/config alongside
|
||||
# the :dev image.
|
||||
# r (release) = MUST be run from `main`. Build & push :<version>,
|
||||
# :dev, :latest. Fast-forwards `main` against
|
||||
# `origin/main`, merges `dev` into `main` if it exists
|
||||
# (local or remote), creates a `Release vX.Y.Z` commit,
|
||||
# tags it, pushes `main` and the tag, and deletes the
|
||||
# `dev` branch locally and on origin. Version is read
|
||||
# from the top of changelog.md.
|
||||
#
|
||||
# Usage:
|
||||
# ./build-and-push.sh [mode]
|
||||
# - mode = t -> test build, push :dev only
|
||||
# - mode = r -> release build, version taken from changelog.md
|
||||
# - omitted -> prompt (default: t)
|
||||
#
|
||||
# Requirements:
|
||||
# - docs/changelog.md (relative to repo root), with the most recent release
|
||||
# at the top as:
|
||||
# ## vX.Y.Z — YYYY-MM-DD
|
||||
# (the version is parsed from the first such line)
|
||||
# - One Dockerfile per service under ./containers/<service>/Dockerfile
|
||||
#
|
||||
# ----------------------------------------------------------------------------
|
||||
# Self-update notice (for humans AND for Claude):
|
||||
#
|
||||
# The canonical/master copy of this script lives at:
|
||||
# /docker/develop/shared-integrations/tooling/docker-build-and-push/build-and-push.sh
|
||||
#
|
||||
# When this script is copied into a project repo, it may drift from master.
|
||||
# On startup the script compares its own SCRIPT_VERSION to the master copy and
|
||||
# warns if a newer version exists. It does NOT auto-overwrite (too risky during
|
||||
# a release run).
|
||||
#
|
||||
# CLAUDE INSTRUCTION:
|
||||
# If you (Claude) are about to invoke this script in a project repo, first
|
||||
# check whether the master copy at the path above is newer (different
|
||||
# SCRIPT_VERSION or differing file contents). If so, copy the master over
|
||||
# the local copy BEFORE running, then proceed. Mention the update to the user.
|
||||
# ============================================================================
|
||||
|
||||
SCRIPT_VERSION="1.7.0"
|
||||
SCRIPT_MASTER_PATH="/docker/develop/shared-integrations/tooling/docker-build-and-push/build-and-push.sh"
|
||||
|
||||
DOCKER_REGISTRY="gitea.oskamp.info"
|
||||
DOCKER_NAMESPACE="ivooskamp"
|
||||
|
||||
VERSION_FILE="version.txt"
|
||||
START_VERSION="v0.1.0"
|
||||
CHANGELOG_FILE="docs/changelog.md"
|
||||
CONTAINERS_DIR="containers"
|
||||
LAST_BRANCH_FILE=".last-branch"
|
||||
|
||||
BUMP="${1:-}"
|
||||
if [[ -z "${BUMP}" ]]; then
|
||||
echo "Select bump type: [1] patch, [2] minor, [3] major, [t] test (default: t)"
|
||||
read -r BUMP
|
||||
BUMP="${BUMP:-t}"
|
||||
fi
|
||||
|
||||
if [[ "$BUMP" != "1" && "$BUMP" != "2" && "$BUMP" != "3" && "$BUMP" != "t" ]]; then
|
||||
echo "[ERROR] Unknown bump type '$BUMP' (use 1, 2, 3, or t)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read_version() {
|
||||
if [[ -f "$VERSION_FILE" ]]; then
|
||||
tr -d ' \t\n\r' < "$VERSION_FILE"
|
||||
else
|
||||
echo "$START_VERSION"
|
||||
# --- Self-update check -------------------------------------------------------
|
||||
# Compare this script to the canonical master copy. If it differs, offer to
|
||||
# copy master over the local copy and re-exec with the same arguments so the
|
||||
# build runs against the up-to-date script.
|
||||
#
|
||||
# Skip with: SKIP_SELF_UPDATE=1 ./build-and-push.sh ...
|
||||
self_update_check() {
|
||||
if [[ "${SKIP_SELF_UPDATE:-0}" == "1" ]]; then
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
write_version() {
|
||||
echo "$1" > "$VERSION_FILE"
|
||||
}
|
||||
local self_path="${BASH_SOURCE[0]}"
|
||||
# Resolve to absolute path so a comparison against itself is detected.
|
||||
local self_abs
|
||||
self_abs="$(cd "$(dirname "$self_path")" 2>/dev/null && pwd)/$(basename "$self_path")" || self_abs="$self_path"
|
||||
|
||||
bump_version() {
|
||||
local cur="$1"
|
||||
local kind="$2"
|
||||
local core="${cur#v}"
|
||||
IFS='.' read -r MA MI PA <<< "$core"
|
||||
case "$kind" in
|
||||
1) PA=$((PA + 1));;
|
||||
2) MI=$((MI + 1)); PA=0;;
|
||||
3) MA=$((MA + 1)); MI=0; PA=0;;
|
||||
*) echo "[ERROR] Unknown bump kind"; exit 1;;
|
||||
esac
|
||||
echo "v${MA}.${MI}.${PA}"
|
||||
}
|
||||
if [[ "$self_abs" == "$SCRIPT_MASTER_PATH" ]]; then
|
||||
return 0 # We ARE the master copy.
|
||||
fi
|
||||
if [[ ! -f "$SCRIPT_MASTER_PATH" ]]; then
|
||||
return 0 # Master not reachable from this host; silently skip.
|
||||
fi
|
||||
|
||||
local master_version reason=""
|
||||
master_version="$(grep -m1 -E '^SCRIPT_VERSION=' "$SCRIPT_MASTER_PATH" | sed -E 's/.*"([^"]+)".*/\1/')"
|
||||
|
||||
if [[ -n "$master_version" && "$master_version" != "$SCRIPT_VERSION" ]]; then
|
||||
reason="version"
|
||||
elif ! cmp -s "$self_abs" "$SCRIPT_MASTER_PATH"; then
|
||||
reason="contents"
|
||||
else
|
||||
return 0 # Identical to master.
|
||||
fi
|
||||
|
||||
echo "[WARN] Local build-and-push.sh differs from master."
|
||||
if [[ "$reason" == "version" ]]; then
|
||||
echo " local : $SCRIPT_VERSION"
|
||||
echo " master : $master_version ($SCRIPT_MASTER_PATH)"
|
||||
else
|
||||
echo " Same SCRIPT_VERSION ($SCRIPT_VERSION) but file contents differ."
|
||||
echo " master : $SCRIPT_MASTER_PATH"
|
||||
fi
|
||||
|
||||
# Prompt only when stdin is a TTY; in non-interactive runs, abort safely so
|
||||
# an unattended release never silently runs against a stale script.
|
||||
if [[ ! -t 0 ]]; then
|
||||
echo "[ERROR] Non-interactive shell — refusing to auto-update."
|
||||
echo " Re-run interactively, or set SKIP_SELF_UPDATE=1 to bypass,"
|
||||
echo " or update manually: cp \"$SCRIPT_MASTER_PATH\" \"$self_abs\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local reply
|
||||
read -r -p "Update local script from master and re-run? [Y/n] " reply
|
||||
reply="${reply:-Y}"
|
||||
if [[ ! "$reply" =~ ^[Yy]$ ]]; then
|
||||
echo "[INFO] Continuing with local version $SCRIPT_VERSION (not updated)."
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! cp "$SCRIPT_MASTER_PATH" "$self_abs"; then
|
||||
echo "[ERROR] Failed to copy master to $self_abs (read-only filesystem?)."
|
||||
echo " Continuing with local version $SCRIPT_VERSION."
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
chmod +x "$self_abs" 2>/dev/null || true
|
||||
|
||||
echo "[INFO] Updated $self_abs from master. Re-executing..."
|
||||
echo ""
|
||||
# Re-exec with original arguments. SKIP_SELF_UPDATE=1 prevents an
|
||||
# update loop if cp somehow didn't take.
|
||||
export SKIP_SELF_UPDATE=1
|
||||
exec "$self_abs" "$@"
|
||||
}
|
||||
self_update_check "$@"
|
||||
|
||||
# --- Input: prompt if missing ------------------------------------------------
|
||||
MODE="${1:-}"
|
||||
if [[ -z "${MODE}" ]]; then
|
||||
echo "Select build type: [t] test build (push :dev only), [r] release build (default: t)"
|
||||
read -r MODE
|
||||
MODE="${MODE:-t}"
|
||||
fi
|
||||
|
||||
case "$MODE" in
|
||||
t|test) MODE="t" ;;
|
||||
r|release) MODE="r" ;;
|
||||
*)
|
||||
echo "[ERROR] Unknown mode '$MODE' (use 't' for test or 'r' for release)."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Helpers -----------------------------------------------------------------
|
||||
check_docker_ready() {
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
echo "[ERROR] Docker daemon not reachable. Is Docker running and do you have permission to use it?"
|
||||
@ -70,7 +184,7 @@ validate_repo_component() {
|
||||
local comp="$1"
|
||||
if [[ ! "$comp" =~ ^[a-z0-9]+([._-][a-z0-9]+)*$ ]]; then
|
||||
echo "[ERROR] Invalid repository component '$comp'."
|
||||
echo " Must match: ^[a-z0-9]+([._-][a-z0-9]+)*$"
|
||||
echo " Must match: ^[a-z0-9]+([._-][a-z0-9]+)*$ (lowercase, digits, ., _, - as separators)."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@ -88,11 +202,33 @@ validate_tag() {
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! -d ".git" ]]; then
|
||||
echo "[ERROR] Not a git repository (.git missing)."
|
||||
# Parse the first "## vX.Y.Z ..." heading from changelog.md.
|
||||
# Accepts: ## v1.0.3 — 2026-04-24
|
||||
# ## v1.0.3 - 2026-04-24
|
||||
# ## v1.0.3
|
||||
read_version_from_changelog() {
|
||||
if [[ ! -f "$CHANGELOG_FILE" ]]; then
|
||||
echo "[ERROR] $CHANGELOG_FILE not found in $(pwd)." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
local line
|
||||
# Match lines starting with "## v<digits>.<digits>.<digits>"
|
||||
line="$(grep -m1 -E '^##[[:space:]]+v[0-9]+\.[0-9]+\.[0-9]+' "$CHANGELOG_FILE" || true)"
|
||||
if [[ -z "$line" ]]; then
|
||||
echo "[ERROR] No release heading found in $CHANGELOG_FILE (expected e.g. '## v1.0.3 — 2026-04-24' near the top)." >&2
|
||||
exit 1
|
||||
fi
|
||||
# Extract the vX.Y.Z token
|
||||
local version
|
||||
version="$(echo "$line" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -n1)"
|
||||
if [[ -z "$version" ]]; then
|
||||
echo "[ERROR] Could not parse version from line: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$version"
|
||||
}
|
||||
|
||||
# --- Preflight ---------------------------------------------------------------
|
||||
if [[ ! -d "$CONTAINERS_DIR" ]]; then
|
||||
echo "[ERROR] '$CONTAINERS_DIR' directory missing. Expected ./${CONTAINERS_DIR}/<service>/ with a Dockerfile."
|
||||
exit 1
|
||||
@ -102,59 +238,107 @@ check_docker_ready
|
||||
ensure_registry_login
|
||||
validate_repo_component "$DOCKER_NAMESPACE"
|
||||
|
||||
DETECTED_BRANCH="$(git branch --show-current 2>/dev/null || true)"
|
||||
if [[ -z "$DETECTED_BRANCH" ]]; then
|
||||
DETECTED_BRANCH="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)"
|
||||
fi
|
||||
if [[ -z "$DETECTED_BRANCH" ]]; then
|
||||
DETECTED_BRANCH="main"
|
||||
fi
|
||||
|
||||
UPSTREAM_REF="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null || echo "origin/$DETECTED_BRANCH")"
|
||||
HEAD_SHA="$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")"
|
||||
LAST_BRANCH_FILE_PATH="$(pwd)/$LAST_BRANCH_FILE"
|
||||
|
||||
echo "[INFO] Repo: $(pwd)"
|
||||
echo "[INFO] Current branch: $DETECTED_BRANCH"
|
||||
echo "[INFO] Upstream: $UPSTREAM_REF"
|
||||
echo "[INFO] HEAD (sha): $HEAD_SHA"
|
||||
|
||||
CURRENT_VERSION="$(read_version)"
|
||||
NEW_VERSION="$CURRENT_VERSION"
|
||||
DO_TAG_AND_BUMP=true
|
||||
|
||||
if [[ "$BUMP" == "t" ]]; then
|
||||
echo "[INFO] Test build: keeping version $CURRENT_VERSION; will only update :dev."
|
||||
DO_TAG_AND_BUMP=false
|
||||
# Informational: show branch and HEAD if this happens to be a git repo.
|
||||
BRANCH_INFO=""
|
||||
HEAD_INFO=""
|
||||
if [[ -d ".git" ]]; then
|
||||
BRANCH_INFO="$(git branch --show-current 2>/dev/null || echo unknown)"
|
||||
HEAD_INFO="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
||||
echo "[INFO] Repo: $(pwd)"
|
||||
echo "[INFO] Current branch: $BRANCH_INFO"
|
||||
echo "[INFO] HEAD (sha): $HEAD_INFO"
|
||||
else
|
||||
NEW_VERSION="$(bump_version "$CURRENT_VERSION" "$BUMP")"
|
||||
echo "[INFO] New version: $NEW_VERSION"
|
||||
echo "[INFO] Repo: $(pwd) (not a git checkout)"
|
||||
fi
|
||||
|
||||
if $DO_TAG_AND_BUMP; then
|
||||
validate_tag "$NEW_VERSION"
|
||||
# --- Release preflight (BEFORE any docker work) ------------------------------
|
||||
# All git-side validation for a release happens here so a wrong-branch / dirty
|
||||
# tree / stale main / conflicting dev / pre-existing tag aborts the run before
|
||||
# anything is built or pushed to the registry. dev is merged into main now so
|
||||
# the version we read from changelog.md reflects the merged state, not main's
|
||||
# pre-merge state.
|
||||
VERSION=""
|
||||
DEV_MERGED=0
|
||||
if [[ "$MODE" == "r" ]]; then
|
||||
if [[ ! -d ".git" ]]; then
|
||||
echo "[ERROR] Release mode requires a git checkout."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURRENT_BRANCH="$(git symbolic-ref --short -q HEAD || echo)"
|
||||
if [[ "$CURRENT_BRANCH" != "main" ]]; then
|
||||
echo "[ERROR] Release build must run from 'main' branch. Current: ${CURRENT_BRANCH:-<detached>}."
|
||||
echo " Switch with: git checkout main"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! git diff --quiet HEAD -- || ! git diff --cached --quiet; then
|
||||
echo "[ERROR] Working tree has uncommitted changes. Commit or stash them on the appropriate branch before releasing."
|
||||
git status --short
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INFO] Fetching origin..."
|
||||
git fetch origin main
|
||||
if git ls-remote --exit-code --heads origin dev >/dev/null 2>&1; then
|
||||
git fetch origin dev
|
||||
fi
|
||||
|
||||
if ! git merge --ff-only origin/main 2>/dev/null; then
|
||||
echo "[ERROR] Local main has diverged from origin/main. Resolve manually before releasing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Merge dev into main BEFORE reading the version, so changelog.md reflects
|
||||
# the bumped state that dev brings in.
|
||||
if git show-ref --verify --quiet refs/heads/dev; then
|
||||
echo "[INFO] Merging local dev into main..."
|
||||
if ! git merge --no-ff dev -m "Release (merge dev)"; then
|
||||
echo "[ERROR] Merge of dev into main failed (conflict). Resolve manually and re-run."
|
||||
exit 1
|
||||
fi
|
||||
DEV_MERGED=1
|
||||
elif git ls-remote --exit-code --heads origin dev >/dev/null 2>&1; then
|
||||
echo "[INFO] Fetching and merging origin/dev into main..."
|
||||
git fetch origin dev:dev
|
||||
if ! git merge --no-ff dev -m "Release (merge dev)"; then
|
||||
echo "[ERROR] Merge of dev into main failed (conflict). Resolve manually and re-run."
|
||||
exit 1
|
||||
fi
|
||||
DEV_MERGED=1
|
||||
else
|
||||
echo "[INFO] No dev branch found — releasing main as-is."
|
||||
fi
|
||||
|
||||
VERSION="$(read_version_from_changelog)"
|
||||
echo "[INFO] Release version (from $CHANGELOG_FILE, post-merge): $VERSION"
|
||||
validate_tag "$VERSION"
|
||||
validate_tag "latest"
|
||||
|
||||
# Tag collision = abort. A re-release of an existing version with different
|
||||
# content would silently move what consumers think v0.X.Y points to.
|
||||
if git rev-parse -q --verify "refs/tags/${VERSION}" >/dev/null; then
|
||||
echo "[ERROR] Tag ${VERSION} already exists locally. Bump $CHANGELOG_FILE to a new version before releasing."
|
||||
exit 1
|
||||
fi
|
||||
if git ls-remote --exit-code --tags origin "refs/tags/${VERSION}" >/dev/null 2>&1; then
|
||||
echo "[ERROR] Tag ${VERSION} already exists on origin. Bump $CHANGELOG_FILE to a new version before releasing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ask for confirmation so you never accidentally re-push an old version or a wrong one.
|
||||
read -r -p "Proceed building & pushing as ${VERSION}? [y/N] " CONFIRM
|
||||
CONFIRM="${CONFIRM:-N}"
|
||||
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
|
||||
echo "[INFO] Aborted by user. Note: dev has been merged into local main; reset with 'git reset --hard origin/main' if you want to undo."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "[INFO] Test build: only :dev will be pushed."
|
||||
fi
|
||||
validate_tag "dev"
|
||||
|
||||
if $DO_TAG_AND_BUMP; then
|
||||
echo "[INFO] Writing $NEW_VERSION to $VERSION_FILE"
|
||||
write_version "$NEW_VERSION"
|
||||
|
||||
echo "[INFO] Git add + commit (branch: $DETECTED_BRANCH)"
|
||||
git add "$VERSION_FILE"
|
||||
git commit -m "Release $NEW_VERSION on branch $DETECTED_BRANCH (bump type $BUMP)"
|
||||
|
||||
echo "[INFO] Git tag $NEW_VERSION"
|
||||
git tag -a "$NEW_VERSION" -m "Release $NEW_VERSION"
|
||||
|
||||
echo "[INFO] Git push + tags"
|
||||
git push origin "$DETECTED_BRANCH"
|
||||
git push --tags
|
||||
else
|
||||
echo "[INFO] Skipping commit/tagging (test build)."
|
||||
fi
|
||||
|
||||
# --- Build & push per service ------------------------------------------------
|
||||
shopt -s nullglob
|
||||
services=( "$CONTAINERS_DIR"/* )
|
||||
if [[ ${#services[@]} -eq 0 ]]; then
|
||||
@ -178,21 +362,21 @@ for svc_path in "${services[@]}"; do
|
||||
|
||||
IMAGE_BASE="${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/${svc}"
|
||||
|
||||
if $DO_TAG_AND_BUMP; then
|
||||
if [[ "$MODE" == "r" ]]; then
|
||||
echo "============================================================"
|
||||
echo "[INFO] Building ${svc} -> tags: ${NEW_VERSION}, dev, latest"
|
||||
echo "[INFO] Building ${svc} -> tags: ${VERSION}, dev, latest"
|
||||
echo "============================================================"
|
||||
docker build \
|
||||
-t "${IMAGE_BASE}:${NEW_VERSION}" \
|
||||
-t "${IMAGE_BASE}:${VERSION}" \
|
||||
-t "${IMAGE_BASE}:dev" \
|
||||
-t "${IMAGE_BASE}:latest" \
|
||||
"$svc_path"
|
||||
|
||||
docker push "${IMAGE_BASE}:${NEW_VERSION}"
|
||||
docker push "${IMAGE_BASE}:${VERSION}"
|
||||
docker push "${IMAGE_BASE}:dev"
|
||||
docker push "${IMAGE_BASE}:latest"
|
||||
|
||||
BUILT_IMAGES+=("${IMAGE_BASE}:${NEW_VERSION}" "${IMAGE_BASE}:dev" "${IMAGE_BASE}:latest")
|
||||
BUILT_IMAGES+=("${IMAGE_BASE}:${VERSION}" "${IMAGE_BASE}:dev" "${IMAGE_BASE}:latest")
|
||||
else
|
||||
echo "============================================================"
|
||||
echo "[INFO] Test build ${svc} -> tag: dev"
|
||||
@ -203,18 +387,96 @@ for svc_path in "${services[@]}"; do
|
||||
fi
|
||||
done
|
||||
|
||||
echo "$DETECTED_BRANCH" > "$LAST_BRANCH_FILE_PATH"
|
||||
|
||||
# --- Summary -----------------------------------------------------------------
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo "[SUMMARY] Build & push complete (branch: $DETECTED_BRANCH)"
|
||||
if $DO_TAG_AND_BUMP; then
|
||||
echo "[INFO] Release version: $NEW_VERSION"
|
||||
if [[ "$MODE" == "r" ]]; then
|
||||
echo "[SUMMARY] Release build & push complete: $VERSION"
|
||||
else
|
||||
echo "[INFO] Test build (no version bump)"
|
||||
echo "[SUMMARY] Test build & push complete (:dev only)"
|
||||
fi
|
||||
if [[ -n "$BRANCH_INFO" ]]; then
|
||||
echo "[INFO] Branch: $BRANCH_INFO HEAD: $HEAD_INFO"
|
||||
fi
|
||||
echo "[INFO] Images pushed:"
|
||||
for img in "${BUILT_IMAGES[@]}"; do
|
||||
echo " - $img"
|
||||
done
|
||||
echo "============================================================"
|
||||
echo ""
|
||||
|
||||
# --- Git: release commit + tag + push (release mode only) -------------------
|
||||
# Preflight (branch, clean tree, ff origin/main, dev merge, tag collision,
|
||||
# version parse) already ran BEFORE the build. dev is already merged into
|
||||
# local main. We only need to land the Release commit, tag, and push.
|
||||
if [[ "$MODE" == "r" ]]; then
|
||||
echo "[INFO] Finalising release: version=${VERSION}"
|
||||
|
||||
# Produce a clean Release commit at the tip. Preflight guarantees the working
|
||||
# tree was clean at start; any post-build artefacts would be unexpected, so
|
||||
# commit with --allow-empty to keep the release marker isolated.
|
||||
if git diff --quiet HEAD -- && git diff --cached --quiet; then
|
||||
git commit --allow-empty -m "Release ${VERSION}"
|
||||
else
|
||||
echo "[WARN] Working tree changed during the build — staging and including in release commit."
|
||||
git add -A
|
||||
git commit -m "Release ${VERSION}"
|
||||
fi
|
||||
|
||||
git tag -a "${VERSION}" -m "Release ${VERSION}"
|
||||
|
||||
# Push main first (triggers prod webhook), then the tag.
|
||||
git push origin main
|
||||
git push origin "refs/tags/${VERSION}"
|
||||
echo "[INFO] Pushed main and tag ${VERSION} to origin."
|
||||
|
||||
# Clean up dev branch — local and remote.
|
||||
if [[ "$DEV_MERGED" == "1" ]]; then
|
||||
if git show-ref --verify --quiet refs/heads/dev; then
|
||||
git branch -D dev
|
||||
echo "[INFO] Deleted local dev branch."
|
||||
fi
|
||||
if git ls-remote --exit-code --heads origin dev >/dev/null 2>&1; then
|
||||
git push origin --delete dev
|
||||
echo "[INFO] Deleted remote dev branch."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Git: dev branch commit + push (test mode only) -------------------------
|
||||
if [[ "$MODE" == "t" ]]; then
|
||||
if [[ ! -d ".git" ]]; then
|
||||
echo "[WARN] Not a git checkout — skipping dev branch commit/push."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CURRENT_BRANCH="$(git symbolic-ref --short -q HEAD || echo)"
|
||||
|
||||
# Ensure we are on the dev branch. Create it if needed.
|
||||
if [[ "$CURRENT_BRANCH" != "dev" ]]; then
|
||||
if git show-ref --verify --quiet refs/heads/dev; then
|
||||
echo "[INFO] Switching to existing local dev branch."
|
||||
git checkout dev
|
||||
elif git ls-remote --exit-code --heads origin dev >/dev/null 2>&1; then
|
||||
echo "[INFO] Checking out remote dev branch."
|
||||
git fetch origin dev
|
||||
git checkout -b dev origin/dev
|
||||
else
|
||||
echo "[INFO] Creating new dev branch from main."
|
||||
git fetch origin main
|
||||
git checkout -b dev origin/main
|
||||
fi
|
||||
fi
|
||||
|
||||
# Stage and commit if there are changes.
|
||||
git add -A
|
||||
if git diff --cached --quiet; then
|
||||
echo "[INFO] Working tree clean — pushing current HEAD to dev."
|
||||
else
|
||||
git commit -m "Dev build $(date '+%Y-%m-%d %H:%M')"
|
||||
fi
|
||||
|
||||
# Non-force push. Diverged origin/dev fails hard — resolve manually.
|
||||
git push -u origin dev
|
||||
echo "[INFO] Pushed dev to origin."
|
||||
fi
|
||||
|
||||
Loading…
Reference in New Issue
Block a user