better-argo-tunnels/ship.sh

141 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Fixed pipeline (do not reorder or skip steps):
# 1. git add -A
# 2. git commit (normal if staged changes exist, else --allow-empty with the same message)
# 3. GIT_COMMIT = current HEAD (baked into Go binaries inside each image via Dockerfile ARG)
# 4. docker login ghcr.io
# 5. For each of server + client targets: per-arch buildx --push (linux/amd64, linux/arm64), then imagetools manifest
# 6. git push GIT_REMOTE BRANCH
#
# Usage: ./ship.sh <commit message>
# Requires: git, docker (buildx), gh; remote "github"; gh token with write:packages for GHCR.
#
# Multi-arch: Intel/AMD 64-bit (linux/amd64) and ARM64 (linux/arm64), one manifest tag each for server and client.
set -euo pipefail
if [[ $# -lt 1 ]] || [[ -z "${1// }" ]]; then
echo "usage: $0 <commit message>" >&2
exit 1
fi
COMMIT_MSG="$*"
ROOT="$(cd "$(dirname "$0")" && pwd)"
cd "$ROOT"
# --- release targets (edit only here) ---
GITHUB_OWNER="Leopere"
REGISTRY_BASE="ghcr.io/leopere/better-argo-tunnels"
TAG_SERVER="production"
TAG_CLIENT="client-production"
# Exactly two platforms → one multi-arch manifest per image (amd64 + arm64).
PLATFORMS="linux/amd64,linux/arm64"
GIT_REMOTE="github"
# --- end ---
for cmd in git docker gh; do
command -v "$cmd" >/dev/null 2>&1 || {
echo "ship: fatal: missing required command: $cmd" >&2
exit 1
}
done
if ! docker buildx version >/dev/null 2>&1; then
echo "ship: fatal: docker buildx not available" >&2
exit 1
fi
BRANCH="$(git branch --show-current)"
if [[ -z "$BRANCH" ]]; then
echo "ship: fatal: detached HEAD" >&2
exit 1
fi
if ! git remote get-url "$GIT_REMOTE" >/dev/null 2>&1; then
echo "ship: fatal: git remote '$GIT_REMOTE' is not configured" >&2
exit 1
fi
echo "==> [1/6] git add -A"
git add -A
echo "==> [2/6] git commit"
if git diff --cached --quiet; then
git commit --allow-empty -m "$COMMIT_MSG"
else
git commit -m "$COMMIT_MSG"
fi
GIT_COMMIT="$(git rev-parse HEAD)"
BA=(--build-arg "GIT_COMMIT=${GIT_COMMIT}")
echo "==> GIT_COMMIT=${GIT_COMMIT}"
echo "==> [3/6] docker login ghcr.io"
if ! token="$(gh auth token)" || [[ -z "${token}" ]]; then
echo "ship: fatal: gh auth token empty" >&2
exit 1
fi
if ! printf '%s' "$token" | docker login ghcr.io -u "$GITHUB_OWNER" --password-stdin; then
echo "ship: fatal: docker login ghcr.io failed (need: gh auth refresh -h github.com -s write:packages -s read:packages)" >&2
exit 1
fi
pick_builder() {
if docker buildx inspect orbstack >/dev/null 2>&1; then
echo "orbstack"
else
echo "default"
fi
}
platform_slug() {
local p="${1// /}"
p="${p#linux/}"
echo "${p//\//-}"
}
# Per-arch push then single multi-arch manifest (orbstack/docker driver compatible).
push_multi() {
local target="$1"
local final_tag="$2"
local builder="$3"
local base="${final_tag%:*}"
local name="${final_tag##*:}"
local arch_refs=()
local p slug arch_tag
IFS=',' read -ra plats <<< "$PLATFORMS"
for p in "${plats[@]}"; do
p="${p// /}"
[[ -z "$p" ]] && continue
slug="$(platform_slug "$p")"
arch_tag="${base}:${name}-${slug}"
echo "==> [4/6] docker buildx push ${target} ${p} -> ${arch_tag}"
docker buildx build \
"${BA[@]}" \
--builder "$builder" \
--platform "$p" \
--target "$target" \
-t "$arch_tag" \
--push \
--provenance=false \
"$ROOT"
arch_refs+=("$arch_tag")
done
echo "==> [5/6] imagetools manifest ${final_tag}"
docker buildx imagetools create -t "$final_tag" "${arch_refs[@]}"
}
BUILDER="$(pick_builder)"
echo "==> buildx builder: ${BUILDER}"
SERVER_TAG="${REGISTRY_BASE}:${TAG_SERVER}"
CLIENT_TAG="${REGISTRY_BASE}:${TAG_CLIENT}"
push_multi server "$SERVER_TAG" "$BUILDER"
push_multi client "$CLIENT_TAG" "$BUILDER"
echo "==> [6/6] git push ${GIT_REMOTE} ${BRANCH}"
git push "$GIT_REMOTE" "$BRANCH"
echo "ship: ok commit=${GIT_COMMIT}"
echo "ship: images ${SERVER_TAG} ${CLIENT_TAG} (multi-arch: ${PLATFORMS})"