Files
sapl/scripts/build-frontend-dev.sh
T
Hermes Agent 62fe659cba ci(azure): repoint dev CI + registry to Azure VM (refs azure-migration)
- Registry host gitea.* -> git.* (backend base FROM, frontend image,
  build scripts, base workflow)
- deploy-dev: run on the Azure VM host (/opt/sapl/dev, secrets-based
  registry auth, hermes-owned checkout preserved)
- build-base-image: token via REGISTRY_TOKEN secret, clone from git.
2026-11-03 21:41:47 +05:30

109 lines
5.7 KiB
Bash
Executable File

#!/bin/sh
# build-frontend-dev.sh — laptop-first SAPL frontend image build + registry push,
# with automatic local fallback, then compose deploy of the dev stack.
#
# Runs inside the deploy job's docker:28-cli helper container (Alpine). Expected mounts:
# /code → host deploy checkout (e.g. /opt/sapl/dev)
# /var/run/docker.sock → host docker daemon
# /root/.ssh/id_ed25519 → Hermes laptop-build SSH key (read-only)
# /root/.ssh/known_hosts → laptop host key (read-only, may be empty)
# /run/secrets/registry-token → Gitea package token (read-only; or REGISTRY_TOKEN env)
#
# Flow:
# 1. Reset the checkout to origin/<BRANCH> exactly
# 2. Login to the Gitea container registry
# 3. If LAPTOP_HOST + LAPTOP_SSH_USER are set AND the laptop answers SSH:
# sync frontend/ via tar-over-ssh → build + push ON THE LAPTOP
# else: build + push on the host (local fallback)
# 4. `docker compose up -d` with FRONTEND_TAG=<dev-<sha>> so the stack pulls
# the freshly pushed image (compose frontend service has pull_policy: always).
# Also tags/pushes dev-latest so a plain manual `up -d` always works.
set -eu
# ── Config (env-overridable; dev defaults) ──────────────────────────────
BRANCH="${BRANCH:-dev}"
PROJECT="${PROJECT:-sapl-sapl-dev}"
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.dev.yml}"
ENV_FILE="${ENV_FILE:-.env.dev}"
REGISTRY="${REGISTRY:-git.ai-automation-hub.tech}"
IMAGE="${IMAGE:-sapl-deploy-bot/sapl-frontend}" # bot's user namespace (verified push access)
REGISTRY_USER="${REGISTRY_USER:-sapl-deploy-bot}"
PLATFORM="${PLATFORM:-linux/amd64}"
# Laptop (Tailscale IP/host). EMPTY = laptop disabled → always VPS build.
LAPTOP_HOST="${LAPTOP_HOST:-}"
LAPTOP_SSH_USER="${LAPTOP_SSH_USER:-}"
LAPTOP_BUILD_DIR="${LAPTOP_BUILD_DIR:-sapl-build}"
SSH_TIMEOUT="${SSH_TIMEOUT:-6}"
cd /code
# ── 1. Reset checkout to the branch tip ─────────────────────────────────
git fetch origin "$BRANCH" 2>/dev/null
git checkout -f "$BRANCH" 2>/dev/null || git checkout -f FETCH_HEAD
git reset --hard "origin/$BRANCH"
SHA="$(git rev-parse --short=8 HEAD)"
TAG="dev-${SHA}"
echo "==> Building sapl frontend for ${BRANCH} @ ${SHA} → ${REGISTRY}/${IMAGE}:${TAG}"
# ── 2. Registry login (token file preferred; env fallback) ──────────────
REG_TOKEN="$(cat /run/secrets/registry-token 2>/dev/null || echo "${REGISTRY_TOKEN:-}")"
if [ -z "$REG_TOKEN" ]; then
echo "FATAL: no registry token (/run/secrets/registry-token or REGISTRY_TOKEN)" >&2
exit 1
fi
echo "$REG_TOKEN" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin >/dev/null 2>&1
echo "==> Registry login OK (${REGISTRY_USER}@${REGISTRY})"
build_and_push_local() { # $1 = build dir
docker build --platform "$PLATFORM" \
--build-arg VITE_APP_VERSION="dev-${SHA}" \
-t "${REGISTRY}/${IMAGE}:${TAG}" \
-t "${REGISTRY}/${IMAGE}:dev-latest" \
-f "$1/Dockerfile" "$1"
docker push "${REGISTRY}/${IMAGE}:${TAG}" >/dev/null
docker push "${REGISTRY}/${IMAGE}:dev-latest" >/dev/null
echo "==> Pushed ${REGISTRY}/${IMAGE}:${TAG} + dev-latest"
}
# ── 3. Laptop-first build; VPS fallback ─────────────────────────────────
LAPTOP_TARGET=""
if [ -n "$LAPTOP_HOST" ] && [ -n "$LAPTOP_SSH_USER" ]; then
LAPTOP_TARGET="${LAPTOP_SSH_USER}@${LAPTOP_HOST}"
fi
SSHOPTS="-o ConnectTimeout=${SSH_TIMEOUT} -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
if [ -n "$LAPTOP_TARGET" ] && ssh $SSHOPTS "$LAPTOP_TARGET" "command -v podman >/dev/null 2>&1 || docker info >/dev/null 2>&1" 2>/dev/null; then
echo "==> Laptop UP (${LAPTOP_TARGET}) — building on laptop"
# Sync fresh frontend source (tar-over-ssh — no repo credentials stored on the laptop)
ssh $SSHOPTS "$LAPTOP_TARGET" "mkdir -p ~/${LAPTOP_BUILD_DIR}"
tar czf - -C /code frontend | ssh $SSHOPTS "$LAPTOP_TARGET" "tar xzf - -C ~/${LAPTOP_BUILD_DIR}"
echo "==> Source synced to laptop (~/${LAPTOP_BUILD_DIR}/frontend)"
# Login + build + push on the laptop in ONE ssh session (docker CLI, or podman
# when docker is absent — Fedora workstation ships podman). Token via stdin —
# never in remote process args. Guarded by LAPTOP_BUILD_TIMEOUT (default
# 900s): a laptop that sleeps/drops MID-build must fall back to the VPS
# instead of hanging the deploy forever (hit 2026-08-13, run #426).
if echo "$REG_TOKEN" | timeout "${LAPTOP_BUILD_TIMEOUT:-900}" ssh $SSHOPTS "$LAPTOP_TARGET" "
DB=\$(command -v docker 2>/dev/null || command -v podman 2>/dev/null)
[ -n \"\$DB\" ] || { echo 'no docker/podman on laptop'; exit 1; }
cat | \$DB login ${REGISTRY} -u ${REGISTRY_USER} --password-stdin
cd ~/${LAPTOP_BUILD_DIR}/frontend && \$DB build --platform ${PLATFORM} --build-arg VITE_APP_VERSION=dev-${SHA} -t ${REGISTRY}/${IMAGE}:${TAG} -t ${REGISTRY}/${IMAGE}:dev-latest . && \$DB push ${REGISTRY}/${IMAGE}:${TAG} && \$DB push ${REGISTRY}/${IMAGE}:dev-latest
"; then
echo "==> Laptop build+push OK"
else
echo "==> Laptop build failed/timed out (exit $?) — building locally (fallback)"
build_and_push_local /code/frontend
fi
else
echo "==> Laptop DOWN/unconfigured — building locally (fallback)"
build_and_push_local /code/frontend
fi
# ── 4. Deploy — pull the freshly pushed image ───────────────────────────
FRONTEND_TAG="$TAG" docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" -p "$PROJECT" up -d --build --remove-orphans
echo "==> Deployed ${PROJECT} with frontend ${TAG}"
docker logout "$REGISTRY" >/dev/null 2>&1 || true
echo "==> Done"