diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..a37572c --- /dev/null +++ b/build.sh @@ -0,0 +1,222 @@ +#!/usr/bin/env bash +# ============================================================================= +# felhom-controller — Docker image build script +# ============================================================================= +# Location: /home/kisfenyo/build/felhom-controller/build.sh +# +# Copies source from the git repo, syncs app assets, and builds the image. +# Build artifacts stay here — 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 + +# --- Configuration (edit these if your paths differ) --- +REPO_DIR="/home/kisfenyo/git/deploy-felhom-compose" +CONTROLLER_SRC="${REPO_DIR}/controller" +WEBSITE_ASSETS_DIR="/home/kisfenyo/git/felhom.eu/website/assets" +REGISTRY="gitea.dooplex.hu/admin" +IMAGE="${REGISTRY}/felhom-controller" + +# 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 "${CONTROLLER_SRC}" ]]; then + error "Controller source not found: ${CONTROLLER_SRC}" + error "Clone the repo first: git clone https://gitea.dooplex.hu/admin/deploy-felhom-compose.git ${REPO_DIR}" + exit 1 +fi + +if ! command -v docker &>/dev/null; then + error "Docker not found." + exit 1 +fi + +# Git metadata from the repo +GIT_COMMIT="unknown" +if [[ -d "${REPO_DIR}/.git" ]]; then + GIT_COMMIT=$(cd "${REPO_DIR}" && git rev-parse --short HEAD 2>/dev/null || echo "unknown") +fi + +echo "" +info "╔══════════════════════════════════════╗" +info "║ felhom-controller image builder ║" +info "╚══════════════════════════════════════╝" +info "Version: ${VERSION}" +info "Commit: ${GIT_COMMIT}" +info "Source: ${CONTROLLER_SRC}" +info "Build dir: ${BUILD_DIR}" +info "Image: ${IMAGE}:${VERSION}" +echo "" + +# ========================================================================= +# Step 1: Prepare clean build workspace +# ========================================================================= +step "1/4 — Preparing build workspace..." + +rm -rf "${BUILD_DIR}" +mkdir -p "${BUILD_DIR}" + +# Copy the entire controller directory (preserving subdirectory structure) +cp -a "${CONTROLLER_SRC}/." "${BUILD_DIR}/" + +# Verify the expected Go package structure exists +MISSING=() +for required in cmd/controller/main.go internal/config/config.go internal/stacks/manager.go internal/api/router.go internal/web/server.go; do + if [[ ! -f "${BUILD_DIR}/${required}" ]]; then + MISSING+=("${required}") + fi +done + +if [[ ${#MISSING[@]} -gt 0 ]]; then + error "Directory structure check FAILED. Missing files:" + for f in "${MISSING[@]}"; do + error " ✗ ${f}" + done + error "" + error "The Go source must be in package subdirectories, not flat." + error "Run the restructure script first:" + error " cd ${CONTROLLER_SRC} && bash restructure.sh" + error " git add -A && git commit -m 'Restructure into Go package dirs'" + rm -rf "${BUILD_DIR}" + exit 1 +fi + +GO_COUNT=$(find "${BUILD_DIR}" -name '*.go' | wc -l) +info "Source copied: ${GO_COUNT} Go files" + +# ========================================================================= +# Step 2: Sync app assets (logos + screenshots) +# ========================================================================= +step "2/4 — Syncing app assets..." + +mkdir -p "${BUILD_DIR}/assets" + +if [[ -d "${WEBSITE_ASSETS_DIR}" ]]; then + # Count before copy + SVG_N=$(find "${WEBSITE_ASSETS_DIR}" -maxdepth 1 -name '*-logo.svg' 2>/dev/null | wc -l) + PNG_N=$(find "${WEBSITE_ASSETS_DIR}" -maxdepth 1 -name '*-logo.png' 2>/dev/null | wc -l) + SS_N=$(find "${WEBSITE_ASSETS_DIR}" -maxdepth 1 -name '*-screenshot-*.webp' 2>/dev/null | wc -l) + + 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 + + TOTAL=$(find "${BUILD_DIR}/assets" -type f ! -name 'README.md' 2>/dev/null | wc -l) + info "Synced ${TOTAL} assets (${SVG_N} SVG + ${PNG_N} PNG logos, ${SS_N} screenshots)" +else + warn "Website assets not found: ${WEBSITE_ASSETS_DIR}" + warn "Building without logos/screenshots. Set WEBSITE_ASSETS_DIR if needed." +fi + +# ========================================================================= +# Step 3: Run go mod tidy (to generate go.sum if missing) +# ========================================================================= +step "3/4 — Preparing Go modules..." + +cd "${BUILD_DIR}" + +# The Dockerfile handles go mod download internally, but verify go.mod exists +if [[ ! -f "go.mod" ]]; then + error "go.mod not found in build workspace!" + exit 1 +fi + +# If go is installed locally, run tidy to catch issues early +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)" +else + info "Go not installed locally — Docker build stage will handle dependencies." +fi + +# ========================================================================= +# Step 4: Docker build +# ========================================================================= +step "4/4 — Building Docker image..." + +BUILD_ARGS=( + --build-arg "VERSION=${VERSION}" + --build-arg "GIT_COMMIT=${GIT_COMMIT}" +) + +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:" + info " ./build.sh ${VERSION} --push # current arch" + info " ./build.sh ${VERSION} --multiarch # amd64 + arm64" +fi \ No newline at end of file diff --git a/collect-repos.sh b/collect-repos.sh index e4fadad..7111522 100644 --- a/collect-repos.sh +++ b/collect-repos.sh @@ -2,6 +2,7 @@ # collect-repos.sh # Run from ~/git/ - combines all files from each repo into a single .txt file # Output goes to ~/git/collected/ folder +# Output files are UTF-8 with BOM for maximum compatibility set -euo pipefail @@ -47,7 +48,7 @@ should_skip_file() { } # Known text file extensions - always include these -TEXT_EXTENSIONS=("sh" "bash" "zsh" "yaml" "yml" "json" "toml" "ini" "cfg" "conf" "txt" "md" "py" "js" "ts" "html" "css" "xml" "env" "service" "timer" "sql" "lua" "rb" "go" "rs" "java" "c" "h" "cpp" "hpp" "Makefile" "Dockerfile" "csv" "log" "properties" "rules") +TEXT_EXTENSIONS=("sh" "bash" "zsh" "yaml" "yml" "json" "toml" "ini" "cfg" "conf" "txt" "md" "py" "js" "ts" "html" "css" "xml" "env" "service" "timer" "sql" "lua" "rb" "go" "mod" "sum" "rs" "java" "c" "h" "cpp" "hpp" "Makefile" "Dockerfile" "csv" "log" "properties" "rules") is_known_text() { local file="$1" @@ -78,6 +79,20 @@ is_binary() { return 1 } +# Cat a file, stripping UTF-8 BOM if present (avoids inline BOMs in combined output) +cat_strip_bom() { + local file="$1" + # Check if file starts with UTF-8 BOM (EF BB BF) + local header + header=$(head -c 3 "$file" | od -A n -t x1 | tr -d ' \n') + if [ "$header" = "efbbbf" ]; then + # Skip first 3 bytes (the BOM) + tail -c +4 "$file" + else + cat "$file" + fi +} + echo "=== Repo Collector ===" echo "Timestamp: $TIMESTAMP" echo "Working dir: $SCRIPT_DIR" @@ -132,14 +147,16 @@ for repo_dir in */; do updated_repos+=("$repo_dir") echo " Updating: ${repo_dir}/" - # Write header + # Write UTF-8 BOM + header + # printf writes raw bytes; echo writes the text header after it + printf '\xEF\xBB\xBF' > "$output_file" { echo "================================================================================" echo "Repository: ${repo_dir}" echo "Collected: ${TIMESTAMP}" echo "================================================================================" echo "" - } > "$output_file" + } >> "$output_file" # Find all files, excluding skip dirs # First pass: collect READMEs @@ -147,7 +164,7 @@ for repo_dir in */; do rel_path="${file#${repo_dir}/}" { echo "--- FILE: ${rel_path} ---" - cat "$file" + cat_strip_bom "$file" echo "" echo "" } >> "$output_file" @@ -200,10 +217,10 @@ for repo_dir in */; do continue fi - # Include the file + # Include the file (strip BOM from source to avoid inline BOMs) { echo "--- FILE: ${rel_path} ---" - cat "$file" + cat_strip_bom "$file" echo "" echo "" } >> "$output_file"