#!/usr/bin/env bash # ============================================================================= # felhom-hub — Docker image build script # ============================================================================= # CANONICAL LOCATION: felhom.eu/scripts/build-hub.sh (this file, in git). # OPERATIVE LOCATION: /mnt/5_hdd/felhom.eu/build/felhom-hub/build.sh — a SYMLINK to this file, so # there is exactly one source of truth and a repo edit is live immediately (the install_skills.py # pattern). It lived ONLY in that build dir until v1.23.0, outside any repo and therefore # unversioned, unreviewed and unrecoverable — noted as a follow-up in felhom.eu/REPORT.md §6. # # Copies hub/ source from the felhom.eu git repo, builds the Docker image. Build artifacts stay in # the build dir's workspace/ — the git repo stays clean. # # Usage: # ./build.sh # Build for current platform, tag as :dev # ./build.sh 0.1.0 # Build with version tag # ./build.sh 0.1.0 --push # Build + push to Gitea registry # ./build.sh 0.1.0 --multiarch # Build amd64+arm64 + push # ============================================================================= set -euo pipefail SCRIPT_VERSION="1.23.0" # --- Configuration (edit these if your paths differ) --- REPO_DIR="/mnt/5_hdd/felhom.eu/git/felhom.eu" HUB_SRC="${REPO_DIR}/hub" REGISTRY="gitea.dooplex.hu/admin" IMAGE="${REGISTRY}/felhom-hub" # Build workspace — a temp directory next to this script, NOT in the git repo SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BUILD_DIR="${SCRIPT_DIR}/workspace" # --- Parse arguments --- VERSION="${1:-dev}" ACTION="${2:-}" # --push or --multiarch # --- Colors --- RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; } step() { echo -e "${CYAN}[STEP]${NC} $*"; } # --- Pre-flight checks --- if [[ ! -d "${HUB_SRC}" ]]; then error "Hub source not found: ${HUB_SRC}" error "Clone the repo first: git clone https://gitea.dooplex.hu/admin/felhom.eu.git ${REPO_DIR}" exit 1 fi if ! command -v docker &>/dev/null; then error "Docker not found." exit 1 fi # Get git commit for build metadata GIT_COMMIT=$(cd "${REPO_DIR}" && git rev-parse --short HEAD 2>/dev/null || echo "unknown") echo "" info "╔══════════════════════════════════════╗" info "║ felhom-hub build ║" info "╚══════════════════════════════════════╝" info "Version: ${VERSION} (build-hub.sh v${SCRIPT_VERSION})" info "Commit: ${GIT_COMMIT}" info "Source: ${HUB_SRC}" info "Build: ${BUILD_DIR}" info "Image: ${IMAGE}:${VERSION}" echo "" # ========================================================================= # Step 1: Pull latest code # ========================================================================= step "1/3 — Pulling latest code..." cd "${REPO_DIR}" git pull --ff-only 2>/dev/null || warn "git pull failed (offline or dirty tree?)" # ========================================================================= # Step 2: Sync source to build workspace # ========================================================================= step "2/3 — Syncing source to workspace..." mkdir -p "${BUILD_DIR}" rsync -a --delete \ --exclude '.git' \ --exclude 'bin/' \ --exclude '*.exe' \ "${HUB_SRC}/" "${BUILD_DIR}/" info "Source synced to ${BUILD_DIR}" # Sync app assets from website directory into build workspace WEBSITE_ASSETS_DIR="${REPO_DIR}/website/assets" if [[ -d "${WEBSITE_ASSETS_DIR}" ]]; then mkdir -p "${BUILD_DIR}/assets" cp "${WEBSITE_ASSETS_DIR}"/*-logo.svg "${BUILD_DIR}/assets/" 2>/dev/null || true cp "${WEBSITE_ASSETS_DIR}"/*-logo.png "${BUILD_DIR}/assets/" 2>/dev/null || true cp "${WEBSITE_ASSETS_DIR}"/*-screenshot-*.webp "${BUILD_DIR}/assets/" 2>/dev/null || true ASSET_COUNT=$(ls "${BUILD_DIR}/assets/" 2>/dev/null | wc -l) info "App assets synced (${ASSET_COUNT} files)" else warn "Website assets dir not found: ${WEBSITE_ASSETS_DIR}" mkdir -p "${BUILD_DIR}/assets" fi # Verify structure cd "${BUILD_DIR}" for dir in cmd/hub internal/api internal/store internal/web; do if [[ ! -d "${dir}" ]]; then error "Missing expected directory: ${dir}" error "Hub source structure may be incomplete." exit 1 fi done info "Package structure verified ✓" if [[ ! -f "go.mod" ]]; then error "go.mod not found in build workspace!" exit 1 fi # Ensure go.sum exists (Dockerfile COPY will fail without it) if command -v go &>/dev/null; then info "Running go mod tidy..." go mod tidy 2>&1 || warn "go mod tidy had issues (Docker build may still work)" elif [[ ! -f "go.sum" ]]; then warn "go.sum missing and Go not installed locally — creating empty go.sum" warn "(Docker build stage will resolve dependencies via 'go mod download')" touch go.sum fi # ========================================================================= # Step 3: Docker build # ========================================================================= step "3/3 — Building Docker image..." BUILD_ARGS=( --build-arg "VERSION=${VERSION}" --build-arg "BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" ) case "${ACTION}" in --push) info "Building for current platform + pushing..." docker build "${BUILD_ARGS[@]}" \ -t "${IMAGE}:${VERSION}" \ -t "${IMAGE}:latest" \ . info "Pushing..." docker push "${IMAGE}:${VERSION}" docker push "${IMAGE}:latest" ;; --multiarch) info "Building multi-arch (amd64 + arm64) + pushing..." # Ensure buildx builder exists if ! docker buildx inspect felhom-builder &>/dev/null; then info "Creating buildx builder (one-time setup)..." docker buildx create --name felhom-builder --use --bootstrap else docker buildx use felhom-builder fi docker buildx build "${BUILD_ARGS[@]}" \ --platform linux/amd64,linux/arm64 \ -t "${IMAGE}:${VERSION}" \ -t "${IMAGE}:latest" \ --push \ . ;; *) info "Building for current platform (local only)..." docker build "${BUILD_ARGS[@]}" \ -t "${IMAGE}:${VERSION}" \ -t "${IMAGE}:latest" \ . ;; esac # ========================================================================= # Summary # ========================================================================= echo "" info "╔══════════════════════════════════════╗" info "║ Build complete ✓ ║" info "╚══════════════════════════════════════╝" info "Image: ${IMAGE}:${VERSION}" # Show image size if available locally SIZE=$(docker image inspect "${IMAGE}:${VERSION}" --format='{{.Size}}' 2>/dev/null || echo "") if [[ -n "${SIZE}" ]]; then SIZE_HUMAN=$(numfmt --to=iec "${SIZE}" 2>/dev/null || echo "${SIZE} bytes") info "Size: ${SIZE_HUMAN}" fi echo "" if [[ "${ACTION}" == "" ]]; then info "Image is local only. To push: ./build.sh ${VERSION} --push" else info "Image pushed to registry." echo "" info "Deploy to k3s — GitOps ONLY. The manifest in git is the source of truth:" info " 1. bump the image tag in ${REPO_DIR}/manifests/hub.yaml to ${VERSION}, commit, push" info " 2. hard-refresh + sync the ArgoCD app (auto-sync is OFF — the sync is deliberate):" info " sudo kubectl -n argocd annotate application felhom argocd.argoproj.io/refresh=hard --overwrite" info " sudo kubectl -n argocd patch application felhom --type merge \\" info " -p '"'"'{\"operation\":{\"initiatedBy\":{\"username\":\"cc\"},\"sync\":{\"syncStrategy\":{\"apply\":{}}}}}'"'"'" info "" info " NEVER \`kubectl set image\` or \`kubectl apply\` by hand — ArgoCD reverts it on the next" info " sync, so the change looks applied, works for a while, and then silently disappears." fi echo ""