The library admin page carried an inline <script> block of global functions (toggleLibraryPanel, openEditModal, openDeleteModal, openFolderBrowser plus the delete-modal close listener). They move into the admin Alpine component as typed methods; the page body gains x-data="admin" and the call sites switch onclick= -> @click. Behavior unchanged. Guardrail: make lint-rendering (wired into make test) fails when a web/src file builds more HTML template literals than its recorded baseline (web/src/rendering-allowlist.txt, seeded at 18 sites across 9 files post-twin-deletion) or when a .templ file gains an inline script block. theme_flash_guard.templ is the only sanctioned inline script; web/src/reader is a sanctioned island. theme.ts: Array.from(classList) — DOMTokenList spread needs lib.dom.iterable, which the tsconfig does not include.
204 lines
8.4 KiB
Makefile
204 lines
8.4 KiB
Makefile
.PHONY: help test test-integration test-all rebuild-app rebuild-app-force rebuild rebuild-force rebuild-force-db clean restart up down logs ps test-env-up test-env-down release
|
|
|
|
# Include .env file for environment variables (single source of truth)
|
|
# Ignore if .env doesn't exist yet
|
|
ifneq (,$(wildcard ./.env))
|
|
include .env
|
|
export
|
|
endif
|
|
|
|
# Auto-detect container runtime: prefer docker, fall back to podman
|
|
# Override with: CONTAINER_RUNTIME=podman make rebuild-app
|
|
CONTAINER_RUNTIME ?= $(shell command -v docker 2>/dev/null || command -v podman 2>/dev/null)
|
|
|
|
# Dev compose stack: base prod file merged with the dev override (local build + tests).
|
|
# Prod deploy does NOT use this — it runs plain `docker compose` against the base file only.
|
|
COMPOSE := $(CONTAINER_RUNTIME) compose -f docker-compose.yml -f docker-compose.dev.yml
|
|
|
|
# Default target
|
|
help:
|
|
@echo "Available targets:"
|
|
@echo ""
|
|
@echo "Development:"
|
|
@echo " make up - Start containers (if already built)"
|
|
@echo " make rebuild - Rebuild all containers (database preserved)"
|
|
@echo " make rebuild-force - Rebuild all containers, no cache (database preserved)"
|
|
@echo " make rebuild-app - Rebuild app container only (fast, database preserved)"
|
|
@echo " make rebuild-app-force - Rebuild app container only, no cache (database preserved)"
|
|
@echo " make rebuild-force-db - Rebuild all containers, no cache (DELETES database)"
|
|
@echo " make restart - Restart app container (preserves db)"
|
|
@echo " make down - Stop all containers (preserves volumes)"
|
|
@echo " make clean - Stop and remove all containers and volumes"
|
|
@echo ""
|
|
@echo "Status & Logs:"
|
|
@echo " make ps - Show container status"
|
|
@echo " make logs - Show container logs (follow mode)"
|
|
@echo ""
|
|
@echo "Testing:"
|
|
@echo " make test - Run unit tests (from host, fast)"
|
|
@echo " make test-integration - Run integration tests (in containers, matches prod)"
|
|
@echo " make test-all - Run all tests (unit + integration)"
|
|
@echo " make test-env-up - Start containers with test mode enabled"
|
|
@echo " make test-env-down - Stop test environment"
|
|
@echo ""
|
|
@echo "Verification:"
|
|
@echo ""
|
|
@echo "Release:"
|
|
@echo " ./release v0.3.0 - Tag, push, and release (notes auto-generated from commits)"
|
|
|
|
# Run unit tests locally (fast, no containers)
|
|
test: lint-rendering
|
|
go test ./internal/... -v -short
|
|
|
|
# Rendering-ownership guard (AGENTS.md → Rendering ownership): templ renders
|
|
# UI markup, JS orchestrates. Fails when a web/src file builds more HTML
|
|
# template literals than its recorded baseline (web/src/rendering-allowlist.txt),
|
|
# or when a .templ file gains an inline <script> block (theme_flash_guard.templ
|
|
# is the only sanctioned one). Shrink baselines as fragments move to templ
|
|
# partials; grow one only with a written reason.
|
|
lint-rendering:
|
|
@violations=""; \
|
|
for f in $$(grep -rlE 'innerHTML[[:space:]]*=[[:space:]]*\`' web/src --include='*.ts' | grep -v 'web/src/reader/'); do \
|
|
count=$$(grep -cE 'innerHTML[[:space:]]*=[[:space:]]*\`' $$f); \
|
|
allowed=$$(grep -F "$$f:" web/src/rendering-allowlist.txt | cut -d: -f2); \
|
|
allowed=$${allowed:-0}; \
|
|
if [ $$count -gt $$allowed ]; then \
|
|
violations="$$violations $$f ($$count > $$allowed)"; \
|
|
fi; \
|
|
done; \
|
|
for f in $$(grep -rln '<script>' templates --include='*.templ'); do \
|
|
case $$f in *theme_flash_guard*) continue;; esac; \
|
|
violations="$$violations $$f (inline script)"; \
|
|
done; \
|
|
if [ -n "$$violations" ]; then \
|
|
echo "rendering-ownership violations:$$violations"; exit 1; \
|
|
fi; \
|
|
echo "rendering ownership OK"
|
|
|
|
# Run integration tests in containers (matches production environment)
|
|
test-integration:
|
|
@echo "Building test containers..."
|
|
$(COMPOSE) --profile tests build
|
|
@echo "Starting application containers..."
|
|
$(COMPOSE) up -d db app
|
|
@echo "Waiting for services to be healthy..."
|
|
@until $(CONTAINER_RUNTIME) exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do \
|
|
echo " Database not ready yet..."; \
|
|
sleep 2; \
|
|
done; \
|
|
echo " ✓ Database is ready"
|
|
@until $(CONTAINER_RUNTIME) exec bookhoard curl -sf http://localhost:8765/health > /dev/null 2>&1; do \
|
|
echo " Application not ready yet..."; \
|
|
sleep 2; \
|
|
done; \
|
|
echo " ✓ Application is ready"
|
|
@echo ""
|
|
@echo "Running integration tests in container..."
|
|
$(COMPOSE) --profile tests run --rm tests
|
|
@echo ""
|
|
@echo "✅ Integration tests completed!"
|
|
@echo "📝 Containers are still running. Use 'make logs' to view logs or 'make clean' to stop."
|
|
|
|
# Run all tests
|
|
test-all: test test-integration
|
|
|
|
# Rebuild app container only (preserve DB, with cache)
|
|
rebuild-app:
|
|
@echo "Rebuilding app container (database stays running)..."
|
|
$(COMPOSE) up --build --force-recreate -d app
|
|
@echo "✓ App container rebuilt and restarted"
|
|
|
|
# Rebuild app container only (preserve DB, no cache)
|
|
rebuild-app-force:
|
|
@echo "Force rebuilding app container (database stays running, no cache)..."
|
|
$(COMPOSE) build --no-cache app
|
|
$(COMPOSE) up --force-recreate -d app
|
|
@echo "✓ App container rebuilt and restarted"
|
|
|
|
# Rebuild all containers (preserve DB, with cache)
|
|
rebuild:
|
|
@echo "Rebuilding all containers (database preserved)..."
|
|
$(COMPOSE) up --build --force-recreate -d
|
|
@echo "✓ All containers rebuilt and restarted"
|
|
|
|
# Rebuild all containers (preserve DB, no cache)
|
|
rebuild-force:
|
|
@echo "Force rebuilding all containers (database preserved, no cache)..."
|
|
$(COMPOSE) build --no-cache
|
|
$(COMPOSE) up --force-recreate -d
|
|
@echo "✓ All containers rebuilt and restarted"
|
|
|
|
# Rebuild all containers (remove DB, no cache)
|
|
rebuild-force-db:
|
|
@echo "Force rebuilding all containers (database will be DELETED, no cache)..."
|
|
$(COMPOSE) down -v
|
|
$(COMPOSE) build --no-cache
|
|
$(COMPOSE) up --force-recreate -d
|
|
@echo "✓ All containers rebuilt and restarted"
|
|
|
|
# Stop and remove containers
|
|
clean:
|
|
$(COMPOSE) down -v
|
|
|
|
# Quick start (if already built)
|
|
up:
|
|
$(COMPOSE) up -d
|
|
|
|
# Stop all containers (alias for clean)
|
|
down:
|
|
$(COMPOSE) down
|
|
|
|
# Restart app container (preserves database)
|
|
restart:
|
|
@echo "Restarting app container (database stays running)..."
|
|
$(COMPOSE) restart app
|
|
@echo "✓ App container restarted"
|
|
|
|
# Show container status
|
|
ps:
|
|
$(COMPOSE) ps
|
|
|
|
# Show container logs
|
|
logs:
|
|
$(COMPOSE) logs -f
|
|
|
|
# Start containers with test mode enabled for manual testing
|
|
test-env-up:
|
|
@echo "Starting containers with test mode enabled..."
|
|
TEST_MODE=true RATE_LIMIT_ENABLED=false REQUESTS_PER_MINUTE=1000 $(COMPOSE) up --build --force-recreate -d
|
|
@echo "Waiting for services to be ready..."
|
|
@until $(CONTAINER_RUNTIME) exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do sleep 1; done
|
|
@until $(CONTAINER_RUNTIME) exec bookhoard curl -sf http://localhost:8765/health > /dev/null 2>&1; do sleep 1; done
|
|
@echo "✓ Test environment is ready!"
|
|
@echo "Application available at http://localhost:8765"
|
|
|
|
# Stop test environment
|
|
test-env-down:
|
|
$(COMPOSE) down -v
|
|
|
|
|
|
|
|
# Create an annotated version tag carrying auto-generated release notes (git-cliff)
|
|
# and push it. The tag push triggers .gitea/workflows/release.yml, which builds the
|
|
# image and publishes a Gitea Release whose body is this tag's message. Notes come
|
|
# entirely from Conventional Commits — no hand-written message required.
|
|
#
|
|
# git-cliff's --latest needs the tag to exist to scope the notes, so we create a
|
|
# throwaway lightweight tag, generate the notes, replace it with an annotated tag,
|
|
# then push. --cleanup=verbatim keeps the markdown "###" group headers (git's
|
|
# default cleanup would strip lines starting with "#").
|
|
#
|
|
# Requires git-cliff: https://git-cliff.org/install
|
|
# Usage: make release VERSION=v0.3.0
|
|
release:
|
|
@test -n "$(VERSION)" || { echo "Usage: make release VERSION=v0.3.0"; exit 1; }
|
|
@command -v git-cliff >/dev/null 2>&1 || { echo "git-cliff not found — install: https://git-cliff.org/install"; exit 1; }
|
|
@if git rev-parse "$(VERSION)" >/dev/null 2>&1; then echo "Tag $(VERSION) already exists locally — delete it first: git tag -d $(VERSION)"; exit 1; fi
|
|
@echo "Generating release notes for $(VERSION)..."
|
|
@git tag "$(VERSION)" HEAD && \
|
|
(git cliff --latest --config cliff.toml > .release-notes.tmp && git tag -d "$(VERSION)" >/dev/null) || \
|
|
{ git tag -d "$(VERSION)" >/dev/null 2>&1; rm -f .release-notes.tmp; echo "git-cliff failed"; exit 1; }
|
|
@git tag -a --cleanup=verbatim -F .release-notes.tmp "$(VERSION)" HEAD && rm -f .release-notes.tmp
|
|
@git push origin "$(VERSION)"
|
|
@echo "Pushed $(VERSION) — Gitea Actions will build the image and publish the Release."
|