.PHONY: help build build-release frontend backend android install-android-app run migrate reconcile reconcile-delete tidy test testdata clean # Default goal: print the list of available targets. .DEFAULT_GOAL := help # Android SDK location. Override when the SDK lives elsewhere. ANDROID_SDK ?= $(HOME)/Android/sdk # adb from the Android SDK's platform-tools (matches the SDK we build against). ADB ?= $(ANDROID_SDK)/platform-tools/adb # Device serial for `install-android-app` (falls back to $ANDROID_SERIAL). When # empty, the sole connected device is used; with several connected devices the # target lists them and stops instead of guessing. Override with SERIAL=. SERIAL ?= $(ANDROID_SERIAL) # Release version stamped into the binary (-X main.version). Override with VERSION=... VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) # Seed testdata parameters (see `make testdata`). Counts: ALBUMS, PHOTOS, SEED, TIMEOUT. # Boolean (set to 1 to enable): TAGS, COMMENTS, LIKES, SHARE, INVITES, WAIT, DRY, VIDEO. # VIDEO requires ffmpeg on PATH; it is skipped without it. Extra passthrough flags: ARGS. ALBUMS ?= 2 PHOTOS ?= 10 SEED ?= 0 TIMEOUT ?= 30 # Print the list of available targets. help: @echo "Usage: make " @echo @echo "Available targets:" @grep -hE '^[a-zA-Z0-9_-]+:' $(MAKEFILE_LIST) | cut -d: -f1 | sed 's/^/ /' # Frontend sources whose changes must trigger a rebuild. `backend/static/index.html` # doubles as the build stamp, so a clean clone or `make clean` (which empties # backend/static) forces the next build to regenerate. FRONTEND_OUT := backend/static/index.html # `npm ci` is only needed when the manifest/lockfile change. FRONTEND_DEPS_STAMP := frontend/node_modules/.install-stamp # Build the SvelteKit bundle and copy it into backend/static (embedded via # go:embed). Incremental: recompile only when frontend sources are newer than the # embedded bundle. Paths are checked from the recipe (not as make prerequisites) # because SvelteKit route names like `(app)` break make's filename parsing. frontend: @set -e; \ if [ ! -f $(FRONTEND_DEPS_STAMP) ] \ || [ frontend/package.json -nt $(FRONTEND_DEPS_STAMP) ] \ || [ frontend/package-lock.json -nt $(FRONTEND_DEPS_STAMP) ]; then \ echo '> cd frontend && npm ci'; \ cd frontend && npm ci; \ touch node_modules/.install-stamp; \ cd ..; \ fi; \ if [ ! -f $(FRONTEND_OUT) ] \ || [ -n "$$(find frontend/src frontend/static -newer $(FRONTEND_OUT) -print -quit)" ] \ || [ frontend/package.json -nt $(FRONTEND_OUT) ] \ || [ frontend/package-lock.json -nt $(FRONTEND_OUT) ] \ || [ frontend/svelte.config.js -nt $(FRONTEND_OUT) ] \ || [ frontend/vite.config.ts -nt $(FRONTEND_OUT) ] \ || [ frontend/tailwind.config.ts -nt $(FRONTEND_OUT) ] \ || [ frontend/tsconfig.json -nt $(FRONTEND_OUT) ]; then \ echo '> cd frontend && npm run build'; \ cd frontend && npm run build; \ cd ..; \ rm -rf backend/static/*; \ cp -r frontend/build/* backend/static/; \ else \ echo 'frontend: up to date'; \ fi backend: mkdir -p bin # -tags nodynamic forces gen2brain/webp to use its pure-Go transpiled path # (no purego/dlopen), keeping the binary fully static. cd backend && CGO_ENABLED=0 go build -tags nodynamic -trimpath -ldflags="-s -w" \ -o ../bin/galo ./cmd/server build: frontend backend # Android client (independent of the server build). Requires the Android SDK. android: cd android && ANDROID_HOME=$(ANDROID_SDK) ./gradlew :app:assembleDebug # Install the debug Android client on a connected device. Builds the APK via the # `android` target first; only devices in the `device` state are considered. install-android-app: android @devices="$$($(ADB) devices | awk 'NR>1 && $$2 == "device" {print $$1}')"; \ if [ -n "$(SERIAL)" ]; then \ target="$(SERIAL)"; \ else \ count=$$(printf '%s\n' "$$devices" | grep -c .); \ if [ "$$count" -eq 0 ]; then \ echo "install-android-app: no connected device (see '$(ADB) devices')" >&2; \ exit 1; \ elif [ "$$count" -gt 1 ]; then \ echo "install-android-app: several devices connected; set SERIAL=:" >&2; \ printf '%s\n' "$$devices" | sed 's/^/ /' >&2; \ exit 1; \ else \ target="$$devices"; \ fi; \ fi; \ echo "Installing app-debug.apk on $$target"; \ $(ADB) -s "$$target" install -r android/app/build/outputs/apk/debug/app-debug.apk # Release build: full static, stripped and (if available) UPX-compressed binary # with the version embedded. Produces bin/galo and a copy at bin/galo-$(VERSION). build-release: frontend mkdir -p bin cd backend && CGO_ENABLED=0 go build -tags nodynamic -trimpath \ -ldflags="-s -w -X main.version=$(VERSION)" -o ../bin/galo ./cmd/server strip --strip-all bin/galo @if command -v upx >/dev/null 2>&1; then \ upx --best --quiet bin/galo && echo "upx: bin/galo compressed"; \ else \ echo "upx not found - skipping compression (install upx-ucl to enable)"; \ fi cp bin/galo bin/galo-$(VERSION) @echo "release: bin/galo-$(VERSION)" run: frontend backend ./bin/galo migrate: cd backend && go run ./cmd/migrate # Reconcile object storage against the metadata journal (report only). reconcile: cd backend && go run ./cmd/reconcile # Reconcile and delete orphaned blobs older than --min-age (default 1h). # Override the grace period when needed: make reconcile-delete MIN_AGE=1m reconcile-delete: cd backend && go run ./cmd/reconcile --delete $(if $(MIN_AGE),--min-age $(MIN_AGE),) tidy: cd backend && go mod tidy test: cd backend && go test ./... # Seed test data into a running instance through the public API (additive only). # Credentials come from the environment: GALO_EMAIL, GALO_PASSWORD # (optional GALO_BASE_URL). Example: # make testdata ALBUMS=3 PHOTOS=5 TAGS=1 LIKES=1 SEED=42 testdata: @python3 scripts/seed.py \ --albums $(ALBUMS) \ --photos-per-album $(PHOTOS) \ --seed $(SEED) \ --timeout $(TIMEOUT) \ $(if $(filter 1,$(TAGS)),--tags,) \ $(if $(filter 1,$(COMMENTS)),--comments,) \ $(if $(filter 1,$(LIKES)),--likes,) \ $(if $(filter 1,$(SHARE)),--share-links,) \ $(if $(filter 1,$(INVITES)),--invites,) \ $(if $(filter 1,$(VIDEO)),--video,) \ $(if $(filter 1,$(WAIT)),--wait-variants,) \ $(if $(filter 1,$(DRY)),--dry-run,) \ $(ARGS) clean: rm -rf bin backend/static/* touch backend/static/.gitkeep