deploy: static binary and a compose project name that does not collide

Two failures from the first real deploy, both repo-side:

1. The container logged "exec /usr/bin/resume-server: no such file or
   directory" and restarted until the health gate failed. The build host has
   cgo enabled, so the binary carried the nix glibc interpreter, and the
   distroless/static image has no dynamic loader. Every SQLite path already
   uses pure-Go modernc.org/sqlite, so build with CGO_ENABLED=0 and ship a
   static binary.

2. Compose warned about orphan containers ai-resume-backend-1 and
   ai-resume-db-1: /home/sam/deployment/ai-resume on .13 already owns the
   `ai-resume` compose project (on the ai-mesh network). Sharing the name makes
   each stack visible as orphans to the other, so a `compose down
   --remove-orphans` in either could stop the other. Run the site as project
   `ai-resume-site`; the image tag stays ai-resume:local and the port binding,
   mem_limit and memswap_limit are untouched.
This commit is contained in:
2026-10-26 20:53:25 +11:00
parent c95e5361e3
commit 24590cc351
3 changed files with 60 additions and 6 deletions

View File

@@ -12,10 +12,18 @@ VALIDATOR := $(PYTHON) tools/validate_content.py
GO ?= go GO ?= go
TEMPL ?= templ TEMPL ?= templ
# The runtime image is distroless/static (deploy/Dockerfile): no libc, no
# dynamic loader. Everything the server needs is pure Go (modernc.org/sqlite),
# so a static binary is buildable and is what the container can actually exec.
# Without this the build host's cgo links the nix glibc loader and the container
# fails with "exec /usr/bin/resume-server: no such file or directory".
CGO_ENABLED ?= 0
BIN := bin BIN := bin
DIST := dist DIST := dist
.PHONY: build dist generate dev test validate validate-self-test fmt vet clean help .PHONY: build dist generate dev test validate validate-self-test fmt vet clean help \
corpus corpus-sync corpus-rebuild corpus-audit corpus-verify corpus-demo corpus-test
help: help:
@echo "make build templ generate, compile bin/, no dist" @echo "make build templ generate, compile bin/, no dist"
@@ -26,11 +34,19 @@ help:
@echo "make validate validate content/** front-matter against the frozen schema" @echo "make validate validate content/** front-matter against the frozen schema"
@echo "make fmt vet gofmt / go vet over cmd and internal" @echo "make fmt vet gofmt / go vet over cmd and internal"
@echo "make clean remove bin/ and dist/" @echo "make clean remove bin/ and dist/"
@echo ""
@echo "corpus (Phase 4, python3 -m corpus.cli):"
@echo "make corpus rebuild the corpus from content/ (drops the collection)"
@echo "make corpus-sync incremental update; only changed documents are re-embedded"
@echo "make corpus-audit freshness + drift audit (exit 1 on findings)"
@echo "make corpus-verify check the index against content/ and Qdrant"
@echo "make corpus-demo same query with and without an area filter"
@echo "make corpus-test corpus unit tests (no container, no network)"
build: generate build: generate
@mkdir -p $(BIN) @mkdir -p $(BIN)
$(GO) build -trimpath -buildvcs=false -o $(BIN)/resume-build ./cmd/build CGO_ENABLED=$(CGO_ENABLED) $(GO) build -trimpath -buildvcs=false -o $(BIN)/resume-build ./cmd/build
$(GO) build -trimpath -buildvcs=false -o $(BIN)/resume-server ./cmd/server CGO_ENABLED=$(CGO_ENABLED) $(GO) build -trimpath -buildvcs=false -o $(BIN)/resume-server ./cmd/server
dist: build dist: build
@$(BIN)/resume-build --dist $(DIST) @$(BIN)/resume-build --dist $(DIST)
@@ -57,5 +73,34 @@ fmt:
vet: vet:
@$(GO) vet ./... @$(GO) vet ./...
# -- corpus (Phase 4, docs/phase-4-corpus.md) --------------------------------
#
# The corpus is data, not a build artefact: it lives in data/corpus/ (gitignored)
# and in Qdrant. RESUME_QDRANT_URL defaults to http://127.0.0.1:6333 and
# RESUME_EMBED_PROVIDER defaults to local-hashing; production sets both in the
# environment, never in this file.
CORPUS := $(PYTHON) -m corpus.cli
corpus: corpus-rebuild
corpus-rebuild:
@$(CORPUS) rebuild
corpus-sync:
@$(CORPUS) sync
corpus-audit:
@$(CORPUS) audit --fail-on-findings
corpus-verify:
@$(CORPUS) verify
corpus-demo:
@$(CORPUS) demo-filter --query "distributed audio endpoints" --area edge
corpus-test:
@$(PYTHON) -m unittest discover -s corpus/tests -t .
clean: clean:
@rm -rf $(BIN) $(DIST) @rm -rf $(BIN) $(DIST)

View File

@@ -12,7 +12,14 @@
# #
# `curl http://127.0.0.1:8096/healthz` must answer with static ready. # `curl http://127.0.0.1:8096/healthz` must answer with static ready.
name: ai-resume # The compose project name is deliberately not `ai-resume`: /home/sam/deployment/
# ai-resume on .13 already runs a compose project by that name (backend + db on
# the ai-mesh network). Sharing the name would make this site's containers show
# up as orphans in that project and vice versa, so anything running `compose
# down --remove-orphans` under either project could stop the other. The image
# tag stays ai-resume:local (Phase 3 fixes it); only the project namespace is
# distinct.
name: ai-resume-site
services: services:
resume: resume:

View File

@@ -38,7 +38,9 @@
set -Eeuo pipefail set -Eeuo pipefail
APP=ai-resume APP=ai-resume # image tag (fixed by Phase 3 §3.2)
PROJECT=ai-resume-site # compose project; distinct from the older
# /home/sam/deployment/ai-resume project on .13
PORT=8096 PORT=8096
HEALTH_PATH=/healthz HEALTH_PATH=/healthz
@@ -92,7 +94,7 @@ done
# ---------------------------------------------------------------- helpers --- # ---------------------------------------------------------------- helpers ---
compose() { docker compose -f "$COMPOSE" -p "$APP" "$@"; } compose() { docker compose -f "$COMPOSE" -p "$PROJECT" "$@"; }
current_target() { readlink -- "$CURRENT" 2>/dev/null || true; } current_target() { readlink -- "$CURRENT" 2>/dev/null || true; }
previous_target() { readlink -- "$PREVIOUS" 2>/dev/null || true; } previous_target() { readlink -- "$PREVIOUS" 2>/dev/null || true; }