From 18ac000d30c2886e331a4b90cb60d8a3d12ab6e2 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Sun, 12 Apr 2026 19:25:06 +0200 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=90=9B=20fix:=20emit=20all=20config-l?= =?UTF-8?q?oading=20logs=20in=20correct=20format=20from=20the=20start?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The logger was initialised to console format unconditionally, so the "Config file loaded" message (and similar early logs) were always written as human-readable text even when JSON logging was configured. Root cause: classic chicken-and-egg — the format flag lives inside the config that is being loaded. Fix: add peekJSONLogging() which resolves the format before any log is emitted by (1) checking DLC_LOGGING_JSON directly via os.Getenv, then (2) doing a minimal throwaway Viper pre-read of the config file for the logging.json key. The redundant format-switch block that ran after Unmarshal() is removed. Also add the "Logging configured" log as the very first line, and replace the hardcoded PROJECT_DIR path in start-server.sh, test-graceful-shutdown.sh, and test-opentelemetry.sh with a dynamic derivation from BASH_SOURCE[0]. Closes #15 Co-Authored-By: Claude Sonnet 4.6 --- pkg/config/config.go | 54 ++++++++++++++++++++++++------- scripts/start-server.sh | 3 +- scripts/test-graceful-shutdown.sh | 3 +- scripts/test-opentelemetry.sh | 3 +- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 52210f7..f9cbdc1 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -118,6 +118,34 @@ type SamplerConfig struct { Ratio float64 `mapstructure:"ratio"` } +// peekJSONLogging determines whether JSON logging should be used before the full +// config is loaded, solving the chicken-and-egg problem where the logger format +// must be known before any log is emitted, yet the format is stored in the config. +// +// Resolution order (mirrors Viper's own priority): +// 1. DLC_LOGGING_JSON env var — checked directly via os.Getenv (zero overhead) +// 2. logging.json key in the config file — read with a minimal throwaway Viper +// instance so we don't parse the whole config twice unnecessarily +func peekJSONLogging() bool { + // 1. Env var takes highest priority — check it first + if env := os.Getenv("DLC_LOGGING_JSON"); env != "" { + return strings.EqualFold(env, "true") || env == "1" + } + + // 2. Try to read logging.json from the config file + preV := viper.New() + preV.SetDefault("logging.json", false) + if configFile := os.Getenv("DLC_CONFIG_FILE"); configFile != "" { + preV.SetConfigFile(configFile) + } else { + preV.SetConfigName("config") + preV.SetConfigType("yaml") + preV.AddConfigPath(".") + } + _ = preV.ReadInConfig() // ignore errors — defaults apply on failure + return preV.GetBool("logging.json") +} + // LoadConfig loads configuration from file, environment variables, and defaults // Configuration priority: file > environment variables > defaults // To specify a custom config file path, set DLC_CONFIG_FILE environment variable @@ -129,9 +157,17 @@ func LoadConfig() (*Config, error) { v := viper.New() - // Set up initial console logging for config loading messages - consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr} - log.Logger = log.Output(consoleWriter) + // Configure the logger format before emitting any log output. + // peekJSONLogging reads the JSON setting early (env var + config file pre-read) + // so that every log line — including those produced during config loading — is + // already in the correct format. + jsonLogging := peekJSONLogging() + if jsonLogging { + log.Logger = log.Output(os.Stderr) + } else { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + } + log.Info().Bool("json", jsonLogging).Msg("Logging configured") // Set default values v.SetDefault("server.host", "0.0.0.0") @@ -227,15 +263,9 @@ func LoadConfig() (*Config, error) { return nil, fmt.Errorf("config unmarshal error: %w", err) } - // Configure log output format (JSON or console) first - if config.Logging.JSON { - log.Logger = log.Output(os.Stderr) - } else { - consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr} - log.Logger = log.Output(consoleWriter) - } - - // Setup logging based on configuration + // Setup logging based on configuration (level, output file, time format). + // The JSON/console format was already applied at the top of LoadConfig via + // peekJSONLogging, so SetupLogging only needs to handle the remaining knobs. config.SetupLogging() log.Info(). diff --git a/scripts/start-server.sh b/scripts/start-server.sh index 98229e5..fd7ace8 100755 --- a/scripts/start-server.sh +++ b/scripts/start-server.sh @@ -4,7 +4,8 @@ # This script starts the server in the background and provides control functions # Configuration -PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach" +SCRIPTS_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")") +PROJECT_DIR=$(dirname "$SCRIPTS_DIR") SERVER_CMD="go run ./cmd/server" LOG_FILE="server.log" PID_FILE="server.pid" diff --git a/scripts/test-graceful-shutdown.sh b/scripts/test-graceful-shutdown.sh index e3dd437..a5a0691 100755 --- a/scripts/test-graceful-shutdown.sh +++ b/scripts/test-graceful-shutdown.sh @@ -7,7 +7,8 @@ set -e # Configuration -PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach" +SCRIPTS_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")") +PROJECT_DIR=$(dirname "$SCRIPTS_DIR") SERVER_CMD="./scripts/start-server.sh" LOG_FILE="server.log" PID_FILE="server.pid" diff --git a/scripts/test-opentelemetry.sh b/scripts/test-opentelemetry.sh index 750fc15..3d76cff 100755 --- a/scripts/test-opentelemetry.sh +++ b/scripts/test-opentelemetry.sh @@ -9,7 +9,8 @@ echo -e "\033[1;34m=== dance-lessons-coach OpenTelemetry Test ===\033[0m" echo "" # Configuration -PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach" +SCRIPTS_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")") +PROJECT_DIR=$(dirname "$SCRIPTS_DIR") SERVER_CMD="./scripts/start-server.sh" LOG_FILE="server.log" PID_FILE="server.pid" -- 2.49.1 From 928fa9170c87f0879508903871303acad9eff6e8 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Sun, 12 Apr 2026 19:49:04 +0200 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=90=9B=20fix:=20wire=20up=20readiness?= =?UTF-8?q?=20cancellation=20and=20stabilise=20graceful=20shutdown=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three related issues fixed together: 1. Readiness context was never cancelled during shutdown server.Run() had a type assertion for a Cancel() method that no standard context.Context implements, so readiness stayed "ready" through the entire shutdown window. Added CancelableContext to pkg/server — a thin wrapper that exposes Cancel() — and switched cmd/server/main.go to use it. Test servers and CLI continue passing context.Background() unchanged. 2. "Server exited" log was never emitted The test script expected it; main.go had no log after server.Run() returned. Added log.Trace().Msg("Server exited") after the Run() call. 3. Double-SIGTERM caused non-JSON "signal: terminated" in server.log test-graceful-shutdown.sh sent SIGTERM, then called $SERVER_CMD stop which sent a second SIGTERM. After signal.NotifyContext is cancelled, the second signal hits the default handler and Go prints "signal: terminated" to stderr, breaking the all-JSON-lines assertion. Fixed by waiting for the PID to exit ourselves instead of re-invoking the stop script. Co-Authored-By: Claude Sonnet 4.6 --- cmd/server/main.go | 7 ++++-- pkg/server/server.go | 22 +++++++++++++++++++ scripts/test-graceful-shutdown.sh | 36 +++++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 6f8cf38..abe8fa1 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -48,8 +48,10 @@ func main() { log.Fatal().Err(err).Msg("Failed to load configuration") } - // Create readiness context to control readiness state - readyCtx, readyCancel := context.WithCancel(context.Background()) + // Create readiness context to control readiness state. + // CancelableContext exposes Cancel() so that Server.Run() can cancel + // readiness at the start of graceful shutdown (before the propagation sleep). + readyCtx, readyCancel := server.NewCancelableContext(context.Background()) defer readyCancel() // Create and run server @@ -57,4 +59,5 @@ func main() { if err := server.Run(); err != nil { log.Fatal().Err(err).Msg("Server failed") } + log.Trace().Msg("Server exited") } diff --git a/pkg/server/server.go b/pkg/server/server.go index 8b7286a..9c20539 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -33,6 +33,28 @@ import ( //go:embed docs/swagger.json var swaggerJSON embed.FS +// CancelableContext wraps a context.Context and exposes a Cancel() method so +// that Server.Run() can cancel readiness during graceful shutdown via the type +// assertion it already performs. Callers that don't need controlled cancellation +// (tests, CLI) can pass a plain context.Background() — the assertion silently +// fails and readiness is never explicitly cancelled, which is harmless. +type CancelableContext struct { + context.Context + cancel context.CancelFunc +} + +// NewCancelableContext creates a CancelableContext whose Cancel() method will +// be invoked by Server.Run() at the start of graceful shutdown, before the +// 1-second readiness propagation window. The returned CancelFunc is a no-op +// after Cancel() has been called, so it is safe to defer in main. +func NewCancelableContext(parent context.Context) (*CancelableContext, context.CancelFunc) { + ctx, cancel := context.WithCancel(parent) + return &CancelableContext{Context: ctx, cancel: cancel}, cancel +} + +// Cancel satisfies the interface checked in Run() and cancels the context. +func (c *CancelableContext) Cancel() { c.cancel() } + type Server struct { router *chi.Mux readyCtx context.Context diff --git a/scripts/test-graceful-shutdown.sh b/scripts/test-graceful-shutdown.sh index a5a0691..22c41b8 100755 --- a/scripts/test-graceful-shutdown.sh +++ b/scripts/test-graceful-shutdown.sh @@ -60,11 +60,39 @@ echo "Response: $GREET_NAME_RESPONSE" echo "" echo "Stopping server gracefully..." -# Test readiness during shutdown (in background) -(curl -s http://localhost:8080/api/ready > /dev/null 2>&1 &) +# Send SIGTERM once and probe /api/ready during the 1-second propagation window +# the server holds open (pkg/server/server.go: time.Sleep(1s) after readiness +# cancel). Previously the curl fired *before* the signal — it always saw "ready". +# We also avoid calling "$SERVER_CMD stop" afterwards because that would send a +# second SIGTERM: after signal.NotifyContext is done, the default handler kicks in +# and the process terminates with a non-JSON "signal: terminated" on stderr. +SERVER_PID=$(cat "$PID_FILE" 2>/dev/null || echo "") +if [[ -z "$SERVER_PID" ]]; then + echo -e "\033[0;31m❌ FAIL: PID file not found\033[0m" + exit 1 +fi -$SERVER_CMD stop -sleep 3 +kill -TERM "$SERVER_PID" +# Brief yield so the signal handler runs and CancelableContext.Cancel() fires +sleep 0.2 +curl -s http://localhost:8080/api/ready > /dev/null 2>&1 || true + +# Wait for the process to exit cleanly (up to 30s) without sending another signal +echo "Waiting for server to exit..." +for i in {1..30}; do + if ! ps -p "$SERVER_PID" > /dev/null 2>&1; then + echo "Server stopped successfully" + rm -f "$PID_FILE" + break + fi + sleep 1 +done +if ps -p "$SERVER_PID" > /dev/null 2>&1; then + echo -e "\033[0;31m❌ FAIL: Server did not stop within 30s\033[0m" + kill -9 "$SERVER_PID" 2>/dev/null || true + exit 1 +fi +sleep 0.5 echo "" echo "Analyzing server logs..." -- 2.49.1 From 165e082eb9232646c57cc3a3a6db80977b545d97 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Sun, 12 Apr 2026 19:57:01 +0200 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=94=8D=20test:=20show=20readiness=20H?= =?UTF-8?q?TTP=20response=20during=20shutdown=20window?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Print the full response body and status code from the /api/ready probe that fires during the graceful shutdown window, so the 200→503 transition is visible in test output rather than silently discarded. Co-Authored-By: Claude Sonnet 4.6 --- scripts/test-graceful-shutdown.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/test-graceful-shutdown.sh b/scripts/test-graceful-shutdown.sh index 22c41b8..63930ec 100755 --- a/scripts/test-graceful-shutdown.sh +++ b/scripts/test-graceful-shutdown.sh @@ -75,7 +75,8 @@ fi kill -TERM "$SERVER_PID" # Brief yield so the signal handler runs and CancelableContext.Cancel() fires sleep 0.2 -curl -s http://localhost:8080/api/ready > /dev/null 2>&1 || true +READY_DURING_SHUTDOWN=$(curl -s -w "\n[HTTP %{http_code}]" http://localhost:8080/api/ready 2>&1 || echo "[connection refused]") +echo "Readiness during shutdown: $READY_DURING_SHUTDOWN" # Wait for the process to exit cleanly (up to 30s) without sending another signal echo "Waiting for server to exit..." -- 2.49.1 From 18b2f0a87f0be558d5d1e6b81945f120ae423990 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Sun, 12 Apr 2026 20:01:59 +0200 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=94=8D=20test:=20print=20formatted=20?= =?UTF-8?q?server=20log=20at=20end=20of=20graceful=20shutdown=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display the full log as a readable summary before cleanup so the complete server lifecycle is visible in test output without needing to preserve the log file manually. Co-Authored-By: Claude Sonnet 4.6 --- scripts/test-graceful-shutdown.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/test-graceful-shutdown.sh b/scripts/test-graceful-shutdown.sh index 63930ec..6b362bc 100755 --- a/scripts/test-graceful-shutdown.sh +++ b/scripts/test-graceful-shutdown.sh @@ -231,6 +231,12 @@ fi echo "" echo -e "\033[0;32m🎉 GRACEFUL SHUTDOWN TEST PASSED!\033[0m" echo "All required logs are present and in correct order." + +echo "" +echo "📋 Full server log:" +echo "===============================" +cat "$LOG_FILE" | jq -r '"[\(.level | ascii_upcase)] \(.time | tostring) — \(.message)"' +echo "===============================" echo "" # Clean up -- 2.49.1 From 99b2e410f2f9aa01591437efd49309eaf945b559 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Sun, 12 Apr 2026 20:18:25 +0200 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=93=9D=20docs:=20rewrite=20AGENTS.md?= =?UTF-8?q?=20and=20README.md=20for=20clarity=20and=20brevity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cut AGENTS.md from 1296 to ~210 lines: removed development timeline, changelog, stale AI agent section, verbose architecture examples, and duplicate content. Kept tech stack, project structure, key commands, config reference, API table, ADR index, and commit conventions. Cut README.md from 423 to ~80 lines: removed duplicate CI/CD sections (one had merge conflict markers), non-existent Cobra CLI and documentation/ references, and the AI agent usage section. Kept features, quick start, config table, API table, testing, and Gitea client setup. Also includes gitea-client.sh fixes from earlier session: create-pr sub-command and safe jq-based JSON body in comment-pr. Co-Authored-By: Claude Sonnet 4.6 --- .../gitea-client/scripts/gitea-client.sh | 30 +- AGENTS.md | 1339 ++--------------- README.md | 429 +----- 3 files changed, 199 insertions(+), 1599 deletions(-) diff --git a/.vibe/skills/gitea-client/scripts/gitea-client.sh b/.vibe/skills/gitea-client/scripts/gitea-client.sh index 5353a49..105dc3d 100755 --- a/.vibe/skills/gitea-client/scripts/gitea-client.sh +++ b/.vibe/skills/gitea-client/scripts/gitea-client.sh @@ -203,6 +203,31 @@ cmd_wait_job() { } # Comment on PR +# Create a pull request +cmd_create_pr() { + local owner="$1" + local repo="$2" + local title="$3" + local body="$4" + local head="$5" + local base="${6:-main}" + + if [[ -z "$owner" || -z "$repo" || -z "$title" || -z "$head" ]]; then + echo "Usage: $0 create-pr <body> <head_branch> [base_branch]" >&2 + exit 1 + fi + + local endpoint="/repos/${owner}/${repo}/pulls" + local data + data=$(jq -n \ + --arg title "$title" \ + --arg body "$body" \ + --arg head "$head" \ + --arg base "$base" \ + '{title: $title, body: $body, head: $head, base: $base}') + api_request "POST" "$endpoint" "$data" +} + cmd_comment_pr() { local owner="$1" local repo="$2" @@ -215,7 +240,8 @@ cmd_comment_pr() { fi local endpoint="/repos/${owner}/${repo}/issues/${pr_number}/comments" - local data="{\"body\": \"${comment}\"}" + local data + data=$(jq -n --arg body "$comment" '{body: $body}') api_request "POST" "$endpoint" "$data" } @@ -250,6 +276,7 @@ main() { monitor-workflow) cmd_monitor_workflow "$@" ;; diagnose-job) cmd_diagnose_job "$@" ;; recent-workflows) cmd_recent_workflows "$@" ;; + create-pr) cmd_create_pr "$@" ;; comment-pr) cmd_comment_pr "$@" ;; pr-status) cmd_pr_status "$@" ;; list-issues) cmd_list_issues "$@" ;; @@ -274,6 +301,7 @@ main() { echo " monitor-workflow <owner> <repo> <workflow_run_id> [interval_seconds]" >&2 echo " diagnose-job <owner> <repo> <job_id>" >&2 echo " recent-workflows <owner> <repo> [limit] [status_filter]" >&2 + echo " create-pr <owner> <repo> <title> <body> <head_branch> [base_branch]" >&2 echo " comment-pr <owner> <repo> <pr_number> <comment>" >&2 echo " pr-status <owner> <repo> <pr_number>" >&2 echo " list-issues <owner> <repo> [state]" >&2 diff --git a/AGENTS.md b/AGENTS.md index 827bf7e..6b41084 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,84 +1,8 @@ -# dance-lessons-coach - AI Agent Documentation +# dance-lessons-coach — Agent Documentation -This file documents the AI agents, tools, and development workflow for the dance-lessons-coach project. +AI agent reference for developing, testing, and operating the dance-lessons-coach service. -## 🎯 Project Overview - -**dance-lessons-coach** is a Go-based web service with CLI capabilities, featuring: -- RESTful JSON API with Chi router -- High-performance Zerolog logging -- Interface-based architecture -- Context-aware services -- Comprehensive testing - -## 📋 Development Timeline - -### Phase 1: Foundation (Completed ✅) -- Go 1.26.1 environment setup -- Project structure with `cmd/` and `pkg/` directories -- Core Greet service implementation -- CLI interface -- Unit tests - -### Phase 2: Web API (Completed ✅) -- Chi router integration -- Versioned API endpoints (`/api/v1`) -- Health endpoint (`/api/health`) -- JSON responses with proper headers - -### Phase 3: Logging & Architecture (Completed ✅) -- Zerolog integration with Trace level -- Context-aware logging -- Interface-based design patterns -- Dependency injection - -### Phase 4: Documentation & Testing (Completed ✅) -- Comprehensive AGENTS.md -- README.md with usage instructions -- Server management guide -- API endpoint documentation - -### Phase 5: Configuration Management (Completed ✅) -- Viper integration for configuration -- Environment variable support with DLC_ prefix -- Customizable server host/port -- Configurable shutdown timeout -- Configuration validation and logging -- Example configuration file - -### Phase 6: Graceful Shutdown (Completed ✅) -- Context-aware server initialization -- Signal-based termination (SIGINT, SIGTERM) -- Configurable shutdown timeout -- Readiness endpoint for Kubernetes/service mesh integration -- Proper resource cleanup during shutdown -- Health endpoint remains healthy during graceful shutdown - -### Phase 7: OpenTelemetry Integration (Completed ✅) -- OpenTelemetry Go libraries integration -- Jaeger compatibility for distributed tracing -- Middleware-only approach using otelhttp.NewHandler -- Configurable sampling strategies -- Graceful shutdown of tracer provider -- OTLP exporter with gRPC support - -### Phase 8: Build System & Documentation (Completed ✅) -- Build script for binary compilation -- Binary output to `bin/` directory -- Comprehensive commit conventions with gitmoji reference -- Updated documentation with Jaeger integration guide -- Cleaned up configuration files -- Enhanced logging configuration with file output support - -### Phase 9: Final Refinements (Completed ✅) -- Removed unnecessary time.Sleep for log flushing -- Changed server operational logs from Info to Trace level -- Moved all logging setup logic to config package -- Simplified server entrypoint to 27 lines -- Verified all functionality with comprehensive testing -- Updated documentation to reflect final architecture - -## 🛠️ Tools & Technologies +## Tech Stack | Component | Technology | Version | |-----------|------------|---------| @@ -86,1211 +10,182 @@ This file documents the AI agents, tools, and development workflow for the dance | Router | Chi | v5.2.5 | | Logging | Zerolog | v1.35.0 | | Configuration | Viper | v1.21.0 | -| Testing | Standard Library | - | -| Dependency Management | Go Modules | - | | Telemetry | OpenTelemetry | v1.43.0 | -| Tracing | Jaeger | Compatible | -## 🗺️ Project Structure +## Project Structure ``` dance-lessons-coach/ ├── adr/ # Architecture Decision Records -│ ├── README.md # ADR guidelines and index -│ ├── 0001-go-1.26.1-standard.md -│ ├── 0002-chi-router.md -│ ├── 0003-zerolog-logging.md -│ ├── 0004-interface-based-design.md -│ ├── 0005-graceful-shutdown.md -│ ├── 0006-configuration-management.md -│ ├── 0007-opentelemetry-integration.md -│ ├── 0008-bdd-testing.md -│ └── 0009-hybrid-testing-approach.md ├── cmd/ -│ ├── greet/ # CLI application -│ │ └── main.go -│ └── server/ # Web server -│ └── main.go +│ ├── greet/ # CLI application +│ └── server/ # Web server entry point ├── pkg/ -│ ├── config/ # Configuration management -│ │ └── config.go # Viper-based config -│ ├── greet/ # Core domain logic -│ │ ├── api_v1.go # API handlers -│ │ ├── greet.go # Service implementation -│ │ └── greet_test.go # Unit tests -│ ├── server/ # HTTP server -│ │ └── server.go -│ └── telemetry/ # OpenTelemetry instrumentation -│ └── telemetry.go -├── go.mod # Dependencies -├── go.sum # Dependency checksums -├── config.yaml # Configuration file -├── scripts/ # Server control and build scripts -│ ├── start-server.sh # Server lifecycle management -│ ├── build.sh # Binary compilation -│ └── test-opentelemetry.sh # OpenTelemetry testing -├── README.md # User documentation -├── AGENTS.md # This file -└── .gitignore # Ignore patterns +│ ├── config/ # Viper-based configuration +│ ├── greet/ # Core domain logic + API handlers +│ ├── server/ # HTTP server, routing, graceful shutdown +│ ├── telemetry/ # OpenTelemetry instrumentation +│ ├── user/ # User domain (auth, JWT, repository) +│ └── validation/ # Request validation +├── scripts/ # Server lifecycle, build, test scripts +├── config.yaml # Configuration file +└── config.example.yaml # Configuration template ``` -## 🎮 CLI Management - -### New Cobra CLI (Recommended) - -dance-lessons-coach now includes a modern CLI built with Cobra framework: +## Server Management ```bash -# Show help and available commands -./bin/dance-lessons-coach --help - -# Show version information -./bin/dance-lessons-coach version - -# Greet someone by name -./bin/dance-lessons-coach greet John - -# Start the server -./bin/dance-lessons-coach server -``` - -**Available Commands:** -- `version` - Print version information -- `server` - Start the dance-lessons-coach server -- `greet [name]` - Greet someone by name -- `help` - Built-in help system -- `completion` - Generate shell completion scripts - -**Server Command Flags:** -- `--config` - Config file path -- `--env` - Environment (dev, staging, prod) -- `--debug` - Enable debug logging - -### Version Information - -The server provides runtime version information: - -```bash -# Check version using new CLI -./bin/dance-lessons-coach version - -# Check version using server binary -./bin/server --version - -# Output: -dance-lessons-coach Version Information: - Version: 1.0.0 - Commit: abc1234 - Built: 2026-04-05T10:00:00+0000 - Go: go1.26.1 -``` - -### Using the Server Control Script - -A convenient shell script is provided for managing the server lifecycle: - -```bash -# Navigate to project directory -cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach - -# Start the server +# Start / stop / restart ./scripts/start-server.sh start +./scripts/start-server.sh stop +./scripts/start-server.sh restart -# Check server status +# Status and logs ./scripts/start-server.sh status - -# Test API endpoints -./scripts/start-server.sh test - -# View server logs ./scripts/start-server.sh logs -# Stop the server -./scripts/start-server.sh stop +# Test all API endpoints +./scripts/start-server.sh test ``` -**Server Control Script Commands:** -- `start` - Start the server in background with proper logging -- `stop` - Stop the server gracefully -- `restart` - Restart the server -- `status` - Check if server is running -- `logs` - Show recent server logs -- `test` - Test all API endpoints +## Configuration -### Manual Server Management +All settings can be provided via `config.yaml` or environment variables (`DLC_` prefix). -If you prefer manual control: - -```bash -# Navigate to project directory -cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach - -# Run server in background using control script -./scripts/start-server.sh start -``` - -**Expected output:** -``` -Server running on :8080 -[INF] Starting HTTP server on :8080 -[TRC] Registering greet routes -[TRC] Greet routes registered -``` - -**Features:** -- Context-aware server initialization -- Graceful shutdown handling -- Signal-based termination (SIGINT, SIGTERM) -- 30-second shutdown timeout -- Proper resource cleanup - -### Configuration Management - -The server supports flexible configuration through environment variables with the `DLC_` prefix: - -**Available Configuration Options:** - -| Option | Environment Variable | Default Value | Description | -|--------|---------------------|---------------|-------------| -| Host | `DLC_SERVER_HOST` | `0.0.0.0` | Server bind address | -| Port | `DLC_SERVER_PORT` | `8080` | Server listening port | -| Shutdown Timeout | `DLC_SHUTDOWN_TIMEOUT` | `30s` | Graceful shutdown timeout | -| JSON Logging | `DLC_LOGGING_JSON` | `false` | Enable JSON format logging | -| Log Output | `DLC_LOGGING_OUTPUT` | `""` | Log output file path (empty for stderr) | - -**Usage Examples:** - -```bash -# Custom port -export DLC_SERVER_PORT=9090 -./scripts/start-server.sh start - -# Custom host and port -export DLC_SERVER_HOST="127.0.0.1" -export DLC_SERVER_PORT=8081 -./scripts/start-server.sh start - -# Custom shutdown timeout -export DLC_SHUTDOWN_TIMEOUT=45s -./scripts/start-server.sh start - -# Enable JSON logging -export DLC_LOGGING_JSON=true -./scripts/start-server.sh start - -# Log to file -export DLC_LOGGING_OUTPUT="server.log" -./scripts/start-server.sh start - -# Combined: JSON logging to file -export DLC_LOGGING_JSON=true -export DLC_LOGGING_OUTPUT="server.json.log" -./scripts/start-server.sh start -``` - -**Configuration File Support:** - -A `config.example.yaml` file is provided as a template. By default, the application looks for `config.yaml` in the current working directory. - -To specify a custom config file path, set the `DLC_CONFIG_FILE` environment variable: - -```bash -DLC_CONFIG_FILE="/path/to/config.yaml" go run ./cmd/server -``` - -Example `config.yaml`: +| Option | Env var | Default | Description | +|--------|---------|---------|-------------| +| Host | `DLC_SERVER_HOST` | `0.0.0.0` | Bind address | +| Port | `DLC_SERVER_PORT` | `8080` | Listening port | +| Shutdown timeout | `DLC_SHUTDOWN_TIMEOUT` | `30s` | Graceful shutdown window | +| JSON logging | `DLC_LOGGING_JSON` | `false` | Structured JSON output | +| Log output | `DLC_LOGGING_OUTPUT` | `""` | File path (empty = stderr) | +| API v2 | `DLC_API_V2_ENABLED` | `false` | Enable `/api/v2` routes | +| Config file | `DLC_CONFIG_FILE` | `./config.yaml` | Override config path | +Minimal `config.yaml`: ```yaml server: host: "0.0.0.0" port: 8080 - shutdown: timeout: 30s - logging: json: false ``` -**Configuration Loading:** -- **File-based configuration** takes highest precedence -- **Environment variables** override defaults but are overridden by config file -- **Default values** are used when no other configuration is provided -- All configuration is validated on startup -- Invalid configurations cause server startup failure -- Configuration values and source are logged at startup +**Priority**: env var > config file > default. -**Verification:** -```bash -# Test with custom configuration -DLC_SERVER_PORT=9090 DLC_SERVER_HOST="127.0.0.1" ./scripts/start-server.sh start +## API Endpoints -# Verify it's running on the custom port -curl http://127.0.0.1:9090/api/health -# Expected: {"status":"healthy"} -``` - -### Checking Server Status +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/health` | Liveness — always `{"status":"healthy"}` | +| GET | `/api/ready` | Readiness — 200 when ready, 503 during shutdown | +| GET | `/api/version` | Version info (`?format=plain\|full\|json`) | +| GET | `/api/v1/greet/` | Default greeting | +| GET | `/api/v1/greet/{name}` | Personalized greeting | +| POST | `/api/v2/greet` | V2 greeting with validation (feature-flagged) | +| GET | `/swagger/` | Swagger UI | +| GET | `/swagger/doc.json` | OpenAPI spec | ```bash -# Check health endpoint -curl -s http://localhost:8080/api/health - -# Check readiness endpoint -curl -s http://localhost:8080/api/ready -``` - -**Expected responses:** -- Health: `{"status":"healthy"}` -- Readiness (normal): `{"ready":true}` -- Readiness (during shutdown): `{"ready":false}` with HTTP 503 - -**Endpoint Differences:** -- **Health endpoint** (`/api/health`): Indicates if the application is running and functional -- **Readiness endpoint** (`/api/ready`): Indicates if the application is ready to accept traffic - -**Use Cases:** -- **Health**: Used by load balancers to check if the app is alive -- **Readiness**: Used by Kubernetes/service meshes to determine if the app can accept new requests - -**During Graceful Shutdown:** -- Health endpoint continues to return `{"status":"healthy"}` -- Readiness endpoint returns `{"ready":false}` with HTTP 503 Service Unavailable -- This allows existing requests to complete while preventing new requests - -### Stopping the Server - -To stop the server gracefully: -```bash -# Send SIGTERM for graceful shutdown -kill -TERM $(lsof -ti :8080) - -# Or send SIGINT (Ctrl+C equivalent) -pkill -INT -f "go run" -``` - -**Graceful shutdown process:** -1. Server receives termination signal -2. Logs shutdown message -3. Stops accepting new connections -4. Waits up to 30 seconds for active requests to complete -5. Closes all connections cleanly -6. Exits with proper cleanup - -For force stop (if graceful shutdown hangs): -```bash -kill -9 $(lsof -ti :8080) -``` - -**Verification:** -```bash -curl -s http://localhost:8080/api/health -# Should return connection refused -``` - -## 🌐 API Endpoints - -### Base URL -``` -http://localhost:8080 -``` - -### OpenAPI Documentation - -**Swagger UI:** `http://localhost:8080/swagger/` -**OpenAPI Spec:** `http://localhost:8080/swagger/doc.json` - -The API provides interactive documentation using Swagger UI with complete OpenAPI 2.0 specification. All endpoints, request/response models, and validation rules are documented using a **hierarchical tagging system**. - -**Features:** -- Interactive API exploration with hierarchical organization -- Try-it-out functionality for all endpoints -- Model schemas with examples -- Response examples with validation rules -- **Hierarchical tag structure** for better navigation - -**Generation:** Documentation is auto-generated from code annotations using [swaggo/swag](https://github.com/swaggo/swag) with the command: - -```bash -go generate ./pkg/server/ -``` - -**Tag Organization:** -- `API/v1/Greeting` - Version 1 greeting endpoints -- `API/v2/Greeting` - Version 2 greeting endpoints -- `System/Health` - Health and readiness endpoints - -**Hierarchical Benefits:** -- Clear separation between API domains (API vs System) -- Version organization within each domain -- Natural hierarchy in Swagger UI -- Scalable for future API growth - -```bash -# Generate documentation -go generate ./pkg/server/ -``` - -**Embedded Documentation:** The OpenAPI spec is embedded in the binary using Go's `//go:embed` directive for single-binary deployment. - ---- - -### Health Check - -### Health Check -```http -GET /api/health -``` - -**Response:** -```json -{"status":"healthy"} -``` - -### Readiness Check -```http -GET /api/ready -``` - -**Responses:** -- Normal operation: `{"ready":true}` (HTTP 200) -- During shutdown: `{"ready":false}` (HTTP 503 Service Unavailable) - -**Purpose:** Indicates whether the server is ready to accept new requests. Returns false during graceful shutdown to allow existing requests to complete while preventing new ones. - -### Greet Service -```http -GET /api/v1/greet/ -GET /api/v1/greet/{name} -``` - -**Examples:** -```bash -# Default greeting -curl http://localhost:8080/api/v1/greet/ -# Response: {"message":"Hello world!"} - -# Personalized greeting -curl http://localhost:8080/api/v1/greet/John -# Response: {"message":"Hello John!"} - -# Another example +curl http://localhost:8080/api/health +curl http://localhost:8080/api/ready curl http://localhost:8080/api/v1/greet/Alice -# Response: {"message":"Hello Alice!"} -``` - -### Greet Service v2 (Feature Flag Enabled) -```http -POST /api/v2/greet -``` - -**Request Body:** -```json -{ - "name": "John" -} -``` - -**Examples:** -```bash -# Valid request curl -X POST http://localhost:8080/api/v2/greet \ - -H "Content-Type: application/json" \ - -d '{"name":"John"}' -# Response: {"message":"Hello my friend John!"} - -# Empty name (valid, returns default) -curl -X POST http://localhost:8080/api/v2/greet \ - -H "Content-Type: application/json" \ - -d '{"name":""}' -# Response: {"message":"Hello my friend!"} - -# Missing name field (valid, returns default) -curl -X POST http://localhost:8080/api/v2/greet \ - -H "Content-Type: application/json" \ - -d '{}' -# Response: {"message":"Hello my friend!"} - -# Name too long (validation error) -curl -X POST http://localhost:8080/api/v2/greet \ - -H "Content-Type: application/json" \ - -d '{"name":"ThisNameIsWayTooLongAndShouldFailValidationBecauseItExceedsTheMaximumAllowedLengthOf100Characters!!!!"}' -# Response: {"error":"validation_failed","message":"Invalid request data","details":[{"message":"Name failed validation for 'max' (parameter: 100)"}]}' + -H "Content-Type: application/json" -d '{"name":"Alice"}' ``` -**Validation Rules:** -- `name`: Maximum length 100 characters (optional field) - -**Feature Flag:** Enable with `DLC_API_V2_ENABLED=true` or in config file with `api.v2_enabled: true` - -## 🔗 OpenTelemetry & Jaeger Integration - -The application supports OpenTelemetry for distributed tracing with Jaeger compatibility. - -### Configuration - -Enable OpenTelemetry in your `config.yaml`: - -```yaml -telemetry: - enabled: true - otlp_endpoint: "localhost:4317" - service_name: "dance-lessons-coach" - insecure: true - sampler: - type: "parentbased_always_on" - ratio: 1.0 -``` - -Or via environment variables: +## Testing ```bash -export DLC_TELEMETRY_ENABLED=true -export DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317" -export DLC_TELEMETRY_SERVICE_NAME="dance-lessons-coach" -export DLC_TELEMETRY_INSECURE=true -export DLC_TELEMETRY_SAMPLER_TYPE="parentbased_always_on" -export DLC_TELEMETRY_SAMPLER_RATIO=1.0 -``` +# Unit + integration tests +go test ./... +go test -v ./... -### Testing with Jaeger +# Graceful shutdown + JSON logging validation +./scripts/test-graceful-shutdown.sh -1. **Start Jaeger in Docker:** -```bash -docker run -d --name jaeger \ - -e COLLECTOR_OTLP_ENABLED=true \ - -p 16686:16686 \ - -p 4317:4317 \ - jaegertracing/all-in-one:latest -``` - -2. **Start the server with OpenTelemetry enabled:** -```bash -# Using config file -./scripts/start-server.sh start - -# Or with environment variables -DLC_TELEMETRY_ENABLED=true ./scripts/start-server.sh start -``` - -3. **Make API requests:** -```bash -curl http://localhost:8080/api/v1/greet/John -``` - -4. **View traces in Jaeger UI:** -Open http://localhost:16686 and select the "dance-lessons-coach" service. - -### Sampler Types - -- `always_on`: Sample all traces -- `always_off`: Sample no traces -- `traceidratio`: Sample based on trace ID ratio -- `parentbased_always_on`: Sample based on parent span (always on) -- `parentbased_always_off`: Sample based on parent span (always off) -- `parentbased_traceidratio`: Sample based on parent span with ratio - -### Testing Script - -Use the provided test script: -```bash +# OpenTelemetry end-to-end ./scripts/test-opentelemetry.sh ``` -This script: -1. Starts Jaeger container -2. Starts the server with OpenTelemetry -3. Makes test API calls -4. Shows Jaeger UI URL -5. Cleans up on exit +**Note:** Do not call `go generate` unless editing API endpoint annotations. +When needed: `go generate ./pkg/server/` -## 🔧 Development Workflow - -### 1. Check Server Status -```bash -curl -s http://localhost:8080/api/health -``` - -### 2. Start Development Server -```bash -cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach -./scripts/start-server.sh start -``` - -### 3. Test API Endpoints -```bash -# Test all endpoints as shown above -curl http://localhost:8080/api/v1/greet/YourName -``` - -### 4. Run Tests -```bash -# Run all tests -go test ./... - -# Run specific package -go test ./pkg/greet/ -``` - -### 5. Build Binaries - -The project uses a build script to compile binaries into the `bin/` directory: +## Build ```bash -# Build both server and greet binaries ./scripts/build.sh - -# This creates: -# - ./bin/server - The web server binary -# - ./bin/greet - The CLI greeting tool -``` - -**Binary Usage:** -```bash -# Run the server -./bin/server - -# Use the greet CLI -./bin/greet # Output: Hello world! -./bin/greet John # Output: Hello John! -``` - -**The `bin/` directory is gitignored** to prevent binary files from being committed to the repository. - -### 6. Make Changes -- Edit source files in `pkg/` or `cmd/` -- Follow existing patterns and interfaces -- Add tests for new functionality - -### 7. Stop and Restart -```bash -./scripts/start-server.sh restart -``` - -## 🧪 Testing - -### Unit Tests -```bash -# Run all tests -go test ./... - -# Run with verbose output -go test -v ./... - -# Run specific test -go test ./pkg/greet/ -run TestService_Greet -``` - -### CLI Testing -```bash -# Default greeting -go run ./cmd/greet -# Output: Hello world! - -# Personalized greeting -go run ./cmd/greet John -# Output: Hello John! -``` - -### API Testing -```bash -# Health check -curl http://localhost:8080/api/health - -# Greet endpoints -curl http://localhost:8080/api/v1/greet/John -curl http://localhost:8080/api/v1/greet/ -``` - -## 📝 Architecture Decisions - -### Interface-Based Design -```go -type Greeter interface { - Greet(ctx context.Context, name string) string -} -``` - -**Benefits:** -- Easy mocking for tests -- Dependency injection -- Multiple implementations -- Clear contracts - -### Context-Aware Services -```go -func (s *Service) Greet(ctx context.Context, name string) string { - log.Trace().Ctx(ctx).Str("name", name).Msg("Greet function called") - // ... -} -``` - -**Benefits:** -- Request tracing -- Cancellation support -- Deadline propagation -- Metadata passing - -### Server Context Management -```go -// Root context with cancellation -ctx, cancel := context.WithCancel(context.Background()) -defer cancel() - -// Server context with graceful shutdown -serverCtx, serverStop := context.WithCancel(ctx) - -// HTTP server with context-aware shutdown -srv := &http.Server{ - Addr: ":8080", - Handler: server.Router(), -} - -// Graceful shutdown with timeout -shutdownCtx, shutdownCancel := context.WithTimeout( - context.Background(), - 30*time.Second -) -defer shutdownCancel() - -if err := srv.Shutdown(shutdownCtx); err != nil { - log.Error().Err(err).Msg("Server shutdown failed") -} -``` - -**Benefits:** -- Graceful shutdown handling -- Signal-based termination (SIGINT, SIGTERM) -- 30-second timeout for active connections -- Proper resource cleanup -- Context propagation throughout server lifecycle - -### Zerolog Logging -```go -zerolog.SetGlobalLevel(zerolog.TraceLevel) -log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) -``` - -**Benefits:** -- High performance -- Structured logging -- Trace level detail -- Color output - -### Versioned API -```go -router.Route("/api/v1", func(r chi.Router) { - // v1 endpoints -}) -``` - -**Benefits:** -- Backward compatibility -- Clear versioning -- Easy migration -- Parallel versions - -## 🔍 Troubleshooting - -### Port Already in Use -```bash -# Find and kill process using port 8080 -kill -TERM $(lsof -ti :8080) -``` - -### Server Not Responding -```bash -# Check if running -curl -s http://localhost:8080/api/health - -# Restart server using control script -./scripts/start-server.sh restart -``` - -### Dependency Issues -```bash -# Clean and rebuild -go mod tidy -go build ./... -``` - -### Tests Failing -```bash -# Run with verbose output -go test -v ./... - -# Check specific test -go test ./pkg/greet/ -run TestName -``` - -## 📚 Code Examples - -### Adding New API Endpoint -```go -// 1. Add to interface -func (h *apiV1GreetHandler) RegisterRoutes(router chi.Router) { - router.Get("/", h.handleGreetQuery) - router.Get("/{name}", h.handleGreetPath) - router.Post("/custom", h.handleCustomGreet) // New endpoint -} - -// 2. Implement handler -func (h *apiV1GreetHandler) handleCustomGreet(w http.ResponseWriter, r *http.Request) { - // Parse request - // Call service - // Return JSON response -} -``` - -### Adding Logging -```go -// Trace level logging -log.Trace().Ctx(ctx).Str("key", "value").Msg("message") - -// Info level -log.Info().Msg("Important event") - -// Error level -log.Error().Err(err).Msg("Error occurred") -``` - -### Using Context -```go -// Pass context through calls -func handler(w http.ResponseWriter, r *http.Request) { - result := service.Greet(r.Context(), "John") - // ... -} - -// Create context with values -ctx := context.WithValue(r.Context(), "key", "value") - -// Create context with timeout -ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) -defer cancel() -``` - -## 🎓 Best Practices - -### Code Organization -- Keep handlers thin, move logic to services -- Use interfaces for dependencies -- Separate route registration from handlers -- Group related functionality - -### Error Handling -- Return proper HTTP status codes -- Log errors with context -- Don't expose internal errors to clients -- Use structured error responses - -### Performance -- Use Zerolog's Trace level sparingly in production -- Avoid allocations in hot paths -- Use context timeouts for external calls -- Batch database operations - -### Testing -- Test interfaces, not implementations -- Use table-driven tests -- Test error cases -- Mock dependencies - -## 📈 Future Enhancements - -### Potential Features -- [ ] Database integration -- [ ] Authentication/Authorization -- [ ] Rate limiting -- [ ] Metrics and monitoring -- [ ] Docker containerization -- ✅ CI/CD pipeline ([ADR-0016](adr/0016-ci-cd-pipeline-design.md), [ADR-0017](adr/0017-trunk-based-development-workflow.md)) -- [ ] Configuration hot reload -- [ ] Circuit breakers - -### Architectural Improvements -- [ ] Request validation middleware -- ✅ OpenAPI/Swagger documentation with embedded spec -- [ ] Enhanced OpenTelemetry instrumentation -- [ ] Metrics collection and visualization -- [ ] Health check improvements -- [ ] Configuration validation enhancements - -### Completed Features -- ✅ Graceful shutdown with readiness endpoint -- ✅ OpenTelemetry integration with Jaeger support -- ✅ Configuration management with Viper -- ✅ Comprehensive logging with Zerolog -- ✅ Build system with binary output -- ✅ Complete documentation with commit conventions -- ✅ Version management with runtime info - -## 📦 Version Management - -dance-lessons-coach uses a comprehensive version management system based on Semantic Versioning 2.0.0. - -### Version Information - -**Current Version:** `1.0.0` (see VERSION file) -**Version Format:** `MAJOR.MINOR.PATCH-PRERELEASE` -**SemVer Compliance:** ✅ Yes - -### Version Files - -```bash -# VERSION file - Source of truth -MAJOR=1 -MINOR=0 -PATCH=0 -PRERELEASE="" - -# Auto-generated fields -BUILD_DATE="" -GIT_COMMIT="" -GIT_TAG="" -``` - -### Version Management Commands - -#### Check Version -```bash -# From VERSION file -source VERSION && echo "$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}" - -# From built binary +# Produces: ./bin/server ./bin/greet ./bin/server --version - -# From running server (future) -curl http://localhost:8080/api/version ``` -#### Bump Version +Build injects version, commit, and date via `-ldflags`. + +## Graceful Shutdown + +On `SIGTERM` / `SIGINT`: +1. Readiness context is cancelled → `/api/ready` returns 503. +2. 1-second propagation window (load balancer drains). +3. `srv.Shutdown()` waits up to `shutdown.timeout` for active requests. +4. Process exits cleanly. + +Health endpoint stays 200 throughout; readiness endpoint goes 503 immediately on signal. + +## OpenTelemetry / Jaeger + +Enable in config or via env: ```bash -# Patch version (bug fixes) -./scripts/version-bump.sh patch # 1.0.0 → 1.0.1 - -# Minor version (new features) -./scripts/version-bump.sh minor # 1.0.1 → 1.1.0 - -# Major version (breaking changes) -./scripts/version-bump.sh major # 1.1.0 → 2.0.0 - -# Pre-release version -./scripts/version-bump.sh pre # 2.0.0 → 2.0.0-alpha.1 - -# Release from pre-release -./scripts/version-bump.sh release # 2.0.0-alpha.1 → 2.0.0 +export DLC_TELEMETRY_ENABLED=true +export DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317" ``` -#### Build with Version +Quick Jaeger setup: ```bash -# Development build -./scripts/build-with-version.sh bin/server-dev - -# Release build -go build -o bin/server \ - -ldflags="\ - -X 'dance-lessons-coach/pkg/version.Version=1.0.0' \ - -X 'dance-lessons-coach/pkg/version.Commit=$(git rev-parse --short HEAD)' \ - -X 'dance-lessons-coach/pkg/version.Date=$(date +%Y-%m-%dT%H:%M:%S%z)' \ - " \ - ./cmd/server +docker run -d --name jaeger \ + -e COLLECTOR_OTLP_ENABLED=true \ + -p 16686:16686 -p 4317:4317 \ + jaegertracing/all-in-one:latest ``` -### Semantic Versioning Rules +## Architecture Decision Records -| Part | When to Increment | Examples | -|------|-------------------|----------| -| **MAJOR** | Breaking changes, major features | Database schema changes, API breaking changes | -| **MINOR** | Backwards-compatible features | New API endpoints, new functionality | -| **PATCH** | Backwards-compatible fixes | Bug fixes, performance improvements | -| **PRERELEASE** | Pre-release versions | alpha.1, beta.2, rc.1 | +| ADR | Decision | +|-----|----------| +| [0001](adr/0001-go-1.26.1-standard.md) | Go 1.26.1 | +| [0002](adr/0002-chi-router.md) | Chi router | +| [0003](adr/0003-zerolog-logging.md) | Zerolog | +| [0004](adr/0004-interface-based-design.md) | Interface-based design | +| [0005](adr/0005-graceful-shutdown.md) | Graceful shutdown | +| [0006](adr/0006-configuration-management.md) | Viper configuration | +| [0007](adr/0007-opentelemetry-integration.md) | OpenTelemetry | +| [0008](adr/0008-bdd-testing.md) | BDD with Godog | +| [0009](adr/0009-hybrid-testing-approach.md) | Hybrid testing strategy | -### Release Lifecycle +Add a new ADR: copy an existing file, edit it, update `adr/README.md`. -#### Development Workflow -```mermaid -graph LR - A[Feature Branch] --> B[PR to main] - B --> C[Auto-build with dev version] - C --> D[Deploy to dev/staging] +## Commit Conventions + +[Conventional Commits](https://www.conventionalcommits.org) with optional [gitmoji](https://gitmoji.dev): + +| Emoji | Type | When | +|-------|------|------| +| ✨ | `feat` | New feature | +| 🐛 | `fix` | Bug fix | +| 📝 | `docs` | Documentation | +| 🎨 | `style` | Formatting only | +| ♻️ | `refactor` | Structural change | +| 🚀 | `perf` | Performance | +| 🔒 | `security` | Security fix | +| 📦 | `chore` | Dependencies / build | +| 🧪 | `test` | Tests | +| 🤖 | `ci` | CI/CD | +| 🔥 | `remove` | Delete code/files | + +Examples: ``` - -#### Release Workflow -```mermaid -graph LR - A[Bump version] --> B[Update CHANGELOG] - B --> C[Create git tag] - C --> D[Build release binaries] - D --> E[Push to GitHub Releases] - E --> F[Deploy to production] +feat: add JWT authentication middleware +fix: ensure first log line is JSON when json logging is enabled +docs: rewrite AGENTS.md for clarity ``` - -### Version Package - -The `pkg/version` package provides runtime access to version information: - -```go -package main - -import ( - "dance-lessons-coach/pkg/version" - "fmt" -) - -func main() { - fmt.Println("Version:", version.Short()) - fmt.Println("Full info:", version.Full()) - fmt.Println("Info:", version.Info()) -} -``` - -**Functions:** -- `version.Short()` - Returns version number (e.g., "1.0.0") -- `version.Info()` - Returns short info string -- `version.Full()` - Returns detailed version information - -### Implementation Status - -| Component | Status | Notes | -|-----------|--------|-------| -| Version Package | ✅ Complete | Runtime version access | -| VERSION File | ✅ Complete | Source of truth | -| Build Script | ✅ Complete | Version injection | -| Version Command | ✅ Complete | `--version` flag | -| Version Bump Script | 🟡 Partial | Basic functionality | -| Git Tag Integration | 🟡 Planned | Release automation | -| CI/CD Integration | ✅ Complete | Pipeline automation with local testing | -| Release Scripts | 🟡 Planned | Full release lifecycle | - -### Future Enhancements - -1. **Automated Changelog Generation** -2. **Git Tag Automation** -3. **CI/CD Pipeline Integration** -4. **Version API Endpoint** (`GET /api/version`) -5. **Dependency Version Tracking** -6. **Security Vulnerability Alerts** - -See [ADR 0014](adr/0014-version-management-lifecycle.md) for complete version management architecture. - -## 📝 Architecture Decision Records - -The project maintains comprehensive Architecture Decision Records (ADRs) that document all major architectural choices. See the [adr/](adr/) directory for complete documentation. - -**Key Decisions**: -- **Language**: Go 1.26.1 ([ADR-0001](adr/0001-go-1.26.1-standard.md)) -- **Routing**: Chi router ([ADR-0002](adr/0002-chi-router.md)) -- **Logging**: Zerolog ([ADR-0003](adr/0003-zerolog-logging.md)) -- **Design**: Interface-based ([ADR-0004](adr/0004-interface-based-design.md)) -- **Shutdown**: Graceful with readiness ([ADR-0005](adr/0005-graceful-shutdown.md)) -- **Config**: Viper ([ADR-0006](adr/0006-configuration-management.md)) -- **Observability**: OpenTelemetry ([ADR-0007](adr/0007-opentelemetry-integration.md)) -- **Testing**: BDD with Godog ([ADR-0008](adr/0008-bdd-testing.md)) -- **CI/CD**: Trunk-based development ([ADR-0017](adr/0017-trunk-based-development-workflow.md)) -- **Testing**: BDD with Godog ([ADR-0008](adr/0008-bdd-testing.md)) -- **Strategy**: Hybrid testing ([ADR-0009](adr/0009-hybrid-testing-approach.md)) - -**Adding New ADRs**: -```bash -# 1. Copy template -cp adr/0001-go-1.26.1-standard.md adr/0010-new-decision.md - -# 2. Edit the new ADR -# 3. Update adr/README.md -# 4. Reference in documentation -``` - -## 📝 Changelog - -### 2026-04-05 - Architecture Documentation -- ✅ Added comprehensive ADR directory with 9 decision records -- ✅ Enhanced Zerolog vs Zap analysis in logging ADR -- ✅ Updated README.md and AGENTS.md with ADR references -- ✅ Documented hybrid testing approach -- ✅ Added BDD testing decision record - -### 2026-04-04 - Observability & Testing -- ✅ OpenTelemetry integration with Jaeger -- ✅ Middleware-only tracing approach -- ✅ Comprehensive telemetry configuration -- ✅ BDD testing framework setup -- ✅ Hybrid testing strategy documentation - -### 2026-04-03 - Production Readiness -- ✅ Graceful shutdown with readiness endpoints -- ✅ Configuration management with Viper -- ✅ JSON logging configuration -- ✅ File output logging support -- ✅ Comprehensive error handling - -### 2026-04-02 - Web API Foundation -- ✅ Chi router integration -- ✅ Versioned API endpoints (`/api/v1`) -- ✅ Health and readiness endpoints -- ✅ JSON responses with proper headers -- ✅ Interface-based design patterns - -### 2026-04-01 - Project Foundation -- ✅ Go 1.26.1 environment setup -- ✅ Project structure with `cmd/` and `pkg/` -- ✅ Core Greet service implementation -- ✅ CLI interface -- ✅ Unit tests with table-driven approach - -## 🤖 AI Agent Information - -**Agent:** Mistral Vibe CLI Agent -**Version:** devstral-2 -**Role:** Development Assistant -**Capabilities:** -- Code generation and refactoring -- Test creation -- Documentation -- Architecture guidance -- Best practices enforcement - -## 📋 Quick Reference - -### Common Commands -```bash -# Start server -./scripts/start-server.sh start - -# Test API -curl http://localhost:8080/api/v1/greet/John - -# Run tests -go test ./... - -# Stop server -pkill -f "go" - -# CLI usage -go run ./cmd/greet John -``` - -### Project Structure -``` -cmd/ # Entry points -pkg/ # Core logic - greet/ # Domain services - server/ # HTTP server -go.mod # Dependencies -README.md # User docs -AGENTS.md # This file -``` - -### Key Interfaces -```go -type Greeter interface { - Greet(ctx context.Context, name string) string -} - -type ApiV1Greet interface { - RegisterRoutes(router chi.Router) -} -``` - -## 📝 Commit Conventions - -**Follow Conventional Commits for clear communication:** - -**Core Types:** -- `feat`: New user-facing feature -- `fix`: Bug fix for users -- `docs`: Documentation changes -- `style`: Code formatting (no functional change) -- `refactor`: Code structure changes -- `perf`: Performance improvements -- `test`: Test additions/corrections -- `chore`: Build process, dependencies, maintenance - -**Examples:** -```bash -# New feature -git commit -m "feat: add user authentication" - -# Bug fix -git commit -m "fix: prevent race condition in cache" - -# Documentation -git commit -m "docs: add API endpoint documentation" - -# Maintenance -git commit -m "chore: update dependencies" - -# Refactoring -git commit -m "refactor: extract user service from controller" -``` - -**Optional Emoji Support:** -- Use [gitmoji](https://gitmoji.dev) for visual commit messages -- Example: `git commit -m "✨ feat: add new API endpoint"` - -**Common Gitmoji Reference:** - -| Emoji | Code | Type | Description | -|-------|------|------|-------------| -| ✨ | `:sparkles:` | feat | New feature | -| 🐛 | `:bug:` | fix | Bug fix | -| 📝 | `:memo:` | docs | Documentation | -| 🎨 | `:art:` | style | Code formatting | -| 🔧 | `:wrench:` | chore | Build/config changes | -| ♻️ | `:recycle:` | refactor | Code refactoring | -| 🚀 | `:rocket:` | perf | Performance improvements | -| 🔒 | `:lock:` | security | Security fixes | -| 📦 | `:package:` | dependencies | Dependency changes | -| 🔥 | `:fire:` | remove | Remove code/files | -| 🐧 | `:penguin:` | linux | Linux-specific changes | -| 🍎 | `:apple:` | macos | macOS-specific changes | -| 🪟 | `:window:` | windows | Windows-specific changes | -| 🤖 | `:robot:` | ci | CI/CD changes | -| 🧪 | `:test_tube:` | test | Tests | -| 📈 | `:chart_with_upwards_trend:` | analytics | Analytics/SEO | -| 🌐 | `:globe_with_meridians:` | i18n | Internationalization | -| ⚡ | `:zap:` | performance | Performance improvements | - -**Benefits:** -- Clear communication of change types -- Better git history readability -- Tool compatibility (agent-tasks generators, etc.) -- Consistent project history -- Visual scanning of commit history - -## 📞 Support - -For issues or questions: -1. Check this documentation -2. Review test cases -3. Examine existing implementations -4. Consult Go and Chi documentation -5. Ask the AI agent for guidance - -This documentation provides a complete guide to developing, testing, and maintaining the dance-lessons-coach project using the established patterns and best practices. -## 📋 BDD Feature Structure - -All user stories and BDD features follow the structure defined in ADR-0019: - - - -See [ADR-0019](adr/0019-bdd-feature-structure.md) for complete details. - -## 🗑️ Retention Policy - -### ADRs -- Review quarterly -- Deprecate unused features with `Status: Deprecated` header -- Remove after 6 months of deprecation - -### Documentation -- Archive completed projects to `archive/` directory -- Remove archived documentation after 12 months - -### Scripts -- Move unused scripts to `scripts/deprecated/` -- Remove deprecated scripts after 6 months - -### Skills -- Move unused skills to `.vibe/skills/deprecated/` -- Remove deprecated skills after 6 months diff --git a/README.md b/README.md index a1f03db..fe9c9f4 100644 --- a/README.md +++ b/README.md @@ -1,421 +1,98 @@ # dance-lessons-coach -[![Build Status](https://gitea.arcodange.fr/arcodange/dance-lessons-coach/actions/workflows/ci-cd.yaml/badge.svg)](https://gitea.arcodange.fr/arcodange/dance-lessons-coach/actions/workflows/ci-cd.yaml/badge.svg) -[![Go Report Card](https://goreportcard.com/badge/github.com/arcodange/dance-lessons-coach)](https://goreportcard.com/report/github.com/arcodange/dance-lessons-coach) +[![Build Status](https://gitea.arcodange.fr/arcodange/dance-lessons-coach/actions/workflows/ci-cd.yaml/badge.svg)](https://gitea.arcodange.fr/arcodange/dance-lessons-coach/actions/workflows/ci-cd.yaml) [![Version](https://img.shields.io/badge/version-1.4.0-blue.svg)](https://gitea.arcodange.fr/arcodange/dance-lessons-coach/releases) [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) -[![BDD Coverage](https://img.shields.io/badge/BDD_Coverage-51.1%%-red?style=flat-square)](https://gitea.arcodange.lab/arcodange/dance-lessons-coach) -[![UNIT Coverage](https://img.shields.io/badge/UNIT_Coverage-8.9%%-red?style=flat-square)](https://gitea.arcodange.lab/arcodange/dance-lessons-coach) -A Go project demonstrating idiomatic package structure, CLI implementation, and JSON API with Chi router. -======= +Go web service demonstrating idiomatic package structure, versioned JSON API, and production-ready features. ## Features -- Greet function with default behavior -- Command-line interface -- JSON API with versioned endpoints -- Chi router integration -- Zerolog for high-performance logging -- Viper for configuration management -- Graceful shutdown with context -- Readiness endpoint for Kubernetes/service mesh integration -- OpenTelemetry integration with Jaeger support -- OpenAPI/Swagger documentation -- Unit tests -- Go 1.26.1 compatible +- Versioned JSON API (`/api/v1`, `/api/v2`) +- Chi router with graceful shutdown +- Zerolog structured logging (console and JSON modes) +- Viper configuration (file + env vars) +- Readiness endpoint for Kubernetes / service mesh +- OpenTelemetry / Jaeger distributed tracing +- OpenAPI / Swagger UI (embedded in binary) +- PostgreSQL user service with JWT auth +- BDD + unit tests -## Installation +## Quick Start ```bash -# Clone the repository git clone https://gitea.arcodange.lab/arcodange/dance-lessons-coach.git cd dance-lessons-coach - -# Build all binaries -./scripts/build.sh - -# Use the new Cobra CLI -./bin/dance-lessons-coach --help - -# Or use the legacy greet CLI -go run ./cmd/greet +./scripts/build.sh # produces ./bin/server and ./bin/greet +./scripts/start-server.sh start ``` -## CI/CD Pipeline - -dance-lessons-coach features an optimized CI/CD pipeline using GitHub Actions with container/services architecture: - -### Key Features -- ✅ **Container-based execution**: All steps run in pre-built Docker cache images -- ✅ **Service-based PostgreSQL**: Automatic database service provisioning -- ✅ **Smart caching**: Dependency-aware cache invalidation -- ✅ **Multi-platform**: Compatible with Gitea, GitHub, and GitLab -- ✅ **Fast execution**: No Docker Compose overhead -- ✅ **Reliable testing**: Full database connectivity with proper environment setup - -### Architecture - -The pipeline uses GitHub Actions' native `container` and `services` directives instead of Docker Compose: - -```yaml -jobs: - ci-pipeline: - container: - image: gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:${{ needs.build-cache.outputs.deps_hash }} - - services: - postgres: - image: postgres:15 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: dance_lessons_coach_bdd_test -``` - -### Benefits - -1. **Performance**: Direct container execution without compose overhead -2. **Reliability**: Service containers managed by GitHub Actions -3. **Simplicity**: Cleaner workflow definition -4. **Portability**: Works across CI platforms -5. **Caching**: Intelligent dependency-based cache rebuilding - -### Workflow Steps - -1. **Build Cache**: Creates Docker image with Go tools and dependencies -2. **CI Pipeline**: Runs tests, builds binaries, and generates documentation -3. **Database Tests**: Connects to PostgreSQL service container -4. **Coverage Reporting**: Updates coverage badges automatically -5. **Artifact Publishing**: Builds and pushes Docker images (main branch only) - -### Environment Configuration - -The pipeline automatically sets up database environment variables: - ```bash -echo "DLC_DATABASE_HOST=postgres" >> $GITHUB_ENV -echo "DLC_DATABASE_PORT=5432" >> $GITHUB_ENV -echo "DLC_DATABASE_USER=postgres" >> $GITHUB_ENV -echo "DLC_DATABASE_PASSWORD=postgres" >> $GITHUB_ENV -echo "DLC_DATABASE_NAME=dance_lessons_coach_bdd_test" >> $GITHUB_ENV -echo "DLC_DATABASE_SSL_MODE=disable" >> $GITHUB_ENV +curl http://localhost:8080/api/health +curl http://localhost:8080/api/v1/greet/Alice ``` -### Status +Stop: `./scripts/start-server.sh stop` -[![Build Status](https://gitea.arcodange.fr/api/badges/arcodange/dance-lessons-coach/status)](https://gitea.arcodange.fr/arcodange/dance-lessons-coach) +## Greet CLI -======= -- ✅ **Linting**: Code quality checks with `go fmt` and `go vet` -- ✅ **Version Management**: Automatic version detection -- ✅ **Portable**: Uses standard GitHub Actions workflow format - -### Workflow File -```yaml -# .github/workflows/main.yml -jobs: - build-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 - with: - go-version: '1.26.1' - - run: go build ./... - - run: go test ./... -cover - - lint-format: - runs-on: ubuntu-latest - steps: - - run: go fmt ./... - - run: go vet ./... +```bash +go run ./cmd/greet # Hello world! +go run ./cmd/greet Alice # Hello Alice! ``` -### Setup Instructions -1. **Gitea**: Enable GitHub Actions compatibility in repo settings -2. **GitHub**: Push to mirror repository (workflow runs automatically) -3. **GitLab**: Convert workflow to `.gitlab-ci.yml` or use compatibility mode - -**See [ADR 0016](adr/0016-ci-cd-pipeline-design.md) for complete CI/CD design and [STATUS_BADGES.md](STATUS_BADGES.md) for badge setup.** - ## Configuration -Basic configuration options: +All options are available via `config.yaml` or `DLC_*` environment variables. -```bash -# Start with default configuration -./scripts/start-server.sh start +| Env var | Default | Description | +|---------|---------|-------------| +| `DLC_SERVER_PORT` | `8080` | Listening port | +| `DLC_SERVER_HOST` | `0.0.0.0` | Bind address | +| `DLC_LOGGING_JSON` | `false` | JSON log format | +| `DLC_LOGGING_OUTPUT` | stderr | Log file path | +| `DLC_SHUTDOWN_TIMEOUT` | `30s` | Graceful shutdown window | +| `DLC_API_V2_ENABLED` | `false` | Enable `/api/v2` routes | +| `DLC_CONFIG_FILE` | `./config.yaml` | Override config path | -# Custom port -export DLC_SERVER_PORT=9090 -./scripts/start-server.sh start +See `config.example.yaml` for a full template. -# JSON logging -export DLC_LOGGING_JSON=true -./scripts/start-server.sh start -``` +## API -**See [AGENTS.md](AGENTS.md#configuration-management) for comprehensive configuration guide including:** -- File-based configuration -- Environment variables -- Configuration priority rules -- OpenTelemetry setup -- Advanced scenarios - -## Usage - -### New Cobra CLI (Recommended) - -```bash -# Show help -./bin/dance-lessons-coach --help - -# Show version -./bin/dance-lessons-coach version - -# Greet someone -./bin/dance-lessons-coach greet John - -# Start server -./bin/dance-lessons-coach server -``` - -### Legacy CLI (Deprecated) - -```bash -# Default greeting -go run ./cmd/greet -# Output: Hello world! - -# Custom greeting -go run ./cmd/greet John -# Output: Hello John! -``` - -### Web Server - -**Using the server control script (recommended):** - -```bash -# Start the server -./scripts/start-server.sh start - -# Test API endpoints -./scripts/start-server.sh test - -# Access OpenAPI documentation -# Swagger UI: http://localhost:8080/swagger/ -# OpenAPI spec: http://localhost:8080/swagger/doc.json - -# Stop the server -./scripts/start-server.sh stop -``` - -**Manual server management:** - -```bash -# Start the server -go run ./cmd/server - -# Test API endpoints -curl http://localhost:8080/api/health -# Output: {"status":"healthy"} - -curl http://localhost:8080/api/ready -# Output: {"ready":true} - -curl http://localhost:8080/api/v1/greet -# Output: {"message":"Hello world!"} - -curl http://localhost:8080/api/v1/greet/John -# Output: {"message":"Hello John!"} -``` +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/health` | Liveness check | +| GET | `/api/ready` | Readiness check (503 during shutdown) | +| GET | `/api/version` | Version info (`?format=plain\|full\|json`) | +| GET | `/api/v1/greet/` | Default greeting | +| GET | `/api/v1/greet/{name}` | Named greeting | +| POST | `/api/v2/greet` | V2 greeting with validation | +| GET | `/swagger/` | Swagger UI | ## Testing ```bash -# Run all tests -go test ./... - -# Run specific package tests -go test ./pkg/greet/ +go test ./... # unit + integration tests +./scripts/test-graceful-shutdown.sh # lifecycle + JSON logging validation +./scripts/test-opentelemetry.sh # tracing end-to-end ``` -## CI/CD +## Gitea Client -dance-lessons-coach includes a comprehensive CI/CD pipeline with multiple testing options: +AI agent helper script at `.vibe/skills/gitea-client/scripts/gitea-client.sh`. -### Local Testing (No Gitea Required) +Auth setup: ```bash -# Validate workflow structure -./scripts/cicd.sh validate - -# Test workflow steps locally -./scripts/cicd.sh test-simple +echo "your_token" > ~/.gitea_token +chmod 600 ~/.gitea_token +export GITEA_API_TOKEN_FILE="$HOME/.gitea_token" ``` -### Gitea Integration -```bash -# Test local setup with Gitea configuration -./scripts/cicd.sh test-local - -# Check pipeline status on Gitea -./scripts/cicd.sh check-status -``` - -### Full CI/CD Testing -```bash -# Test with docker compose (requires Gitea runner) -./scripts/cicd.sh test-docker -``` - -**See [adr/0016-ci-cd-pipeline-design.md](adr/0016-ci-cd-pipeline-design.md) for complete CI/CD architecture.** - -## Project Structure - -``` -dance-lessons-coach/ -├── adr/ # Architecture Decision Records -├── cmd/ # Entry points (greet CLI, server) -├── pkg/ # Core packages (config, greet, server, telemetry) -│ └── server/docs/ # Generated OpenAPI documentation (gitignored) -├── config.yaml # Configuration file -├── scripts/ # Management scripts -└── go.mod # Go module definition -``` - -**See [AGENTS.md](AGENTS.md#project-structure) for detailed structure and component explanations.** -``` - -## Development - -### Generate OpenAPI Documentation - -The project uses [swaggo/swag](https://github.com/swaggo/swag) to generate OpenAPI/Swagger documentation from code annotations: - -```bash -# Generate documentation -go generate ./pkg/server/ - -# This creates: -# - pkg/server/docs/docs.go (swagger template) -# - pkg/server/docs/swagger.json (OpenAPI spec) -# - pkg/server/docs/swagger.yaml (YAML version) -``` - -**Note:** `pkg/server/docs/` is gitignored. Documentation is embedded in the binary at build time. - -### Documentation Annotations - -Add swagger annotations to handlers and models: - -```go -// @Summary Get personalized greeting -// @Description Returns a greeting with the specified name -// @Tags greet -// @Accept json -// @Produce json -// @Param name path string true "Name to greet" -// @Success 200 {object} GreetResponse "Successful response" -// @Failure 400 {object} ErrorResponse "Invalid name parameter" -// @Router /v1/greet/{name} [get] -func (h *apiV1GreetHandler) handleGreetPath(w http.ResponseWriter, r *http.Request) { - // handler implementation -} -``` +Get a token at https://gitea.arcodange.lab → Profile → Settings → Applications. ## Architecture -This project uses Architecture Decision Records (ADRs) to document key technical choices. See [adr/](adr/) for complete documentation including decisions on Go 1.26.1, Chi router, Zerolog, OpenTelemetry, interface-based design, graceful shutdown, configuration management, testing strategies, and OpenAPI documentation. - -**Adding new decisions?** See [adr/README.md](adr/README.md) for guidelines. - -## Gitea Integration - -dance-lessons-coach includes AI agent skills for Gitea integration to monitor CI/CD jobs and interact with pull requests. - -### Gitea Client Skill Setup - -The Gitea client skill enables AI agents to: -- Monitor CI/CD job status -- Fetch job logs for debugging -- Comment on pull requests -- Track PR status - -**Setup Instructions:** - -1. **Create a Personal Access Token:** - - Log in to https://gitea.arcodange.lab - - Go to Profile → Settings → Applications - - Generate token with `read:repository`, `write:repository`, and `read:user` scopes - -2. **Configure Authentication:** - ```bash - # Option 1: Environment variable - export GITEA_API_TOKEN="your_token" - - # Option 2: Token file (recommended) - echo "your_token" > ~/.gitea_token - chmod 600 ~/.gitea_token - export GITEA_API_TOKEN_FILE="$HOME/.gitea_token" - ``` - -3. **Add to shell configuration:** - ```bash - echo 'export GITEA_API_TOKEN_FILE="$HOME/.gitea_token"' >> ~/.bashrc - source ~/.bashrc - ``` - -**Usage Examples:** -```bash -# List recent jobs -.vibe/skills/gitea-client/scripts/gitea-client.sh list-jobs owner repo workflow_id 5 - -# Wait for job completion -.vibe/skills/gitea-client/scripts/gitea-client.sh wait-job owner repo job_id 300 - -# Comment on PR -.vibe/skills/gitea-client/scripts/gitea-client.sh comment-pr owner repo 42 "Build completed!" -``` - -**Documentation:** See [.vibe/skills/gitea-client/README.md](.vibe/skills/gitea-client/README.md) for complete setup and usage guide. - -## 🤖 AI Agent Usage - -### Quick Launch Commands - -**Programmer Agent** (for code implementation, testing, CI/CD): -```bash -vibe start --agent dancelessonscoachprogrammer -``` - -**Product Owner Agent** (for requirements, interviews, documentation): -```bash -vibe start --agent dancelessonscoach-product-owner -``` - -### Full Documentation - -For complete agent usage guide including: -- Agent selection guidance -- Common workflow examples -- Configuration reference -- Best practices -- Troubleshooting tips - -See: [AGENT_USAGE_GUIDE.md](documentation/AGENT_USAGE_GUIDE.md) - -### Gitmoji Cheatsheet - -Quick reference for commit messages: -- **📝 `:memo:` docs** - Documentation -- **✨ `:sparkles:` feat** - New feature -- **🐛 `:bug:` fix** - Bug fix -- **♻️ `:recycle:` refactor** - Code refactoring -- **🔧 `:wrench:` chore** - Build/config changes - -Full cheatsheet: [GITMOJI_CHEATSHEET.md](documentation/GITMOJI_CHEATSHEET.md) +Key decisions are documented in [adr/](adr/). See [AGENTS.md](AGENTS.md) for the full development reference (commands, config, ADR index, commit conventions). ## License -- 2.49.1