Files
foxhunt/infra/scripts/build-and-push.sh
jgrusewski 57e22c01a8 refactor: update K8s, CI, Docker, Prometheus, scripts, and FXT CLI for api rename
- K8s: rename api-gateway → api manifests, delete web-gateway, update network policies
- CI: rename compile/deploy jobs, delete web-gateway jobs
- Docker: rename service in compose files
- Prometheus: update scrape targets and alert rules
- Scripts: update binary references in build/test/cert scripts
- FXT CLI: rename api_gateway_url → api_url (with serde alias for compat)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 23:46:36 +01:00

246 lines
7.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ---------------------------------------------------------------------------
# build-and-push.sh - Build and push service images to Scaleway registry
# ---------------------------------------------------------------------------
# Builds all Foxhunt service Docker images and pushes to the Scaleway
# Container Registry. Supports parallel builds, selective service builds,
# and sccache for faster compilation.
#
# Usage:
# ./infra/scripts/build-and-push.sh # Build & push all
# ./infra/scripts/build-and-push.sh --no-push # Build only
# ./infra/scripts/build-and-push.sh --services "api-gateway trading-service"
# ./infra/scripts/build-and-push.sh --parallel 3 # 3 concurrent builds
# ./infra/scripts/build-and-push.sh --dry-run # Show what would run
# ---------------------------------------------------------------------------
REGISTRY="rg.fr-par.scw.cloud"
NAMESPACE="foxhunt"
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
# Services built with Dockerfile.service (--build-arg SERVICE=<name>)
STANDARD_SERVICES=(
api
trading-service
trading-agent-service
ml-training-service
backtesting-service
broker-gateway
)
# Defaults
DO_PUSH=true
DRY_RUN=false
PARALLEL=1
SELECTED_SERVICES=()
SKIP_STANDARD=false
SKIP_TRAINING=false
TAG_LATEST=true
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
Options:
--no-push Build only, don't push to registry
--dry-run Show what would be built
--services "s1 s2" Build only these standard services
--skip-training Skip training image build
--parallel N Run N builds concurrently (default: 1)
--no-latest Don't tag images as :latest
-h, --help Show this help
EOF
}
while [[ $# -gt 0 ]]; do
case $1 in
--no-push) DO_PUSH=false; shift ;;
--dry-run) DRY_RUN=true; shift ;;
--services) IFS=' ' read -ra SELECTED_SERVICES <<< "$2"; shift 2 ;;
--skip-training) SKIP_TRAINING=true; shift ;;
--parallel) PARALLEL="$2"; shift 2 ;;
--no-latest) TAG_LATEST=false; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown option: $1"; usage; exit 1 ;;
esac
done
# If --services was given, use only those; otherwise use all standard services
if [[ ${#SELECTED_SERVICES[@]} -gt 0 ]]; then
SERVICES=("${SELECTED_SERVICES[@]}")
SKIP_TRAINING=true
else
SERVICES=("${STANDARD_SERVICES[@]}")
fi
# Dirty check
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
GIT_COMMIT="${GIT_COMMIT}-dirty"
fi
TAG_COMMIT="${GIT_COMMIT}"
TAG_TS="${TIMESTAMP}"
echo "=== Foxhunt Docker Build ==="
echo "Registry: ${REGISTRY}/${NAMESPACE}"
echo "Commit: ${TAG_COMMIT}"
echo "Timestamp: ${TAG_TS}"
echo "Parallel: ${PARALLEL}"
echo ""
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
PASS=0
FAIL=0
FAILURES=()
build_and_tag() {
local image_name="$1"
local dockerfile="$2"
shift 2
local extra_args=("$@")
local full="${REGISTRY}/${NAMESPACE}/${image_name}"
local tags=("-t" "${full}:${TAG_COMMIT}" "-t" "${full}:${TAG_TS}")
if [[ "$TAG_LATEST" == "true" ]]; then
tags+=("-t" "${full}:latest")
fi
local cmd=(docker build "${tags[@]}" -f "$dockerfile" "${extra_args[@]}" .)
if [[ "$DRY_RUN" == "true" ]]; then
echo " [DRY] ${cmd[*]}"
return 0
fi
echo " Building ${image_name}..."
local start
start=$(date +%s)
if DOCKER_BUILDKIT=1 "${cmd[@]}" 2>&1 | tail -5; then
local elapsed=$(( $(date +%s) - start ))
echo " [OK] ${image_name} (${elapsed}s)"
(( PASS++ ))
else
echo " [FAIL] ${image_name}"
(( FAIL++ ))
FAILURES+=("${image_name}")
return 1
fi
}
push_image() {
local image_name="$1"
local full="${REGISTRY}/${NAMESPACE}/${image_name}"
for tag in "${TAG_COMMIT}" "${TAG_TS}" $( [[ "$TAG_LATEST" == "true" ]] && echo "latest" ); do
if [[ "$DRY_RUN" == "true" ]]; then
echo " [DRY] docker push ${full}:${tag}"
else
echo " Pushing ${full}:${tag}"
docker push "${full}:${tag}"
fi
done
}
# ---------------------------------------------------------------------------
# Ensure registry login
# ---------------------------------------------------------------------------
if [[ "$DRY_RUN" != "true" && "$DO_PUSH" == "true" ]]; then
echo "--- Registry Login ---"
if ! docker login "${REGISTRY}" -u nologin --password-stdin <<< "$(scw registry login -o json 2>/dev/null | python3 -c 'import sys,json; print(json.load(sys.stdin).get("password",""))' 2>/dev/null)" 2>/dev/null; then
echo " Trying scw registry login..."
scw registry login
fi
echo ""
fi
# ---------------------------------------------------------------------------
# Build standard services (Dockerfile.service)
# ---------------------------------------------------------------------------
echo "--- Standard Services ---"
if [[ "$PARALLEL" -gt 1 ]]; then
# Parallel builds using background jobs
PIDS=()
for svc in "${SERVICES[@]}"; do
(
build_and_tag "$svc" "infra/docker/Dockerfile.service" --build-arg "SERVICE=${svc}"
) &
PIDS+=($!)
# Throttle to PARALLEL concurrent jobs
while [[ $(jobs -r | wc -l) -ge $PARALLEL ]]; do
sleep 1
done
done
# Wait for all builds
for pid in "${PIDS[@]}"; do
wait "$pid" || (( FAIL++ ))
done
else
for svc in "${SERVICES[@]}"; do
build_and_tag "$svc" "infra/docker/Dockerfile.service" --build-arg "SERVICE=${svc}" || true
done
fi
# ---------------------------------------------------------------------------
# Build training image (Dockerfile.training)
# ---------------------------------------------------------------------------
if [[ "$SKIP_TRAINING" != "true" ]]; then
echo ""
echo "--- Training Image ---"
build_and_tag "training" "infra/docker/Dockerfile.training" || true
fi
# ---------------------------------------------------------------------------
# Push
# ---------------------------------------------------------------------------
if [[ "$DO_PUSH" == "true" ]]; then
echo ""
echo "--- Pushing Images ---"
for svc in "${SERVICES[@]}"; do
push_image "$svc"
done
if [[ "$SKIP_TRAINING" != "true" ]]; then
push_image "training"
fi
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
echo ""
echo "======================================================================"
printf " Results: %d built, %d failed\n" "$PASS" "$FAIL"
echo "======================================================================"
if [[ $FAIL -gt 0 ]]; then
echo ""
echo " Failed:"
for f in "${FAILURES[@]}"; do
echo " - ${f}"
done
echo ""
exit 1
fi
echo ""
echo " All images built successfully."
if [[ "$DO_PUSH" == "true" && "$DRY_RUN" != "true" ]]; then
echo " Pushed to: ${REGISTRY}/${NAMESPACE}/"
fi
exit 0