- 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.
109 lines
5.7 KiB
Bash
Executable File
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"
|