Files
john-okeefe 451aa48aec
Release / build-and-push (push) Successful in 2m42s
ci(release): auto-generate release notes via git-cliff
Replace the image-only tag pipeline with a full release workflow that also
publishes a Gitea Release whose body is the annotated tag's message,
generated from Conventional Commits by git-cliff. No hand-written release
notes are required.

- cliff.toml: group commits (Features, Bug Fixes, Refactor, Documentation,
  Tests, Miscellaneous Tasks) with scopes and short-SHA links; emit only the
  current tag's section rather than the full history.
- .gitea/workflows/release.yml: tag-driven. Reads the release body from the
  annotated tag (git tag -l --format), so the tag message and release body are
  a single source of truth. Idempotent create/PATCH; prints the Gitea API
  error body on failure so a 403 names the missing token scope instead of
  failing silently. Adds a workflow_dispatch tag input so manual re-runs
  target the right tag instead of the default branch.
- Makefile: release VERSION=vX.Y.Z generates notes via git cliff --latest
  against a throwaway tag, then creates an annotated tag with
  --cleanup=verbatim so the markdown group headers are preserved (git's
  default cleanup strips lines starting with "#").
- release: project-attached wrapper accepting a positional version arg
  (./release 0.3.0 or ./release v0.3.0) and auto-prefixing v, for ergonomic
  one-command releases.
2026-07-30 15:40:27 -04:00

188 lines
7.6 KiB
Makefile

.PHONY: help test test-integration test-all rebuild rebuild-force rebuild-app rebuild-app-force rebuild-force-db clean restart up down logs ps test-env-up test-env-down verify-guidelines verify-quick 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 " make verify-guidelines - Run comprehensive guidelines check"
@echo " make verify-quick - Run quick guidelines check"
@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:
go test ./internal/... -v -short
# 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
# Verify project guidelines compliance
verify-guidelines:
@echo "Running comprehensive project guidelines verification..."
@./scripts/verify-guidelines.sh
verify-quick:
@echo "Running quick project guidelines verification..."
@./scripts/verify-quick.sh
# 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."