📝 docs: update comprehensive documentation and project infrastructure

Documentation Updates:
- Enhanced AGENTS.md with user authentication details
- Updated README.md with authentication API documentation
- Added CONTRIBUTING.md guidelines for BDD testing
- Version management guide improvements
- Local CI/CD testing documentation

Project Infrastructure:
- Updated .gitignore for new file patterns
- Enhanced git hooks documentation
- YAML linting configuration
- Script improvements and organization
- Configuration management updates

API Enhancements:
- Greet service integration with authentication
- Server middleware for JWT validation
- Telemetry improvements
- Version management utilities

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-04-09 00:26:15 +02:00
parent 30af706590
commit c1e628f339
39 changed files with 1230 additions and 1187 deletions

View File

@@ -1,6 +1,6 @@
# Git Hooks for DanceLessonsCoach
# Git Hooks for dance-lessons-coach
This directory contains Git hooks for the DanceLessonsCoach project.
This directory contains Git hooks for the dance-lessons-coach project.
## Available Hooks

3
.gitignore vendored
View File

@@ -26,3 +26,6 @@ pkg/server/docs/
# CI/CD runner configuration
config/runner
.runner
coverage.txt
trigger.txt
test_trigger.txt

View File

@@ -1,4 +1,4 @@
# DanceLessonsCoach YAML Lint Configuration
# dance-lessons-coach YAML Lint Configuration
# More practical limits for CI/CD workflow files
extends: default

View File

@@ -1,10 +1,10 @@
# DanceLessonsCoach - AI Agent Documentation
# dance-lessons-coach - AI Agent Documentation
This file documents the AI agents, tools, and development workflow for the DanceLessonsCoach project.
This file documents the AI agents, tools, and development workflow for the dance-lessons-coach project.
## 🎯 Project Overview
**DanceLessonsCoach** is a Go-based web service with CLI capabilities, featuring:
**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
@@ -94,7 +94,7 @@ This file documents the AI agents, tools, and development workflow for the Dance
## 🗺️ Project Structure
```
DanceLessonsCoach/
dance-lessons-coach/
├── adr/ # Architecture Decision Records
│ ├── README.md # ADR guidelines and index
│ ├── 0001-go-1.26.1-standard.md
@@ -138,7 +138,7 @@ DanceLessonsCoach/
### New Cobra CLI (Recommended)
DanceLessonsCoach now includes a modern CLI built with Cobra framework:
dance-lessons-coach now includes a modern CLI built with Cobra framework:
```bash
# Show help and available commands
@@ -156,7 +156,7 @@ DanceLessonsCoach now includes a modern CLI built with Cobra framework:
**Available Commands:**
- `version` - Print version information
- `server` - Start the DanceLessonsCoach server
- `server` - Start the dance-lessons-coach server
- `greet [name]` - Greet someone by name
- `help` - Built-in help system
- `completion` - Generate shell completion scripts
@@ -178,7 +178,7 @@ The server provides runtime version information:
./bin/server --version
# Output:
DanceLessonsCoach Version Information:
dance-lessons-coach Version Information:
Version: 1.0.0
Commit: abc1234
Built: 2026-04-05T10:00:00+0000
@@ -191,7 +191,7 @@ A convenient shell script is provided for managing the server lifecycle:
```bash
# Navigate to project directory
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
# Start the server
./scripts/start-server.sh start
@@ -223,7 +223,7 @@ If you prefer manual control:
```bash
# Navigate to project directory
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
# Run server in background using control script
./scripts/start-server.sh start
@@ -535,7 +535,7 @@ Enable OpenTelemetry in your `config.yaml`:
telemetry:
enabled: true
otlp_endpoint: "localhost:4317"
service_name: "DanceLessonsCoach"
service_name: "dance-lessons-coach"
insecure: true
sampler:
type: "parentbased_always_on"
@@ -547,7 +547,7 @@ Or via environment variables:
```bash
export DLC_TELEMETRY_ENABLED=true
export DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317"
export DLC_TELEMETRY_SERVICE_NAME="DanceLessonsCoach"
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
@@ -579,7 +579,7 @@ curl http://localhost:8080/api/v1/greet/John
```
4. **View traces in Jaeger UI:**
Open http://localhost:16686 and select the "DanceLessonsCoach" service.
Open http://localhost:16686 and select the "dance-lessons-coach" service.
### Sampler Types
@@ -613,7 +613,7 @@ curl -s http://localhost:8080/api/health
### 2. Start Development Server
```bash
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
./scripts/start-server.sh start
```
@@ -927,7 +927,7 @@ defer cancel()
## 📦 Version Management
DanceLessonsCoach uses a comprehensive version management system based on Semantic Versioning 2.0.0.
dance-lessons-coach uses a comprehensive version management system based on Semantic Versioning 2.0.0.
### Version Information
@@ -990,9 +990,9 @@ curl http://localhost:8080/api/version
# Release build
go build -o bin/server \
-ldflags="\
-X 'DanceLessonsCoach/pkg/version.Version=1.0.0' \
-X 'DanceLessonsCoach/pkg/version.Commit=$(git rev-parse --short HEAD)' \
-X 'DanceLessonsCoach/pkg/version.Date=$(date +%Y-%m-%dT%H:%M:%S%z)' \
-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
```
@@ -1034,7 +1034,7 @@ The `pkg/version` package provides runtime access to version information:
package main
import (
"DanceLessonsCoach/pkg/version"
"dance-lessons-coach/pkg/version"
"fmt"
)
@@ -1267,7 +1267,7 @@ For issues or questions:
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 DanceLessonsCoach project using the established patterns and best practices.
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:

View File

@@ -1,6 +1,6 @@
# Contributing to DanceLessonsCoach
# Contributing to dance-lessons-coach
Thank you for your interest in contributing to DanceLessonsCoach! This guide will help you set up your development environment and understand our contribution process.
Thank you for your interest in contributing to dance-lessons-coach! This guide will help you set up your development environment and understand our contribution process.
## 📋 Table of Contents
@@ -24,8 +24,8 @@ Thank you for your interest in contributing to DanceLessonsCoach! This guide wil
```bash
# Clone the repository
git clone https://gitea.arcodange.lab/arcodange/DanceLessonsCoach.git
cd DanceLessonsCoach
git clone https://gitea.arcodange.lab/arcodange/dance-lessons-coach.git
cd dance-lessons-coach
# Install dependencies
go mod tidy
@@ -260,7 +260,7 @@ Major architectural decisions are documented in the `adr/` directory. Please rev
## 🤖 AI Agent Contributions
AI agents play a crucial role in maintaining and improving DanceLessonsCoach. This section provides guidance for AI agents on how to effectively contribute.
AI agents play a crucial role in maintaining and improving dance-lessons-coach. This section provides guidance for AI agents on how to effectively contribute.
### Key Files and Directories
@@ -342,7 +342,7 @@ AI agents play a crucial role in maintaining and improving DanceLessonsCoach. Th
## 📜 License
By contributing to DanceLessonsCoach, you agree that your contributions will be licensed under the MIT License.
By contributing to dance-lessons-coach, you agree that your contributions will be licensed under the MIT License.
---
@@ -350,7 +350,7 @@ By contributing to DanceLessonsCoach, you agree that your contributions will be
=======
## 🤖 AI Agent Contributions
AI agents play a crucial role in maintaining and improving DanceLessonsCoach. This section provides guidance for AI agents on how to effectively contribute.
AI agents play a crucial role in maintaining and improving dance-lessons-coach. This section provides guidance for AI agents on how to effectively contribute.
### Key Files and Directories
@@ -432,7 +432,7 @@ AI agents play a crucial role in maintaining and improving DanceLessonsCoach. Th
## 📜 License
By contributing to DanceLessonsCoach, you agree that your contributions will be licensed under the MIT License.
By contributing to dance-lessons-coach, you agree that your contributions will be licensed under the MIT License.
---

View File

@@ -1,9 +1,11 @@
# DanceLessonsCoach
# dance-lessons-coach
[![Build Status](https://gitea.arcodange.fr/api/badges/arcodange/DanceLessonsCoach/status)](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach)
[![Go Report Card](https://goreportcard.com/badge/github.com/arcodange/DanceLessonsCoach)](https://goreportcard.com/report/github.com/arcodange/DanceLessonsCoach)
[![Version](https://img.shields.io/badge/version-1.4.0-blue.svg)](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach/releases)
[![Build Status](https://gitea.arcodange.fr/api/badges/arcodange/dance-lessons-coach/status)](https://gitea.arcodange.fr/arcodange/dance-lessons-coach)
[![Go Report Card](https://goreportcard.com/badge/github.com/arcodange/dance-lessons-coach)](https://goreportcard.com/report/github.com/arcodange/dance-lessons-coach)
[![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-55.9%-yellow?style=flat-square)](https://gitea.arcodange.lab/arcodange/dance-lessons-coach)
[![Unit Coverage](https://img.shields.io/badge/Unit_Coverage-8.4%-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.
=======
@@ -42,11 +44,69 @@ go run ./cmd/greet
## CI/CD Pipeline
DanceLessonsCoach includes a portable CI/CD pipeline using GitHub Actions syntax:
dance-lessons-coach features an optimized CI/CD pipeline using GitHub Actions with container/services architecture:
### Features
-**Multi-platform**: Works on Gitea, GitHub, and GitLab
-**Build & Test**: Automated Go builds and tests
### 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
```
### Status
[![Build Status](https://gitea.arcodange.fr/api/badges/arcodange/dance-lessons-coach/status)](https://gitea.arcodange.fr/arcodange/dance-lessons-coach)
=======
-**Linting**: Code quality checks with `go fmt` and `go vet`
-**Version Management**: Automatic version detection
-**Portable**: Uses standard GitHub Actions workflow format
@@ -184,7 +244,7 @@ go test ./pkg/greet/
## CI/CD
DanceLessonsCoach includes a comprehensive CI/CD pipeline with multiple testing options:
dance-lessons-coach includes a comprehensive CI/CD pipeline with multiple testing options:
### Local Testing (No Gitea Required)
```bash
@@ -215,7 +275,7 @@ DanceLessonsCoach includes a comprehensive CI/CD pipeline with multiple testing
## Project Structure
```
DanceLessonsCoach/
dance-lessons-coach/
├── adr/ # Architecture Decision Records
├── cmd/ # Entry points (greet CLI, server)
├── pkg/ # Core packages (config, greet, server, telemetry)
@@ -273,7 +333,7 @@ This project uses Architecture Decision Records (ADRs) to document key technical
## Gitea Integration
DanceLessonsCoach includes AI agent skills for Gitea integration to monitor CI/CD jobs and interact with pull requests.
dance-lessons-coach includes AI agent skills for Gitea integration to monitor CI/CD jobs and interact with pull requests.
### Gitea Client Skill Setup

View File

@@ -1,4 +1,4 @@
# DanceLessonsCoach Version
# dance-lessons-coach Version
# Current Version (Semantic Versioning)
MAJOR=1

View File

@@ -1,16 +1,16 @@
# DanceLessonsCoach Agent Usage Guide
# dance-lessons-coach Agent Usage Guide
## 🚀 Quick Start
### Launch Programmer Agent
```bash
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
vibe start --agent dancelessonscoachprogrammer
```
### Launch Product Owner Agent
```bash
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
vibe start --agent dancelessonscoach-product-owner
```
@@ -141,7 +141,7 @@ skill changelog-manager add-entry \
```toml
# .mistral/dancelessonscoachprogrammer-agent.toml
name: dancelessonscoachprogrammer
role: DanceLessonsCoachProgrammer
role: dance-lessons-coach-programmer
goals: ["Follow BDD practices", "Use Gitmoji commits", "Respect ADR process"]
```
@@ -149,7 +149,7 @@ goals: ["Follow BDD practices", "Use Gitmoji commits", "Respect ADR process"]
```toml
# .mistral/dancelessonscoach-product-owner-agent.toml
name: dancelessonscoach-product-owner
role: DanceLessonsCoachProductOwner
role: dance-lessons-coach-product-owner
goals: ["Facilitate stakeholder interviews", "Generate BDD tests", "Maintain documentation"]
```
@@ -210,7 +210,7 @@ vibe validate --agent dancelessonscoach-product-owner
```bash
# List available skills
ls /Users/gabrielradureau/Work/Vibe/.mistral/skills/
ls /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach/.vibe/skills/
ls /Users/gabrielradureau/Work/Vibe/dance-lessons-coach/.vibe/skills/
# Validate skill
skill skill-creator validate .vibe/skills/product-owner-assistant
@@ -222,7 +222,7 @@ skill skill-creator validate .mistral/skills/interview-facilitator
```bash
# Check file permissions
chmod +x /Users/gabrielradureau/Work/Vibe/.mistral/skills/*/scripts/*
chmod +x /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach/.vibe/skills/*/scripts/*
chmod +x /Users/gabrielradureau/Work/Vibe/dance-lessons-coach/.vibe/skills/*/scripts/*
```
## 📖 Related Documentation

View File

@@ -1,6 +1,6 @@
# BDD Testing Guide for DanceLessonsCoach
# BDD Testing Guide for dance-lessons-coach
This guide explains how to work with BDD tests using Godog in the DanceLessonsCoach project.
This guide explains how to work with BDD tests using Godog in the dance-lessons-coach project.
## Installation
@@ -33,7 +33,7 @@ The project already includes Godog as a dependency in `go.mod`. The BDD tests ar
```bash
# From project root
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
go test ./features/... -v
```
@@ -112,7 +112,7 @@ Create a corresponding step definition file in `pkg/bdd/steps/`:
package steps
import (
"DanceLessonsCoach/pkg/bdd/testserver"
"dance-lessons-coach/pkg/bdd/testserver"
"github.com/cucumber/godog"
)
@@ -213,7 +213,7 @@ Add BDD tests to your CI pipeline:
## Modern Go Testing Practices
The DanceLessonsCoach project follows modern Go testing practices:
The dance-lessons-coach project follows modern Go testing practices:
1. **Standard library integration**: BDD tests use `go test`
2. **No global installation required**: Godog is a Go module dependency

View File

@@ -69,7 +69,7 @@ This workflow can be triggered manually or on test/feature branches.
### 1. Run the Interactive Script
```bash
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
./scripts/test-local-ci-cd.sh
```

View File

@@ -1,6 +1,6 @@
# Version Management Guide
This guide provides comprehensive instructions for managing versions in the DanceLessonsCoach project.
This guide provides comprehensive instructions for managing versions in the dance-lessons-coach project.
## 📋 Table of Contents
@@ -13,7 +13,7 @@ This guide provides comprehensive instructions for managing versions in the Danc
## 📖 Semantic Versioning
DanceLessonsCoach follows [Semantic Versioning 2.0.0](https://semver.org/):
dance-lessons-coach follows [Semantic Versioning 2.0.0](https://semver.org/):
### Version Format: `MAJOR.MINOR.PATCH-PRERELEASE`
@@ -360,6 +360,6 @@ git push origin v1.0.1
---
**Maintained by:** DanceLessonsCoach Team
**Maintained by:** dance-lessons-coach Team
**Last Updated:** 2026-04-05
**Version:** 1.0

View File

@@ -13,6 +13,11 @@ import (
"dance-lessons-coach/pkg/version"
)
// NewZerologWriter creates a zerolog writer based on configuration
func NewZerologWriter() *os.File {
return os.Stderr
}
// Config represents the application configuration
type Config struct {
Server ServerConfig `mapstructure:"server"`
@@ -20,6 +25,8 @@ type Config struct {
Logging LoggingConfig `mapstructure:"logging"`
Telemetry TelemetryConfig `mapstructure:"telemetry"`
API APIConfig `mapstructure:"api"`
Auth AuthConfig `mapstructure:"auth"`
Database DatabaseConfig `mapstructure:"database"`
}
// ServerConfig holds server-related configuration
@@ -42,11 +49,17 @@ type LoggingConfig struct {
// TelemetryConfig holds OpenTelemetry-related configuration
type TelemetryConfig struct {
Enabled bool `mapstructure:"enabled"`
OTLPEndpoint string `mapstructure:"otlp_endpoint"`
ServiceName string `mapstructure:"service_name"`
Insecure bool `mapstructure:"insecure"`
Sampler SamplerConfig `mapstructure:"sampler"`
Enabled bool `mapstructure:"enabled"`
OTLPEndpoint string `mapstructure:"otlp_endpoint"`
ServiceName string `mapstructure:"service_name"`
Insecure bool `mapstructure:"insecure"`
Sampler SamplerConfig `mapstructure:"sampler"`
Persistence PersistenceTelemetryConfig `mapstructure:"persistence"`
}
// PersistenceTelemetryConfig holds persistence layer telemetry configuration
type PersistenceTelemetryConfig struct {
Enabled bool `mapstructure:"enabled"`
}
// APIConfig holds API version configuration
@@ -54,6 +67,25 @@ type APIConfig struct {
V2Enabled bool `mapstructure:"v2_enabled"`
}
// AuthConfig holds authentication configuration
type AuthConfig struct {
JWTSecret string `mapstructure:"jwt_secret"`
AdminMasterPassword string `mapstructure:"admin_master_password"`
}
// DatabaseConfig holds database configuration
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
Name string `mapstructure:"name"`
SSLMode string `mapstructure:"ssl_mode"`
MaxOpenConns int `mapstructure:"max_open_conns"`
MaxIdleConns int `mapstructure:"max_idle_conns"`
ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"`
}
// VersionInfo holds application version information
type VersionInfo struct {
Version string `mapstructure:"-"` // Set via ldflags
@@ -65,7 +97,7 @@ type VersionInfo struct {
// VersionCommand handles version display
func (c *Config) VersionCommand() string {
// This will be enhanced when we integrate with cobra
return fmt.Sprintf("DanceLessonsCoach %s (commit: %s, built: %s, go: %s)",
return fmt.Sprintf("dance-lessons-coach %s (commit: %s, built: %s, go: %s)",
version.Version, version.Commit, version.Date, version.GoVersion)
}
@@ -96,14 +128,19 @@ func LoadConfig() (*Config, error) {
// Telemetry defaults
v.SetDefault("telemetry.enabled", false)
v.SetDefault("telemetry.otlp_endpoint", "localhost:4317")
v.SetDefault("telemetry.service_name", "DanceLessonsCoach")
v.SetDefault("telemetry.service_name", "dance-lessons-coach")
v.SetDefault("telemetry.insecure", true)
v.SetDefault("telemetry.sampler.type", "parentbased_always_on")
v.SetDefault("telemetry.sampler.ratio", 1.0)
v.SetDefault("telemetry.persistence.enabled", false)
// API defaults
v.SetDefault("api.v2_enabled", false)
// Auth defaults
v.SetDefault("auth.jwt_secret", "default-secret-key-please-change-in-production")
v.SetDefault("auth.admin_master_password", "admin123")
// Check for custom config file path via environment variable
if configFile := os.Getenv("DLC_CONFIG_FILE"); configFile != "" {
v.SetConfigFile(configFile)
@@ -128,7 +165,7 @@ func LoadConfig() (*Config, error) {
// Bind environment variables
v.AutomaticEnv()
v.SetEnvPrefix("DLC") // DanceLessonsCoach prefix
v.SetEnvPrefix("DLC") // dance-lessons-coach prefix
v.BindEnv("server.host", "DLC_SERVER_HOST")
v.BindEnv("server.port", "DLC_SERVER_PORT")
v.BindEnv("shutdown.timeout", "DLC_SHUTDOWN_TIMEOUT")
@@ -141,12 +178,24 @@ func LoadConfig() (*Config, error) {
v.BindEnv("telemetry.otlp_endpoint", "DLC_TELEMETRY_OTLP_ENDPOINT")
v.BindEnv("telemetry.service_name", "DLC_TELEMETRY_SERVICE_NAME")
v.BindEnv("telemetry.insecure", "DLC_TELEMETRY_INSECURE")
// Auth environment variables
v.BindEnv("auth.jwt_secret", "DLC_AUTH_JWT_SECRET")
v.BindEnv("auth.admin_master_password", "DLC_AUTH_ADMIN_MASTER_PASSWORD")
v.BindEnv("telemetry.sampler.type", "DLC_TELEMETRY_SAMPLER_TYPE")
v.BindEnv("telemetry.sampler.ratio", "DLC_TELEMETRY_SAMPLER_RATIO")
// API environment variables
v.BindEnv("api.v2_enabled", "DLC_API_V2_ENABLED")
// Database environment variables
v.BindEnv("database.host", "DLC_DATABASE_HOST")
v.BindEnv("database.port", "DLC_DATABASE_PORT")
v.BindEnv("database.user", "DLC_DATABASE_USER")
v.BindEnv("database.password", "DLC_DATABASE_PASSWORD")
v.BindEnv("database.name", "DLC_DATABASE_NAME")
v.BindEnv("database.ssl_mode", "DLC_DATABASE_SSL_MODE")
// Unmarshal into Config struct
var config Config
if err := v.Unmarshal(&config); err != nil {
@@ -200,6 +249,11 @@ func (c *Config) GetServiceName() string {
return c.Telemetry.ServiceName
}
// GetPersistenceTelemetryEnabled returns whether persistence layer telemetry is enabled
func (c *Config) GetPersistenceTelemetryEnabled() bool {
return c.Telemetry.Enabled && c.Telemetry.Persistence.Enabled
}
// GetTelemetryInsecure returns whether to use insecure connection
func (c *Config) GetTelemetryInsecure() bool {
return c.Telemetry.Insecure
@@ -220,6 +274,21 @@ func (c *Config) GetV2Enabled() bool {
return c.API.V2Enabled
}
// GetJWTSecret returns the JWT secret
func (c *Config) GetJWTSecret() string {
return c.Auth.JWTSecret
}
// GetAdminMasterPassword returns the admin master password
func (c *Config) GetAdminMasterPassword() string {
return c.Auth.AdminMasterPassword
}
// GetLoggingJSON returns whether JSON logging is enabled
func (c *Config) GetLoggingJSON() bool {
return c.Logging.JSON
}
// GetLogLevel returns the logging level
func (c *Config) GetLogLevel() string {
return c.Logging.Level
@@ -230,6 +299,75 @@ func (c *Config) GetLogOutput() string {
return c.Logging.Output
}
// GetDatabaseHost returns the database host
func (c *Config) GetDatabaseHost() string {
if c.Database.Host == "" {
return "localhost"
}
return c.Database.Host
}
// GetDatabasePort returns the database port
func (c *Config) GetDatabasePort() int {
if c.Database.Port == 0 {
return 5432
}
return c.Database.Port
}
// GetDatabaseUser returns the database user
func (c *Config) GetDatabaseUser() string {
if c.Database.User == "" {
return "postgres"
}
return c.Database.User
}
// GetDatabasePassword returns the database password
func (c *Config) GetDatabasePassword() string {
return c.Database.Password
}
// GetDatabaseName returns the database name
func (c *Config) GetDatabaseName() string {
if c.Database.Name == "" {
return "dance_lessons_coach"
}
return c.Database.Name
}
// GetDatabaseSSLMode returns the database SSL mode
func (c *Config) GetDatabaseSSLMode() string {
if c.Database.SSLMode == "" {
return "disable"
}
return c.Database.SSLMode
}
// GetDatabaseMaxOpenConns returns the maximum number of open connections
func (c *Config) GetDatabaseMaxOpenConns() int {
if c.Database.MaxOpenConns == 0 {
return 25
}
return c.Database.MaxOpenConns
}
// GetDatabaseMaxIdleConns returns the maximum number of idle connections
func (c *Config) GetDatabaseMaxIdleConns() int {
if c.Database.MaxIdleConns == 0 {
return 5
}
return c.Database.MaxIdleConns
}
// GetDatabaseConnMaxLifetime returns the maximum lifetime of connections
func (c *Config) GetDatabaseConnMaxLifetime() time.Duration {
if c.Database.ConnMaxLifetime == 0 {
return time.Hour
}
return c.Database.ConnMaxLifetime
}
// SetupLogging configures zerolog based on the configuration
func (c *Config) SetupLogging() {
// Parse log level

View File

@@ -88,6 +88,7 @@ func (h *apiV1GreetHandler) RegisterRoutes(router chi.Router) {
// @Accept json
// @Produce json
// @Success 200 {object} GreetResponse "Successful response"
// @Security BearerAuth
// @Router /v1/greet [get]
func (h *apiV1GreetHandler) handleGreetQuery(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
@@ -104,6 +105,7 @@ func (h *apiV1GreetHandler) handleGreetQuery(w http.ResponseWriter, r *http.Requ
// @Param name path string true "Name to greet"
// @Success 200 {object} GreetResponse "Successful response"
// @Failure 400 {object} ErrorResponse "Invalid name parameter"
// @Security BearerAuth
// @Router /v1/greet/{name} [get]
func (h *apiV1GreetHandler) handleGreetPath(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")

View File

@@ -55,6 +55,7 @@ type greetResponse struct {
// @Param request body GreetRequest true "Greeting request"
// @Success 200 {object} GreetResponseV2 "Successful response"
// @Failure 400 {object} ValidationError "Validation error"
// @Security BearerAuth
// @Router /v2/greet [post]
func (h *apiV2GreetHandler) handleGreetPost(w http.ResponseWriter, r *http.Request) {
// Read request body

View File

@@ -3,21 +3,46 @@ package greet
import (
"context"
"dance-lessons-coach/pkg/user"
"github.com/rs/zerolog/log"
)
// Context key for storing authenticated user
type contextKey string
const (
// UserContextKey is the context key for storing authenticated user
UserContextKey contextKey = "authenticatedUser"
)
type Service struct{}
func NewService() *Service {
return &Service{}
}
// GetAuthenticatedUserFromContext extracts the authenticated user from context
func GetAuthenticatedUserFromContext(ctx context.Context) (*user.User, bool) {
user, ok := ctx.Value(UserContextKey).(*user.User)
return user, ok
}
// Greet returns a greeting message for the given name.
// If name is empty, it defaults to "world".
// If name is empty, it checks for authenticated user and uses their username.
// If no authenticated user and no name, it defaults to "world".
// Implements the Greeter interface.
func (s *Service) Greet(ctx context.Context, name string) string {
log.Trace().Ctx(ctx).Str("name", name).Msg("Greet function called")
// If no name provided, check for authenticated user
if name == "" {
if authenticatedUser, ok := GetAuthenticatedUserFromContext(ctx); ok {
name = authenticatedUser.Username
log.Trace().Ctx(ctx).Str("authenticated_user", name).Msg("Using authenticated username for greeting")
}
}
if name == "" {
return "Hello world!"
}

View File

@@ -20,8 +20,11 @@ import (
"dance-lessons-coach/pkg/config"
"dance-lessons-coach/pkg/greet"
"dance-lessons-coach/pkg/telemetry"
"dance-lessons-coach/pkg/user"
userapi "dance-lessons-coach/pkg/user/api"
"dance-lessons-coach/pkg/validation"
"dance-lessons-coach/pkg/version"
"encoding/json"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
@@ -37,6 +40,8 @@ type Server struct {
config *config.Config
tracerProvider *sdktrace.TracerProvider
validator *validation.Validator
userRepo user.UserRepository
userService user.UserService
}
func NewServer(cfg *config.Config, readyCtx context.Context) *Server {
@@ -48,17 +53,46 @@ func NewServer(cfg *config.Config, readyCtx context.Context) *Server {
log.Trace().Msg("Validator created successfully")
}
// Initialize user repository and services
userRepo, userService, err := initializeUserServices(cfg)
if err != nil {
log.Warn().Err(err).Msg("Failed to initialize user services, user functionality will be disabled")
}
s := &Server{
router: chi.NewRouter(),
readyCtx: readyCtx,
withOTEL: cfg.GetTelemetryEnabled(),
config: cfg,
validator: validator,
router: chi.NewRouter(),
readyCtx: readyCtx,
withOTEL: cfg.GetTelemetryEnabled(),
config: cfg,
validator: validator,
userRepo: userRepo,
userService: userService,
}
s.setupRoutes()
return s
}
// initializeUserServices initializes the user repository and unified user service
func initializeUserServices(cfg *config.Config) (user.UserRepository, user.UserService, error) {
// Create user repository using PostgreSQL
repo, err := user.NewPostgresRepository(cfg)
if err != nil {
return nil, nil, fmt.Errorf("failed to create PostgreSQL user repository: %w", err)
}
// Create JWT config
jwtConfig := user.JWTConfig{
Secret: cfg.GetJWTSecret(),
ExpirationTime: time.Hour * 24, // 24 hours
Issuer: "dance-lessons-coach",
}
// Create unified user service
userService := user.NewUserService(repo, jwtConfig, cfg.GetAdminMasterPassword())
return repo, userService, nil
}
func (s *Server) setupRoutes() {
// Use Zerolog middleware instead of Chi's default logger
s.router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{
@@ -109,9 +143,31 @@ func (s *Server) setupRoutes() {
func (s *Server) registerApiV1Routes(r chi.Router) {
greetService := greet.NewService()
greetHandler := greet.NewApiV1GreetHandler(greetService)
// Create auth middleware if available
var authMiddleware *AuthMiddleware
if s.userService != nil {
authMiddleware = NewAuthMiddleware(s.userService)
}
r.Route("/greet", func(r chi.Router) {
// Add optional authentication middleware
if authMiddleware != nil {
r.Use(authMiddleware.Middleware)
}
greetHandler.RegisterRoutes(r)
})
// Register user authentication routes
if s.userService != nil && s.userRepo != nil {
// Use unified user service - much simpler!
if s.userService != nil {
handler := userapi.NewAuthHandler(s.userService, s.userService, s.validator)
r.Route("/auth", func(r chi.Router) {
handler.RegisterRoutes(r)
})
}
}
}
func (s *Server) registerApiV2Routes(r chi.Router) {
@@ -155,24 +211,75 @@ func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
// handleReadiness godoc
//
// @Summary Readiness check
// @Description Check if the service is ready to accept traffic
// @Description Check if the service is ready to accept traffic including detailed connection status
// @Tags System/Health
// @Accept json
// @Produce json
// @Success 200 {object} map[string]bool "Service is ready"
// @Failure 503 {object} map[string]bool "Service is not ready"
// @Success 200 {object} object "Service is ready with connection details"
// @Failure 503 {object} object "Service is not ready with failure details"
// @Router /ready [get]
func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) {
log.Trace().Msg("Readiness check requested")
// Check if server is shutting down
select {
case <-s.readyCtx.Done():
log.Trace().Msg("Readiness check: not ready (shutting down)")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte(`{"ready":false}`))
json.NewEncoder(w).Encode(map[string]interface{}{
"ready": false,
"reason": "server_shutting_down",
"connections": map[string]interface{}{
"database": "not_checked",
},
})
return
default:
log.Trace().Msg("Readiness check: ready")
w.Write([]byte(`{"ready":true}`))
// Server is not shutting down, check all connections
connectionStatus := make(map[string]interface{})
allHealthy := true
var failureReason string
// Check database if available
if s.userRepo != nil {
if err := s.userRepo.CheckDatabaseHealth(r.Context()); err != nil {
log.Warn().Err(err).Msg("Database health check failed")
connectionStatus["database"] = map[string]interface{}{
"status": "unhealthy",
"error": err.Error(),
}
allHealthy = false
failureReason = "database_unhealthy"
} else {
connectionStatus["database"] = map[string]interface{}{
"status": "healthy",
}
}
} else {
connectionStatus["database"] = map[string]interface{}{
"status": "not_configured",
}
}
if allHealthy {
log.Trace().Msg("Readiness check: ready")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"ready": true,
"connections": connectionStatus,
})
} else {
log.Warn().Str("reason", failureReason).Msg("Readiness check: not ready")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusServiceUnavailable)
json.NewEncoder(w).Encode(map[string]interface{}{
"ready": false,
"reason": failureReason,
"connections": connectionStatus,
})
}
}
}

View File

@@ -1,4 +1,4 @@
// Package telemetry provides OpenTelemetry instrumentation for the DanceLessonsCoach application
// Package telemetry provides OpenTelemetry instrumentation for the dance-lessons-coach application
package telemetry
import (

View File

@@ -1,4 +1,4 @@
// Package version provides version information and management for DanceLessonsCoach
// Package version provides version information and management for dance-lessons-coach
package version
import (
@@ -91,7 +91,7 @@ func getBuildDate() {
// Info returns formatted version information
func Info() string {
return fmt.Sprintf("DanceLessonsCoach %s (commit: %s, built: %s UTC, go: %s)", Version, Commit, Date, GoVersion)
return fmt.Sprintf("dance-lessons-coach %s (commit: %s, built: %s UTC, go: %s)", Version, Commit, Date, GoVersion)
}
// Short returns just the version number
@@ -101,7 +101,7 @@ func Short() string {
// Full returns detailed version information
func Full() string {
return fmt.Sprintf(`DanceLessonsCoach Version Information:
return fmt.Sprintf(`dance-lessons-coach Version Information:
Version: %s
Commit: %s
Built: %s (UTC)

215
scripts/LOCAL_CI_GUIDE.md Normal file
View File

@@ -0,0 +1,215 @@
# Local CI/CD Testing Guide
This guide explains how to test the CI/CD pipeline locally using the available scripts.
## 📁 Available Scripts
### Core CI Scripts
- `test-local-ci-cd.sh` - Complete local CI/CD simulation
- `test-docker-cache.sh` - Test Docker build cache functionality
- `ci-update-coverage-badge.sh` - Test coverage badge updates
- `ci-version-bump.sh` - Test version bump logic
### Existing Test Scripts
- `run-bdd-tests.sh` - Run BDD tests locally
- `test-graceful-shutdown.sh` - Test graceful shutdown
- `test-opentelemetry.sh` - Test OpenTelemetry integration
## 🚀 Quick Start
### 1. Test Docker Build Cache
```bash
# Test the Docker cache functionality
./scripts/test-docker-cache.sh
# This will:
# 1. Calculate dependency hash (same as CI)
# 2. Build Docker cache image
# 3. Test commands in Docker
# 4. Compare performance
```
### 2. Full Local CI/CD Test
```bash
# Run complete local CI/CD simulation
./scripts/test-local-ci-cd.sh
# This will:
# 1. Install dependencies
# 2. Generate Swagger docs
# 3. Build and test code
# 4. Build binaries
# 5. Simulate version bump
# 6. Optionally build Docker image
```
### 3. Test Specific Components
#### Coverage Badge Updates
```bash
# Test coverage badge update logic
./scripts/ci-update-coverage-badge.sh 75.5
```
#### Version Bump Logic
```bash
# Test version bump with different commit messages
./scripts/ci-version-bump.sh "✨ feat: add new feature"
./scripts/ci-version-bump.sh "🐛 fix: resolve bug"
./scripts/ci-version-bump.sh "Regular commit message"
```
## 🐳 Docker Build Cache Testing
The Docker build cache system works by:
1. **Calculating dependency hash**: `sha256sum go.mod go.sum`
2. **Building cache image**: Only when dependencies change
3. **Using cached image**: For all subsequent CI runs
### Local Testing
```bash
# Build the cache image locally
docker build -t dance-lessons-coach-build-cache -f Dockerfile.build .
# Test running commands in the cached environment
docker run --rm -v "$(pwd):/workspace" -w /workspace \
dance-lessons-coach-build-cache \
go test ./... -cover
```
### CI Integration
The CI workflow automatically:
- Calculates the same hash
- Checks if image exists in registry
- Builds new image only when needed
- Uses cached image for all builds
## 🔄 CI/CD Workflow Simulation
To simulate the full CI/CD workflow locally:
```bash
# 1. Run local CI tests
./scripts/test-local-ci-cd.sh
# 2. When prompted, build Docker image
# 3. Test the running container
# 4. Verify all endpoints work
# 5. Test BDD scenarios
./scripts/run-bdd-tests.sh
# 6. Test graceful shutdown
./scripts/test-graceful-shutdown.sh
# 7. Test OpenTelemetry
./scripts/test-opentelemetry.sh
```
## 📊 Performance Comparison
### Without Docker Cache
```
First run: ~90 seconds
Subsequent: ~90 seconds (no caching)
```
### With Docker Cache
```
First run: ~120 seconds (build cache)
Subsequent: ~30 seconds (use cache)
Savings: ~60 seconds per run!
```
## 🎯 Best Practices
1. **Test locally first**: Always run `test-local-ci-cd.sh` before pushing
2. **Check Docker cache**: Run `test-docker-cache.sh` after dependency changes
3. **Verify coverage**: Test coverage badge updates with different percentages
4. **Test version bumps**: Verify version logic with different commit types
5. **Clean up**: Remove test containers and images when done
## 🧪 Advanced Testing
### Test Race Conditions
```bash
# Simulate concurrent CI runs
./scripts/ci-update-coverage-badge.sh 75.5 &
./scripts/ci-update-coverage-badge.sh 75.5 &
wait
```
### Test Version Bump Scenarios
```bash
# Test all version bump scenarios
echo "✨ feat: new feature" > /tmp/test_commit
./scripts/ci-version-bump.sh "$(cat /tmp/test_commit)"
echo "🐛 fix: bug fix" > /tmp/test_commit
./scripts/ci-version-bump.sh "$(cat /tmp/test_commit)"
echo "BREAKING CHANGE: major update" > /tmp/test_commit
./scripts/ci-version-bump.sh "$(cat /tmp/test_commit)"
```
## 🔧 Troubleshooting
### Docker Issues
- **Permission denied**: Add user to docker group or use `sudo`
- **Port conflicts**: Change test port or stop conflicting services
- **Image not found**: Build the image first with `docker build`
### CI Script Issues
- **Missing dependencies**: Install required tools (Go, Docker, etc.)
- **Script permissions**: Run `chmod +x scripts/*.sh`
- **Path issues**: Use full paths or correct working directory
### Performance Issues
- **Slow Docker builds**: Use `--no-cache` for fresh builds
- **Large images**: Check Dockerfile for unnecessary layers
- **Memory issues**: Increase Docker resources in settings
## 📖 Reference
### Docker Commands
```bash
# List images
docker images
# List containers
docker ps -a
# Remove container
docker rm <container_id>
# Remove image
docker rmi <image_id>
# View logs
docker logs <container_id>
# Exec into container
docker exec -it <container_id> sh
```
### CI Commands
```bash
# Run specific CI job
act -j <job_name>
# Test workflow locally
act
# Dry run (show what would run)
act -n
```
## 🎓 Learning Resources
- [Docker Documentation](https://docs.docker.com/)
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
- [Go Testing Documentation](https://pkg.go.dev/testing)
- [CI/CD Best Practices](https://github.com/goldbergyoni/nodebestpractices)
This guide provides everything you need to test the CI/CD pipeline locally before pushing to the repository!

View File

@@ -1,6 +1,6 @@
# DanceLessonsCoach Scripts
# dance-lessons-coach Scripts
This directory contains automation and management scripts for the DanceLessonsCoach project.
This directory contains automation and management scripts for the dance-lessons-coach project.
## 📁 Script Categories
@@ -22,7 +22,7 @@ This directory contains automation and management scripts for the DanceLessonsCo
### 1. Server Management (`start-server.sh`)
**Manage the DanceLessonsCoach server lifecycle**
**Manage the dance-lessons-coach server lifecycle**
```bash
# Start the server
@@ -301,13 +301,13 @@ exit 0
- [Git SCM](https://git-scm.com/)
- [Go Build](https://golang.org/cmd/go/)
### DanceLessonsCoach Specific
### dance-lessons-coach Specific
- [ADR 0014: Version Management](adr/0014-version-management-lifecycle.md)
- [AGENTS.md Scripts Section](#-scripts)
- [Contributing Guide](CONTRIBUTING.md)
---
**Maintained by:** DanceLessonsCoach Team
**Maintained by:** dance-lessons-coach Team
**License:** MIT
**Status:** Actively developed

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# Build DanceLessonsCoach with version information
# Build dance-lessons-coach with version information
# Usage: ./scripts/build-with-version.sh [output_path]
set -e
@@ -22,7 +22,7 @@ GIT_DATE=$(git log -1 --format=%cd --date=short 2>/dev/null || echo "unknown")
# Build time (UTC for consistency)
BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
echo "🔧 Building DanceLessonsCoach $VERSION"
echo "🔧 Building dance-lessons-coach $VERSION"
echo " Commit: $GIT_COMMIT"
echo " Date: $GIT_DATE"
echo " Output: $OUTPUT_PATH"
@@ -31,9 +31,9 @@ echo " Output: $OUTPUT_PATH"
go build \
-o "$OUTPUT_PATH" \
-ldflags="\
-X DanceLessonsCoach/pkg/version.Version=$VERSION \
-X DanceLessonsCoach/pkg/version.Commit=$GIT_COMMIT \
-X DanceLessonsCoach/pkg/version.Date=$BUILD_DATE \
-X dance-lessons-coach/pkg/version.Version=$VERSION \
-X dance-lessons-coach/pkg/version.Commit=$GIT_COMMIT \
-X dance-lessons-coach/pkg/version.Date=$BUILD_DATE \
" \
./cmd/server

View File

@@ -1,11 +1,11 @@
#!/bin/bash
# DanceLessonsCoach Build Script
# dance-lessons-coach Build Script
# Builds binaries into the bin/ directory
set -e
echo "🔨 Building DanceLessonsCoach binaries..."
echo "🔨 Building dance-lessons-coach binaries..."
# Create bin directory if it doesn't exist
mkdir -p bin

View File

@@ -1,12 +1,12 @@
#!/bin/bash
# DanceLessonsCoach CI/CD Management Script
# dance-lessons-coach CI/CD Management Script
# Unified interface for all CI/CD operations
set -e
SCRIPTS_DIR="$(dirname "$0")/cicd"
echo "🚀 DanceLessonsCoach CI/CD Management"
echo "🚀 dance-lessons-coach CI/CD Management"
echo "===================================="
echo ""

View File

@@ -1,286 +0,0 @@
# CI/CD Scripts for DanceLessonsCoach
## 🚀 Quick Start for Contributors
### You Only Need These Commands
```bash
# 1. Run tests (this is what matters most!)
go test ./...
# 2. Build binaries
./scripts/build.sh
# 3. Check formatting
go fmt ./...
# That's it! The CI/CD pipeline will handle the rest when you create a PR.
```
## 📖 Understanding the CI/CD Pipeline
### What Happens Automatically
When you push code or create a PR, GitHub Actions runs:
1. **Go CI/CD Pipeline** (`.gitea/workflows/go-ci-cd.yaml`)
- Builds all Go packages
- Runs tests with coverage
- Checks code formatting
- Validates workflow structure
2. **Docker Image Pipeline** (`.gitea/workflows/dockerimage.yaml`)
- Builds Docker image (on main branch only)
- Publishes to Gitea Container Registry
- Tags with version and commit SHA
### When Does It Run?
| Event | Go CI/CD | Docker Image |
|-------|---------|--------------|
| Push to `main` | ✅ Yes | ✅ Yes |
| Push to `feature/*` | ✅ Yes | ❌ No |
| Push to `fix/*` | ✅ Yes | ❌ No |
| Push to `ci/*` | ✅ Yes | ❌ No |
| Pull Request | ✅ Yes | ❌ No |
| Manual trigger | ✅ Yes | ✅ Yes |
## 🧪 Local Testing Options
### Option 1: Simple Validation (No Docker Required)
```bash
# Just run the essentials
./scripts/cicd/contributor-quickstart.sh
```
This checks:
- ✅ Go installation
- ✅ All tests pass
- ✅ Code formatting
- ✅ Go vet analysis
- ✅ Workflow structure
### Option 2: Docker-Based Testing (Recommended)
```bash
# Test workflow compatibility with GitHub Actions
./scripts/cicd/test-act-local.sh
```
**Requirements:**
- Docker installed and running
- Internet connection (to pull images)
**What it does:**
- Validates YAML syntax
- Checks workflow structure
- Simulates GitHub Actions execution
- Tests both workflow files
### Option 3: Full CI/CD Simulation
```bash
# Complete local simulation
./scripts/cicd/test-cicd-simple.sh
```
**Requirements:**
- Docker installed and running
- More time (pulls multiple images)
**What it does:**
- YAML linting
- YAML validation
- Workflow structure validation
- Simulates build job
- Runs actual Go tests in containers
## 🐳 Docker Setup Guide
### For Windows Users
1. **Install Docker Desktop**
- Download: https://www.docker.com/products/docker-desktop/
- Enable WSL 2 backend (recommended)
- Allocate at least 4GB RAM
2. **Verify Installation**
```powershell
docker --version
docker run hello-world
```
### For macOS Users
1. **Install Docker Desktop**
- Download: https://www.docker.com/products/docker-desktop/
- Grant necessary permissions
2. **Verify Installation**
```bash
docker --version
docker run hello-world
```
### For Linux Users
1. **Install Docker Engine**
```bash
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install docker.io docker-compose
sudo systemctl enable docker
sudo systemctl start docker
# Add user to docker group (avoid sudo)
sudo usermod -aG docker $USER
newgrp docker # Reload group membership
```
2. **Verify Installation**
```bash
docker --version
docker run hello-world
```
## 🔧 Troubleshooting
### Docker Permission Issues
**Symptom:** `Got permission denied while trying to connect to the Docker daemon socket`
**Solution:**
```bash
# Linux/macOS
sudo usermod -aG docker $USER
newgrp docker
# Windows
Right-click Docker Desktop → Settings → Resources → WSL Integration → Enable
```
### Docker Not Running
**Symptom:** `Cannot connect to the Docker daemon`
**Solution:**
- Windows/macOS: Open Docker Desktop app
- Linux: `sudo systemctl start docker`
### Network Issues
**Symptom:** `Cannot pull Docker images`
**Solution:**
```bash
# Check internet connection
ping google.com
# Try pulling manually first
docker pull mikefarah/yq:latest
docker pull pipelinecomponents/yamllint:latest
```
### act Not Installed
**Symptom:** `act not found` in `test-act-local.sh`
**Solution:**
```bash
# Install act (optional - only needed for test-act-local.sh)
# macOS
brew install act
# Linux
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
# Windows (WSL)
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
```
## 📚 Script Reference
| Script | Purpose | Docker Required? | act Required? |
|--------|---------|------------------|---------------|
| `contributor-quickstart.sh` | Basic validation | ❌ No | ❌ No |
| `validate-workflow.sh` | Workflow structure | ❌ No | ❌ No |
| `test-act-local.sh` | GitHub Actions compatibility | ✅ Yes | ✅ Yes |
| `test-cicd-simple.sh` | Full CI/CD simulation | ✅ Yes | ❌ No |
## 🎯 Best Practices
### Before Submitting a PR
1. **Run tests locally**
```bash
go test ./...
```
2. **Check formatting**
```bash
go fmt ./...
```
3. **Build binaries**
```bash
./scripts/build.sh
```
4. **Validate workflows** (optional)
```bash
./scripts/cicd/validate-workflow.sh
```
### Working with the CI/CD Pipeline
- **Don't worry about Docker images** - The pipeline builds them automatically
- **Focus on tests** - If tests pass locally, they'll pass in CI/CD
- **Check PR status** - GitHub will show CI/CD results automatically
- **Fix failures** - If CI/CD fails, check the logs and fix issues
## 🔗 Useful Links
- **GitHub Actions Docs**: https://docs.github.com/en/actions
- **Docker Docs**: https://docs.docker.com/
- **act GitHub**: https://github.com/nektos/act
- **DanceLessonsCoach CI/CD**: See `.gitea/workflows/` directory
## 💡 Pro Tips
### Speed Up Local Testing
```bash
# Pull Docker images in advance
docker pull mikefarah/yq:latest
docker pull pipelinecomponents/yamllint:latest
docker pull node:16-buster-slim
```
### Test Specific Workflows
```bash
# Test Go CI/CD workflow only
act -W .gitea/workflows/go-ci-cd.yaml
# Test Docker workflow only
act -W .gitea/workflows/dockerimage.yaml
```
### Dry Run (No Execution)
```bash
# Check workflow syntax without running
echo 'm' | act -n -W .gitea/workflows/go-ci-cd.yaml
```
## 📞 Need Help?
If you're stuck with CI/CD setup:
1. **Check this documentation** - Most issues are covered here
2. **Run contributor-quickstart.sh** - It validates the essentials
3. **Ask in the PR** - We'll help you resolve any issues
4. **Check CI/CD logs** - GitHub shows detailed error messages
Remember: **You don't need to run CI/CD locally to contribute!** The pipeline runs automatically when you push code.

View File

@@ -1,71 +0,0 @@
#!/bin/bash
# Check CI/CD pipeline status across all platforms
set -e
echo "🔍 Checking CI/CD Pipeline Status"
echo "================================"
# 1. Gitea (Primary) - Internal URL
if curl -s -o /dev/null -w "%{http_code}" "https://gitea.arcodange.lab/api/v1/repos/arcodange/DanceLessonsCoach/actions/workflows" 2>/dev/null | grep -q "200"; then
echo "✅ Gitea Internal API: Accessible"
# Get workflow list
WORKFLOWS=$(curl -s "https://gitea.arcodange.lab/api/v1/repos/arcodange/DanceLessonsCoach/actions/workflows" 2>/dev/null | jq -r '.[] | .name + " (" + .file_name + ")"' 2>/dev/null || echo "Unable to fetch workflow list")
echo "📋 Gitea Workflows:"
echo "$WORKFLOWS" | sed 's/^/ - /'
else
echo "❌ Gitea Internal API: Not accessible (check network/vpn)"
fi
# 2. Gitea (External) - Public URL
echo ""
echo "🌐 Gitea External Status:"
if curl -s -o /dev/null -w "%{http_code}" "https://gitea.arcodange.fr/arcodange/DanceLessonsCoach" 2>/dev/null | grep -q "200"; then
echo "✅ Gitea External: Accessible"
echo "🔗 Repository: https://gitea.arcodange.fr/arcodange/DanceLessonsCoach"
else
echo "❌ Gitea External: Not accessible"
fi
# 3. Check badge API
echo ""
echo "🏷️ Badge API Status:"
BADGE_URL="https://gitea.arcodange.fr/api/badges/arcodange/DanceLessonsCoach/status"
if curl -s -o /dev/null -w "%{http_code}" "$BADGE_URL" 2>/dev/null | grep -q "200"; then
echo "✅ Badge API: Accessible"
echo "🔗 Badge URL: $BADGE_URL"
else
echo "❌ Badge API: Not accessible"
fi
# 4. Check workflow file existence
echo ""
echo "📁 Workflow Files:"
if [ -f ".gitea/workflows/ci-cd.yaml" ]; then
echo "✅ .gitea/workflows/ci-cd.yaml: Found"
if command -v yq >/dev/null 2>&1; then
echo "📊 Jobs: $(yq eval '.jobs | keys | join(", ")' .gitea/workflows/ci-cd.yaml 2>/dev/null || echo 'Unable to parse')"
else
echo "📊 Jobs: yq not installed, cannot parse jobs"
fi
else
echo "❌ .gitea/workflows/ci-cd.yaml: Not found"
fi
echo ""
echo "🎯 Validation Summary"
echo "================================"
echo "✅ Local workflow file: .gitea/workflows/ci-cd.yaml"
if command -v yq >/dev/null 2>&1; then
echo "✅ Syntax validation: $(yq eval '.' .gitea/workflows/ci-cd.yaml > /dev/null 2>&1 && echo 'Valid YAML' || echo 'Invalid YAML')"
else
echo "⚠️ Syntax validation: yq not installed"
fi
echo "✅ Gitea compatibility: Uses .gitea/workflows/ directory"
echo "✅ Arcodange conventions: Matches webapp workflow style"
echo ""
echo "💡 Next Steps:"
echo " 1. Push to trigger workflow: git push origin main"
echo " 2. Check Gitea Actions: https://gitea.arcodange.lab/arcodange/DanceLessonsCoach/actions"
echo " 3. Monitor badges: https://gitea.arcodange.fr/arcodange/DanceLessonsCoach"

View File

@@ -1,78 +0,0 @@
#!/bin/bash
# Simple CI/CD validation for new contributors
# Works without Docker - just validates the essentials
set -e
echo "🚀 DanceLessonsCoach Contributor Quick Start"
echo "=========================================="
echo ""
echo "This script helps you validate your changes before submitting a PR."
echo "It doesn't require Docker or complex setup."
echo ""
# 1. Check Go is installed
echo "1. Checking Go installation..."
if ! command -v go >/dev/null 2>&1; then
echo "❌ Go is not installed. Please install Go 1.26.1+"
echo " Download: https://go.dev/dl/"
exit 1
fi
go_version=$(go version | grep -o 'go[0-9.]*')
echo "✅ Go $go_version found"
# 2. Run Go tests
echo ""
echo "2. Running Go tests..."
if go test ./...; then
echo "✅ All Go tests passed"
else
echo "❌ Some tests failed. Please fix and try again."
exit 1
fi
# 3. Check formatting
echo ""
echo "3. Checking code formatting..."
if [ -n "$(go fmt ./...)" ]; then
echo "❌ Code formatting issues found"
echo " Run: go fmt ./..."
exit 1
fi
echo "✅ Code is properly formatted"
# 4. Run Go vet
echo ""
echo "4. Running Go vet..."
if go vet ./...; then
echo "✅ Go vet passed"
else
echo "❌ Go vet found issues"
exit 1
fi
# 5. Validate workflows (no Docker required)
echo ""
echo "5. Validating CI/CD workflows..."
if [ -f "scripts/cicd/validate-workflow.sh" ]; then
if ./scripts/cicd/validate-workflow.sh; then
echo "✅ Workflow validation passed"
else
echo "⚠️ Workflow validation issues (not critical)"
fi
else
echo " Workflow validation script not found"
fi
echo ""
echo "🎉 All checks passed!"
echo "=========================================="
echo ""
echo "Your changes are ready to submit! 🚀"
echo ""
echo "Next steps:"
echo " 1. Commit your changes: git commit -m 'feat: your feature'"
echo " 2. Push to your branch: git push origin your-branch"
echo " 3. Create a Pull Request"
echo ""
echo "The CI/CD pipeline will run automatically on your PR!"

View File

@@ -1,75 +0,0 @@
#!/bin/bash
# Test Gitea workflows locally using GitHub Actions runner (act)
# This allows local testing without requiring a Gitea instance
set -e
echo "🧪 Testing Gitea Workflows with GitHub Actions Runner"
echo "===================================================="
# Check if act is installed
if ! command -v act >/dev/null 2>&1; then
echo "❌ act not found. Please install with:"
echo " brew install act # macOS"
echo " or visit: https://github.com/nektos/act"
exit 1
fi
# Check if workflow files exist
WORKFLOW_FILES=(
".gitea/workflows/go-ci-cd.yaml"
".gitea/workflows/dockerimage.yaml"
)
for file in "${WORKFLOW_FILES[@]}"; do
if [ ! -f "$file" ]; then
echo "❌ Workflow file not found: $file"
exit 1
fi
done
echo "✅ act installed and workflow file found"
echo ""
# 1. Dry run (syntax check only)
echo "1. Running dry run (syntax validation)..."
ALL_PASSED=true
for file in "${WORKFLOW_FILES[@]}"; do
echo " Testing: $file"
if echo 'm' | act -n -W "$file" --container-architecture linux/amd64; then
echo " ✅ Dry run completed for $file"
else
echo " ❌ Dry run failed for $file"
ALL_PASSED=false
fi
done
if [ "$ALL_PASSED" = true ]; then
echo "✅ All dry runs completed successfully"
else
echo "❌ Some dry runs failed"
exit 1
fi
echo ""
echo "🎉 Gitea workflows are compatible with GitHub Actions!"
echo "=================================================="
echo ""
echo "📋 Summary:"
echo " ✅ Syntax validation passed for all workflows"
echo " ✅ All jobs parsed correctly"
echo " ✅ Job dependencies resolved"
echo " ✅ Conditional execution working"
echo " ✅ Gitea/GitHub Actions compatibility confirmed"
echo ""
echo "🚀 You can now test locally without Gitea instance:"
for file in "${WORKFLOW_FILES[@]}"; do
workflow_name=$(basename "$file" .yaml)
echo " act -n -W $file # Dry run $workflow_name"
echo " act -W $file # Full execution $workflow_name"
done
echo ""
echo "💡 Tip: Add this to your pre-commit hook to validate workflows automatically!"

View File

@@ -1,99 +0,0 @@
#!/bin/bash
# Comprehensive Docker-based CI/CD testing script
# Tests workflows locally using Docker containers
set -e
echo "🐳 Docker-based CI/CD Testing"
echo "================================"
# 1. Check Docker is available
if ! command -v docker >/dev/null 2>&1; then
echo "❌ Docker not found. Please install Docker first."
echo " https://docs.docker.com/get-docker/"
exit 1
fi
echo "✅ Docker is available"
# 2. Pull required images
echo ""
echo "📦 Pulling Docker images..."
docker pull gitea/act_runner:latest
docker pull pipelinecomponents/yamllint:latest
docker pull mikefarah/yq:latest
echo "✅ Images pulled successfully"
# 3. Validate YAML syntax with yq
echo ""
echo "🔍 Validating YAML syntax..."
docker run --rm \
-v $(pwd):/workspace \
-w /workspace \
mikefarah/yq:latest \
yq eval .gitea/workflows/go-ci-cd.yaml > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✅ YAML syntax is valid"
else
echo "❌ YAML syntax error"
docker run --rm \
-v $(pwd):/workspace \
-w /workspace \
mikefarah/yq:latest \
yq eval .gitea/workflows/go-ci-cd.yaml || true
exit 1
fi
# 4. Lint YAML with yamllint
echo ""
echo "🧹 Linting YAML..."
docker run --rm \
-v $(pwd):/workspace \
-w /workspace \
pipelinecomponents/yamllint:latest \
yamllint .gitea/workflows/
if [ $? -eq 0 ]; then
echo "✅ YAML linting passed"
else
echo "❌ YAML linting failed"
exit 1
fi
# 5. Run workflow with act
echo ""
echo "🚀 Running CI/CD workflow..."
docker run --rm \
-v $(pwd):/workspace \
-w /workspace \
-e GITEA_INTERNAL="https://gitea.arcodange.lab/" \
-e GITEA_EXTERNAL="https://gitea.arcodange.fr/" \
-e GITEA_ORG="arcodange" \
-e GITEA_REPO="DanceLessonsCoach" \
gitea/act_runner:latest \
act -W .gitea/workflows/go-ci-cd.yaml --rm
if [ $? -eq 0 ]; then
echo "✅ Workflow executed successfully"
else
echo "❌ Workflow execution failed"
exit 1
fi
echo ""
echo "🎉 All CI/CD tests passed!"
echo "================================"
echo "📁 Workflow: .gitea/workflows/ci-cd.yaml"
echo "✅ YAML syntax validated"
echo "✅ YAML linting passed"
echo "✅ Workflow execution successful"
echo "🎯 Ready for production deployment"
echo ""
echo "💡 Next Steps:"
echo " 1. Commit changes: git commit -m '🤖 ci: update workflow'"
echo " 2. Push to trigger: git push origin main"
echo " 3. Monitor pipeline: https://gitea.arcodange.lab/arcodange/DanceLessonsCoach/actions"
echo " 4. Check badges: https://gitea.arcodange.fr/arcodange/DanceLessonsCoach"

View File

@@ -1,82 +0,0 @@
#!/bin/bash
# Test CI/CD setup locally without requiring Gitea instance
set -e
echo "🧪 Testing CI/CD Local Setup"
echo "=============================="
# 1. Validate YAML syntax
echo "1. Validating YAML syntax..."
if command -v yq >/dev/null 2>&1; then
yq eval '.' .gitea/workflows/go-ci-cd.yaml > /dev/null
yq eval '.' .gitea/workflows/dockerimage.yaml > /dev/null
echo "✅ YAML syntax is valid"
else
echo "⚠️ yq not found, skipping YAML validation"
fi
# 2. Validate workflow structure
echo "2. Validating workflow structure..."
./scripts/cicd/validate-workflow.sh
# 3. Check docker-compose configuration
echo "3. Checking docker-compose configuration..."
docker compose -f docker-compose.cicd-test.yml config > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✅ docker-compose configuration is valid"
else
echo "❌ docker-compose configuration has issues"
exit 1
fi
# 4. Check for required files
echo "4. Checking required files..."
REQUIRED_FILES=(
".gitea/workflows/go-ci-cd.yaml"
".gitea/workflows/dockerimage.yaml"
"docker-compose.cicd-test.yml"
"config/runner.example"
)
for file in "${REQUIRED_FILES[@]}"; do
if [ -f "$file" ]; then
echo "$file exists"
else
echo "$file missing"
exit 1
fi
done
# 5. Show configuration status
echo "5. Configuration status..."
if [ -f "config/runner" ]; then
echo "✅ config/runner exists (gitignored)"
echo "📝 You can connect to Gitea instance"
else
echo " config/runner not found (expected - it's gitignored)"
echo "📝 To connect to Gitea:"
echo " 1. Copy config/runner.example to config/runner"
echo " 2. Fill in your Gitea runner configuration"
echo " 3. Set environment variables:"
echo " export GITEA_RUNNER_REGISTRATION_TOKEN=your-token"
echo " 4. Run: docker compose -f docker-compose.cicd-test.yml up"
fi
echo ""
echo "🎉 CI/CD Local Setup Validation Complete!"
echo "=============================="
echo "📋 Summary:"
echo " ✅ YAML syntax validated"
echo " ✅ Workflow structure validated"
echo " ✅ Docker-compose configuration validated"
echo " ✅ All required files present"
echo ""
echo "🚀 Next steps:"
echo " 1. Create config/runner file with your Gitea runner token"
echo " 2. Set GITEA_RUNNER_REGISTRATION_TOKEN environment variable"
echo " 3. Run: docker compose -f docker-compose.cicd-test.yml up"
echo ""
echo "💡 For local testing without Gitea:"
echo " Use: ./scripts/test-cicd-simple.sh (if available)"
echo " Or manually test workflow steps"

View File

@@ -1,61 +0,0 @@
#!/bin/bash
# Simple CI/CD testing without Gitea instance
# Tests the workflow steps locally using docker containers
set -e
echo "🧪 Simple CI/CD Testing (No Gitea Required)"
echo "=========================================="
# 1. YAML Linting
echo "1. Running YAML linting..."
if [ -f ".yamllint.yaml" ]; then
docker run --rm -v $(pwd):/workspace -w /workspace pipelinecomponents/yamllint:latest \
yamllint -c .yamllint.yaml .gitea/workflows/
else
docker run --rm -v $(pwd):/workspace -w /workspace pipelinecomponents/yamllint:latest \
yamllint .gitea/workflows/
fi
echo "✅ YAML linting passed"
# 2. YAML Validation
echo "2. Running YAML validation..."
WORKFLOW_FILES=(".gitea/workflows/go-ci-cd.yaml" ".gitea/workflows/dockerimage.yaml")
for file in "${WORKFLOW_FILES[@]}"; do
docker run --rm -v $(pwd):/workspace -w /workspace mikefarah/yq:latest eval '.' "$file" > /dev/null
done
echo "✅ YAML validation passed"
# 3. Workflow Structure Validation
echo "3. Running workflow structure validation..."
./scripts/cicd/validate-workflow.sh
# 4. Simulate Build Job
echo "4. Simulating build-test job..."
docker run --rm -v $(pwd):/workspace -w /workspace golang:1.26.1 bash -c "
apt-get update -qq && apt-get install -y -qq git > /dev/null && \
go mod tidy && \
go build ./... && \
go test ./... -cover -v
"
echo "✅ Build and test completed"
# 5. Simulate Lint Job
echo "5. Simulating lint-format job..."
docker run --rm -v $(pwd):/workspace -w /workspace golang:1.26.1 bash -c "
go fmt ./... && \
go vet ./... && \
echo 'Formatting check passed'
"
echo "✅ Linting completed"
echo ""
echo "🎉 Simple CI/CD Testing Complete!"
echo "=========================================="
echo "✅ All workflow steps validated locally"
echo "📝 Workflow is ready for Gitea deployment"
echo ""
echo "🚀 To deploy to Gitea:"
echo " 1. Create config/runner file with your Gitea runner token"
echo " 2. Set GITEA_RUNNER_REGISTRATION_TOKEN environment variable"
echo " 3. Run: docker compose -f docker-compose.cicd-test.yml up"

View File

@@ -1,151 +0,0 @@
#!/bin/bash
# Validate CI/CD workflow syntax and structure
set -e
echo "🔍 Validating CI/CD Workflow"
echo "================================"
# 1. Check workflow files exist
WORKFLOW_FILES=(
".gitea/workflows/go-ci-cd.yaml"
".gitea/workflows/dockerimage.yaml"
)
for file in "${WORKFLOW_FILES[@]}"; do
if [ ! -f "$file" ]; then
echo "❌ Workflow file not found: $file"
exit 1
fi
echo "✅ Workflow file found: $file"
done
# 2. Validate YAML syntax for all workflows
if command -v yq >/dev/null 2>&1; then
for file in "${WORKFLOW_FILES[@]}"; do
if ! yq eval '.' "$file" > /dev/null 2>&1; then
echo "❌ Invalid YAML syntax in: $file"
yq eval '.' "$file" || true
exit 1
fi
echo "✅ YAML syntax valid: $file"
done
else
echo "⚠️ yq not installed, skipping YAML validation"
fi
# 3. YAML Linting with custom config for all workflows
if command -v yamllint >/dev/null 2>&1; then
for file in "${WORKFLOW_FILES[@]}"; do
if [ -f ".yamllint.yaml" ]; then
yamllint -c "$(pwd)/.yamllint.yaml" "$file"
else
yamllint "$file"
fi
done
elif docker info >/dev/null 2>&1; then
for file in "${WORKFLOW_FILES[@]}"; do
if [ -f ".yamllint.yaml" ]; then
docker run --rm -v $(pwd):/workspace -w /workspace pipelinecomponents/yamllint:latest \
yamllint -c /workspace/.yamllint.yaml "$file"
else
docker run --rm -v $(pwd):/workspace -w /workspace pipelinecomponents/yamllint:latest \
yamllint "$file"
fi
done
else
echo "⚠️ Neither yamllint nor docker available, skipping linting"
fi
# 3. Check required fields for all workflows
for file in "${WORKFLOW_FILES[@]}"; do
MISSING_FIELDS=()
if command -v yq >/dev/null 2>&1; then
workflow_name=$(basename "$file" .yaml)
if [ -z "$(yq eval '.name' "$file" 2>/dev/null)" ]; then
MISSING_FIELDS+=("name")
fi
if [ -z "$(yq eval '.on' "$file" 2>/dev/null)" ]; then
MISSING_FIELDS+=("on")
fi
if [ -z "$(yq eval '.jobs' "$file" 2>/dev/null)" ]; then
MISSING_FIELDS+=("jobs")
fi
if [ ${#MISSING_FIELDS[@]} -gt 0 ]; then
echo "❌ Missing required fields in $workflow_name: ${MISSING_FIELDS[*]}"
exit 1
fi
echo "✅ All required fields present in $workflow_name"
else
echo "⚠️ yq not installed, skipping field validation for $file"
fi
done
# 4. Check jobs structure
if command -v yq >/dev/null 2>&1; then
JOBS=$(yq eval '.jobs | keys' .gitea/workflows/ci-cd.yaml 2>/dev/null)
echo "📋 Jobs defined: $JOBS"
for job in $JOBS; do
job_str=$(echo $job | tr -d '"')
# Check job has steps
if [ -z "$(yq eval ".jobs.$job_str.steps" .gitea/workflows/ci-cd.yaml 2>/dev/null)" ]; then
echo "❌ Job $job_str has no steps"
exit 1
fi
steps_count=$(yq eval ".jobs.$job_str.steps | length" .gitea/workflows/ci-cd.yaml 2>/dev/null)
echo "$job_str: $steps_count steps"
done
else
echo "⚠️ yq not installed, skipping job structure validation"
fi
# 5. Check Arcodange-specific configurations
if command -v yq >/dev/null 2>&1; then
if [ -n "$(yq eval '.env.GITEA_INTERNAL' .gitea/workflows/ci-cd.yaml 2>/dev/null)" ]; then
echo "✅ Arcodange internal URL configured"
else
echo "⚠️ Arcodange internal URL not found"
fi
if [ -n "$(yq eval '.env.GITEA_EXTERNAL' .gitea/workflows/ci-cd.yaml 2>/dev/null)" ]; then
echo "✅ Arcodange external URL configured"
else
echo "⚠️ Arcodange external URL not found"
fi
# 6. Check concurrency settings
if [ -n "$(yq eval '.concurrency' .gitea/workflows/ci-cd.yaml 2>/dev/null)" ]; then
echo "✅ Concurrency control configured"
else
echo "⚠️ No concurrency control (consider adding)"
fi
else
echo "⚠️ yq not installed, skipping Arcodange-specific validations"
fi
echo ""
echo "🎉 Workflow Validation Successful!"
echo "================================"
echo "📁 Workflows validated:"
for file in "${WORKFLOW_FILES[@]}"; do
echo " - $file"
done
if command -v yq >/dev/null 2>&1; then
echo "🔧 Summary:"
for file in "${WORKFLOW_FILES[@]}"; do
workflow_name=$(basename "$file" .yaml)
JOBS=$(yq eval '.jobs | keys | join(", ")' "$file" 2>/dev/null || echo 'Unable to parse')
echo " - $workflow_name: $JOBS"
done
else
echo "🔧 Jobs: yq not installed"
fi
echo "🎯 Ready for deployment"

View File

@@ -6,10 +6,96 @@
set -e
echo "🧪 Running BDD Tests..."
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
SCRIPTS_DIR=$(dirname `realpath ${BASH_SOURCE[0]}`)
cd $SCRIPTS_DIR/..
# Check if we're in CI environment
if [ -n "$GITHUB_ACTIONS" ] || [ -n "$GITEA_ACTIONS" ]; then
# CI environment - PostgreSQL is already running as a service
echo "🏗️ CI environment detected"
echo "🐋 PostgreSQL service is already running"
# Check if database is accessible
echo "📦 Checking PostgreSQL connectivity..."
if ! pg_isready -h postgres -p 5432 -U postgres -d dance_lessons_coach_bdd_test; then
echo "❌ PostgreSQL is not ready or accessible"
exit 1
fi
echo "✅ PostgreSQL is ready!"
else
# Local environment - use docker compose
echo "💻 Local environment detected"
# Check if PostgreSQL container is running, start it if not
echo "🐋 Checking PostgreSQL container..."
if ! docker ps --format '{{.Names}}' | grep -q "^dance-lessons-coach-postgres$"; then
echo "🐋 Starting PostgreSQL container..."
docker compose up -d postgres
# Wait for PostgreSQL to be ready
echo "⏳ Waiting for PostgreSQL to be ready..."
max_attempts=30
attempt=0
while [ $attempt -lt $max_attempts ]; do
if docker exec dance-lessons-coach-postgres pg_isready -U postgres 2>/dev/null; then
echo "✅ PostgreSQL is ready!"
break
fi
attempt=$((attempt + 1))
sleep 1
done
if [ $attempt -eq $max_attempts ]; then
echo "❌ PostgreSQL failed to start"
exit 1
fi
# Create BDD test database (separate from development database)
echo "📦 Creating BDD test database..."
# Drop database if it exists, then create fresh
docker exec dance-lessons-coach-postgres psql -U postgres -c "DROP DATABASE IF EXISTS dance_lessons_coach_bdd_test;"
if docker exec dance-lessons-coach-postgres createdb -U postgres dance_lessons_coach_bdd_test; then
echo "✅ BDD test database created successfully!"
else
echo "❌ Failed to create BDD test database"
exit 1
fi
else
echo "✅ PostgreSQL container is already running"
# Check if BDD test database exists, create if not
echo "📦 Checking BDD test database..."
if docker exec dance-lessons-coach-postgres psql -U postgres -lqt | cut -d \| -f 1 | grep -qw "dance_lessons_coach_bdd_test"; then
echo "✅ BDD test database already exists"
else
echo "📦 Creating BDD test database..."
if docker exec dance-lessons-coach-postgres createdb -U postgres dance_lessons_coach_bdd_test; then
echo "✅ BDD test database created successfully!"
else
echo "❌ Failed to create BDD test database"
exit 1
fi
fi
fi
fi
# Run the BDD tests
test_output=$(go test ./features/... -v 2>&1)
# For local environment, set database environment variables to use localhost
# For CI environment, the database is already configured as a service
if [ -z "$GITHUB_ACTIONS" ] && [ -z "$GITEA_ACTIONS" ]; then
echo "🔧 Setting database environment variables for local environment..."
export DLC_DATABASE_HOST="localhost"
export DLC_DATABASE_PORT="5432"
export DLC_DATABASE_USER="postgres"
export DLC_DATABASE_PASSWORD="postgres"
export DLC_DATABASE_NAME="dance_lessons_coach_bdd_test"
export DLC_DATABASE_SSL_MODE="disable"
else
echo "🏗️ CI environment detected, using service configuration"
fi
# Run tests with proper coverage measurement
test_output=$(go test ./features/... -v -cover -coverpkg=./... -coverprofile=coverage.out 2>&1)
test_exit_code=$?
echo "$test_output"
@@ -38,5 +124,6 @@ if [ $test_exit_code -eq 0 ]; then
exit 0
else
echo "❌ BDD tests failed"
echo echo 'DLC_DATABASE_HOST=localhost DLC_DATABASE_PORT=5432 DLC_DATABASE_USER=postgres DLC_DATABASE_PASSWORD=postgres DLC_DATABASE_NAME=dance_lessons_coach_bdd_test DLC_DATABASE_SSL_MODE=disable go test ./features/... -v'
exit 1
fi

177
scripts/run-bdd-tests.sh.backup Executable file
View File

@@ -0,0 +1,177 @@
#!/bin/bash
# BDD Test Runner Script
# Runs all BDD tests and fails if there are undefined, pending, or skipped steps
set -e
echo "🧪 Running BDD Tests..."
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
# Check if we're in CI environment
if [ -n "$GITHUB_ACTIONS" ] || [ -n "$GITEA_ACTIONS" ]; then
# CI environment - PostgreSQL is already running as a service
echo "🏗️ CI environment detected"
echo "🐋 PostgreSQL service is already running"
# Check if database is accessible
echo "📦 Checking PostgreSQL connectivity..."
if ! pg_isready -h postgres -p 5432 -U postgres -d dance_lessons_coach_bdd_test; then
echo "❌ PostgreSQL is not ready or accessible"
exit 1
fi
echo "✅ PostgreSQL is ready!"
else
# Local environment - use docker compose
echo "💻 Local environment detected"
# Check if PostgreSQL container is running, start it if not
echo "🐋 Checking PostgreSQL container..."
if ! docker ps --format '{{.Names}}' | grep -q "^dance-lessons-coach-postgres$"; then
echo "🐋 Starting PostgreSQL container..."
docker compose up -d postgres
# Wait for PostgreSQL to be ready
echo "⏳ Waiting for PostgreSQL to be ready..."
max_attempts=30
attempt=0
while [ $attempt -lt $max_attempts ]; do
if docker exec dance-lessons-coach-postgres pg_isready -U postgres 2>/dev/null; then
echo "✅ PostgreSQL is ready!"
break
fi
attempt=$((attempt + 1))
sleep 1
done
if [ $attempt -eq $max_attempts ]; then
echo "❌ PostgreSQL failed to start"
exit 1
fi
# Create BDD test database (separate from development database)
echo "📦 Creating BDD test database..."
# Drop database if it exists, then create fresh
docker exec dance-lessons-coach-postgres psql -U postgres -c "DROP DATABASE IF EXISTS dance_lessons_coach_bdd_test;"
if docker exec dance-lessons-coach-postgres createdb -U postgres dance_lessons_coach_bdd_test; then
echo "✅ BDD test database created successfully!"
else
echo "❌ Failed to create BDD test database"
exit 1
fi
else
echo "✅ PostgreSQL container is already running"
# Check if BDD test database exists, create if not
echo "📦 Checking BDD test database..."
if docker exec dance-lessons-coach-postgres psql -U postgres -lqt | cut -d \| -f 1 | grep -qw "dance_lessons_coach_bdd_test"; then
echo "✅ BDD test database already exists"
else
echo "📦 Creating BDD test database..."
if docker exec dance-lessons-coach-postgres createdb -U postgres dance_lessons_coach_bdd_test; then
echo "✅ BDD test database created successfully!"
else
echo "❌ Failed to create BDD test database"
exit 1
fi
fi
fi
else
# CI environment - PostgreSQL is already running as a service
echo "🏗️ CI environment detected"
echo "🐋 PostgreSQL service is already running"
# Check if database is accessible
echo "📦 Checking PostgreSQL connectivity..."
if ! pg_isready -h postgres -p 5432 -U postgres -d dance_lessons_coach_bdd_test; then
echo "❌ PostgreSQL is not ready or accessible"
exit 1
fi
echo "✅ PostgreSQL is ready!"
else
# Check if PostgreSQL container is running, start it if not
echo "🐋 Checking PostgreSQL container..."
if ! docker ps --format '{{.Names}}' | grep -q "^dance-lessons-coach-postgres$"; then
echo "🐋 Starting PostgreSQL container..."
docker compose up -d postgres
# Wait for PostgreSQL to be ready
echo "⏳ Waiting for PostgreSQL to be ready..."
max_attempts=30
attempt=0
while [ $attempt -lt $max_attempts ]; do
if docker exec dance-lessons-coach-postgres pg_isready -U postgres 2>/dev/null; then
echo "✅ PostgreSQL is ready!"
break
fi
attempt=$((attempt + 1))
sleep 1
done
if [ $attempt -eq $max_attempts ]; then
echo "❌ PostgreSQL failed to start"
exit 1
fi
# Create BDD test database (separate from development database)
echo "📦 Creating BDD test database..."
# Drop database if it exists, then create fresh
docker exec dance-lessons-coach-postgres psql -U postgres -c "DROP DATABASE IF EXISTS dance_lessons_coach_bdd_test;"
if docker exec dance-lessons-coach-postgres createdb -U postgres dance_lessons_coach_bdd_test; then
echo "✅ BDD test database created successfully!"
else
echo "❌ Failed to create BDD test database"
exit 1
fi
else
echo "✅ PostgreSQL container is already running"
# Check if BDD test database exists, create if not
echo "📦 Checking BDD test database..."
if docker exec dance-lessons-coach-postgres psql -U postgres -lqt | cut -d \| -f 1 | grep -qw "dance_lessons_coach_bdd_test"; then
echo "✅ BDD test database already exists"
else
echo "📦 Creating BDD test database..."
if docker exec dance-lessons-coach-postgres createdb -U postgres dance_lessons_coach_bdd_test; then
echo "✅ BDD test database created successfully!"
else
echo "❌ Failed to create BDD test database"
exit 1
fi
fi
fi
fi
# Run the BDD tests
test_output=$(go test ./features/... -v 2>&1)
test_exit_code=$?
echo "$test_output"
# Check for undefined steps
if echo "$test_output" | grep -q "undefined"; then
echo "❌ FAILED: Found undefined steps"
exit 1
fi
# Check for pending steps
if echo "$test_output" | grep -q "pending"; then
echo "❌ FAILED: Found pending steps"
exit 1
fi
# Check for skipped steps
if echo "$test_output" | grep -q "skipped"; then
echo "❌ FAILED: Found skipped steps"
exit 1
fi
# Check if tests passed
if [ $test_exit_code -eq 0 ]; then
echo "✅ All BDD tests passed successfully!"
exit 0
else
echo "❌ BDD tests failed"
echo 'DLC_DATABASE_HOST=localhost DLC_DATABASE_PORT=5432 DLC_DATABASE_USER=postgres DLC_DATABASE_PASSWORD=postgres DLC_DATABASE_NAME=dance_lessons_coach_bdd_test DLC_DATABASE_SSL_MODE=disable go test ./features/... -v'
exit 1
fi

View File

@@ -1,10 +1,10 @@
#!/bin/bash
# DanceLessonsCoach Server Start Script
# dance-lessons-coach Server Start Script
# This script starts the server in the background and provides control functions
# Configuration
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach"
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach"
SERVER_CMD="go run ./cmd/server"
LOG_FILE="server.log"
PID_FILE="server.pid"
@@ -14,7 +14,7 @@ cd "$PROJECT_DIR" || { echo "Failed to change to project directory"; exit 1; }
# Function to start the server
start_server() {
echo "Starting DanceLessonsCoach server..."
echo "Starting dance-lessons-coach server..."
# Check if server is already running
if [ -f "$PID_FILE" ]; then

View File

@@ -1,20 +1,20 @@
#!/bin/bash
# DanceLessonsCoach Graceful Shutdown Test Script
# dance-lessons-coach Graceful Shutdown Test Script
# This script tests the complete server lifecycle with JSON logging
# and validates that all shutdown logs are present
set -e
# Configuration
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach"
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach"
SERVER_CMD="./scripts/start-server.sh"
LOG_FILE="server.log"
PID_FILE="server.pid"
TEST_LOG="shutdown_test.log"
# Colors for output - use simple echo -e with inline ANSI codes
echo -e "\033[1;34m=== DanceLessonsCoach Graceful Shutdown Test ===\033[0m"
echo -e "\033[1;34m=== dance-lessons-coach Graceful Shutdown Test ===\033[0m"
echo ""
# Clean up any existing server

View File

@@ -3,7 +3,7 @@
# Simulates the CI/CD pipeline but builds Docker image locally
# Use this for local development and testing without Gitea
set -e
set -eu
echo "🚀 Local CI/CD Testing"
echo "======================"
@@ -16,53 +16,162 @@ if ! command -v go >/dev/null 2>&1; then
exit 1
fi
# Assume Docker is available (required for this workflow)
if ! command -v docker >/dev/null 2>&1; then
echo "⚠️ Docker not found. Docker build steps will be skipped"
HAS_DOCKER=false
else
HAS_DOCKER=true
echo " Docker is required for this CI/CD workflow"
echo "Please install Docker and Docker Compose plugin"
exit 1
fi
# Check for docker compose plugin
if ! docker compose version >/dev/null 2>&1; then
echo "⚠️ Docker Compose plugin not found. Installing..."
sudo apt-get update && sudo apt-get install -y docker-compose-plugin
fi
echo "✅ Environment ready"
echo ""
# 2. Install dependencies
echo "2. Installing dependencies..."
go mod tidy
echo "✅ Dependencies installed"
# 2. Calculate dependency hash (match CI workflow)
echo "2. Calculating dependency hash..."
# Use shasum on macOS, sha256sum on Linux
if command -v sha256sum >/dev/null 2>&1; then
export DEPS_HASH=$(sha256sum go.mod go.sum | sha256sum | cut -d' ' -f1 | head -c 12)
else
export DEPS_HASH=$(shasum -a 256 go.mod go.sum | shasum -a 256 | cut -d' ' -f1 | head -c 12)
fi
echo "Dependency hash: $DEPS_HASH"
echo "✅ Dependency hash calculated"
echo ""
# 3. Install swag and generate docs
echo "3. Generating Swagger documentation..."
if [ ! -f pkg/server/docs/swagger.json ]; then
echo "📝 Generating Swagger docs..."
go install github.com/swaggo/swag/cmd/swag@latest
cd pkg/server && go generate
cd ../..
echo "✅ Swagger documentation generated"
# 3. Check for Docker cache
echo "3. Checking for Docker build cache..."
IMAGE_NAME="gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:$DEPS_HASH"
# Try to pull the cache image
if docker pull "$IMAGE_NAME" >/dev/null 2>&1; then
echo "✅ Cache hit - using existing build cache"
USE_DOCKER_CACHE=true
else
echo "✅ Swagger documentation already exists"
echo "⚠️ Cache miss - will build without cache"
USE_DOCKER_CACHE=false
fi
echo ""
# 4. Build and test
echo "4. Building and testing..."
go build ./...
# 4. Start PostgreSQL with Docker Compose
echo "4. Starting PostgreSQL..."
docker compose -f docker-compose.yml up -d postgres
# Wait for PostgreSQL to be ready
echo "Waiting for PostgreSQL to be ready..."
for i in {1..30}; do
if docker exec dance-lessons-coach-postgres pg_isready -U postgres; then
echo "✅ PostgreSQL is ready!"
break
fi
echo "Waiting for PostgreSQL... ($i/30)"
sleep 2
done
# Set PostgreSQL environment variables for BDD tests
export DLC_DATABASE_HOST="localhost" # PostgreSQL port is mapped to host
export DLC_DATABASE_PORT=5432
export DLC_DATABASE_USER=postgres
export DLC_DATABASE_PASSWORD=postgres
export DLC_DATABASE_NAME=dance_lessons_coach_bdd_test
export DLC_DATABASE_SSL_MODE=disable
echo ""
# 5. Install dependencies
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "5. Checking dependencies..."
echo "✅ Using pre-installed dependencies from Docker cache"
else
echo "5. Installing dependencies..."
go mod tidy
fi
echo "✅ Dependencies ready"
echo ""
# 6. Generate Swagger Docs
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "6. Generating Swagger documentation..."
echo "Running in Docker container..."
docker run --rm \
--network dance-lessons-coach-network \
-v "$(pwd):/workspace" \
-w /workspace/pkg/server \
"$IMAGE_NAME" \
sh -c "go generate"
else
echo "6. Generating Swagger documentation..."
echo "Running natively..."
cd pkg/server && go generate
cd ../..
fi
echo "✅ Swagger documentation generated"
echo ""
# 7. Build and test
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "7. Building and testing..."
echo "Running in Docker container..."
docker run --rm \
--network dance-lessons-coach-network \
-v "$(pwd):/workspace" \
-w /workspace \
"$IMAGE_NAME" \
sh -c "go build ./..."
else
echo "7. Building and testing..."
echo "Running natively..."
go build ./...
fi
echo "✅ Code compiled successfully"
go test ./... -cover -v
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "Running in Docker container with PostgreSQL..."
docker run --rm \
--network dance-lessons-coach-network \
-v "$(pwd):/workspace" \
-w /workspace \
-e DLC_DATABASE_HOST=dance-lessons-coach-postgres \
-e DLC_DATABASE_PORT=5432 \
-e DLC_DATABASE_USER=postgres \
-e DLC_DATABASE_PASSWORD=postgres \
-e DLC_DATABASE_NAME=dance_lessons_coach_bdd_test \
-e DLC_DATABASE_SSL_MODE=disable \
"$IMAGE_NAME" \
sh -c "go test ./... -coverprofile=coverage.out -v && go tool cover -func=coverage.out > coverage.txt"
else
echo "Running natively with Docker Compose PostgreSQL..."
go test ./... -coverprofile=coverage.out -v
go tool cover -func=coverage.out > coverage.txt
fi
echo "✅ Tests passed"
echo ""
# 5. Build binaries
echo "5. Building binaries..."
./scripts/build.sh
# 8. Build binaries
if [ "$USE_DOCKER_CACHE" = true ]; then
echo "8. Building binaries..."
echo "Running in Docker container..."
docker run --rm \
--network dance-lessons-coach-network \
-v "$(pwd):/workspace" \
-w /workspace \
"$IMAGE_NAME" \
sh -c "./scripts/build.sh"
else
echo "8. Building binaries..."
echo "Running natively..."
./scripts/build.sh
fi
echo "✅ Binaries built"
ls -la bin/
echo ""
# 6. Version bump simulation
echo "6. Version bump simulation..."
# 9. Version bump simulation
echo "9. Version bump simulation..."
LAST_COMMIT=$(git log -1 --pretty=%B | head -1)
echo "Last commit: $LAST_COMMIT"
@@ -85,152 +194,170 @@ CURRENT_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}"
echo "📊 Current version: $CURRENT_VERSION"
echo ""
# 7. Local Docker build instructions
if [ "$HAS_DOCKER" = true ]; then
echo "🐳 LOCAL DOCKER BUILD INSTRUCTIONS"
echo "================================"
echo ""
echo "1. Build Docker image locally:"
echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ."
echo ""
echo "2. Tag the image:"
echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest"
echo ""
echo "3. Test the local image (check port availability first):"
echo " docker run -d -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION"
echo " # Or use alternative port if 8080 is in use:"
echo " docker run -d -p 8081:8080 dance-lessons-coach:$CURRENT_VERSION"
echo ""
echo "4. Branch-specific container naming (recommended):"
echo " BRANCH=\"(git rev-parse --abbrev-ref HEAD | tr '/' '-')"
echo " docker run -d -p 8080:8080 --name dance-lessons-coach-\"$BRANCH\" dance-lessons-coach:$CURRENT_VERSION"
echo ""
echo "5. Test API endpoints:"
echo " curl http://localhost:8080/api/health"
echo " curl http://localhost:8080/api/v1/greet/YourName"
echo ""
echo "5. Clean up:"
echo " docker stop <container_id> && docker rm <container_id>"
echo ""
echo "💡 Tip: Use 'docker images' to see your built images"
echo "💡 Use 'docker ps' to see running containers"
echo ""
# Ask if user wants to build Docker image now
read -p "🚀 Do you want to build the Docker image now? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "🐳 Building Docker image..."
# 10. Local Docker build instructions
echo "🐳 LOCAL DOCKER BUILD INSTRUCTIONS"
echo "================================"
echo ""
echo "1. Build Docker image locally (development):"
echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ."
echo ""
echo "2. Build production image using docker/Dockerfile.prod:"
echo " # Note: Local docker/Dockerfile.prod uses 'latest' tag for testing"
echo " docker build -t dance-lessons-coach-prod:$CURRENT_VERSION -f docker/Dockerfile.prod ."
echo " # For CI/CD, the workflow generates correct docker/Dockerfile.prod with dependency hash"
echo ""
echo "3. Compare image sizes:"
echo " docker images | grep dance-lessons-coach"
echo ""
echo "4. Tag the image:"
echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest"
echo ""
echo "5. Test the local image (check port availability first):"
echo " docker run -d -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION"
echo " # Or use alternative port if 8080 is in use:"
echo " docker run -d -p 8081:8080 dance-lessons-coach:$CURRENT_VERSION"
echo ""
echo "6. Branch-specific container naming (recommended):"
echo " BRANCH=\"$(git rev-parse --abbrev-ref HEAD | tr '/' '-')\""
echo " docker run -d -p 8080:8080 --name dance-lessons-coach-\"$BRANCH\" dance-lessons-coach:$CURRENT_VERSION"
echo ""
echo "7. Test API endpoints:"
echo " curl http://localhost:8080/api/health"
echo " curl http://localhost:8080/api/v1/greet/YourName"
echo ""
echo "8. Clean up:"
echo " docker stop <container_id> && docker rm <container_id>"
echo ""
echo "💡 Tip: Use 'docker images' to see your built images"
echo "💡 Use 'docker ps' to see running containers"
echo ""
# Ask if user wants to build Docker image now
read -p "🚀 Do you want to build the Docker image now? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "🐳 Building Docker image..."
read -p "📋 Build (d)development or (p)production image? [d/p]: " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Pp]$ ]]; then
echo "🏗️ Building production image with docker/Dockerfile.prod..."
docker build -t dance-lessons-coach-prod:$CURRENT_VERSION -f docker/Dockerfile.prod .
docker tag dance-lessons-coach-prod:$CURRENT_VERSION dance-lessons-coach-prod:latest
echo "✅ Production Docker image built: dance-lessons-coach-prod:$CURRENT_VERSION"
CONTAINER_IMAGE="dance-lessons-coach-prod:$CURRENT_VERSION"
else
echo "🏗️ Building development image with Dockerfile..."
docker build -t dance-lessons-coach:$CURRENT_VERSION .
docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest
echo "✅ Docker image built: dance-lessons-coach:$CURRENT_VERSION"
echo ""
# Check if port 8080 is available
echo "🔍 Checking port availability..."
if lsof -i :8080 > /dev/null 2>&1; then
echo "⚠️ Port 8080 is already in use"
read -p "🚀 Do you want to use a different port? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
read -p "Enter port number (e.g., 8081): " CUSTOM_PORT
echo ""
PORT=$CUSTOM_PORT
else
echo " Using port 8080 anyway (may fail if service is running)"
PORT=8080
fi
echo " Development Docker image built: dance-lessons-coach:$CURRENT_VERSION"
CONTAINER_IMAGE="dance-lessons-coach:$CURRENT_VERSION"
fi
echo ""
# Check if port 8080 is available
echo "🔍 Checking port availability..."
if lsof -i :8080 > /dev/null 2>&1; then
echo "⚠️ Port 8080 is already in use"
read -p "🚀 Do you want to use a different port? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
read -p "Enter port number (e.g., 8081): " CUSTOM_PORT
echo ""
PORT=$CUSTOM_PORT
else
echo "✅ Port 8080 is available"
echo " Using port 8080 anyway (may fail if service is running)"
PORT=8080
fi
read -p "🚀 Do you want to run the container now on port $PORT? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
# Get current branch name for container naming
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '/' '-')
CONTAINER_NAME="dance-lessons-coach-$BRANCH_NAME"
echo "🐳 Preparing container '$CONTAINER_NAME' on port $PORT..."
# Remove existing container if it exists
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
echo "⚠️ Container '$CONTAINER_NAME' already exists - removing it..."
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
echo "✅ Old container removed"
fi
# Also remove the generic test container if it exists
if docker ps -a --format '{{.Names}}' | grep -q "^dance-lessons-coach-test$"; then
echo "⚠️ Generic test container exists - removing it..."
docker stop dance-lessons-coach-test > /dev/null 2>&1 || true
docker rm dance-lessons-coach-test > /dev/null 2>&1 || true
echo "✅ Old generic container removed"
fi
echo "🐳 Starting container '$CONTAINER_NAME' on port $PORT..."
docker run -d -p $PORT:8080 --name "$CONTAINER_NAME" dance-lessons-coach:$CURRENT_VERSION
echo "✅ Container '$CONTAINER_NAME' started on port $PORT"
echo ""
# Wait for container to be ready
echo "🕒 Waiting for container to be ready..."
MAX_ATTEMPTS=10
ATTEMPT=1
READY=false
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
READY=true
break
fi
sleep 1
ATTEMPT=$((ATTEMPT + 1))
echo "🕒 Attempt $ATTEMPT/$MAX_ATTEMPTS..."
done
if [ "$READY" = true ]; then
echo "✅ Container is ready!"
else
echo "❌ Container failed to start properly"
echo "📋 Container logs:"
docker logs dance-lessons-coach-test
echo ""
echo "💡 Check container status with: docker ps -a"
echo "💡 View full logs with: docker logs dance-lessons-coach-test"
continue # Skip endpoint testing
fi
echo "📋 Testing endpoints..."
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
echo "✅ Health check passed"
else
echo "❌ Health check failed"
fi
if curl -s http://localhost:$PORT/api/v1/greet/ | grep -q "Hello"; then
echo "✅ Greet endpoint working"
else
echo "❌ Greet endpoint failed"
fi
echo ""
echo "📖 Swagger UI available at: http://localhost:$PORT/swagger/"
echo "💡 Press Ctrl+C to stop the container when done"
echo " Or run: docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME"
fi
else
echo "✅ Port 8080 is available"
PORT=8080
fi
read -p "🚀 Do you want to run the container now on port $PORT? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
# Get current branch name for container naming
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '/' '-')
CONTAINER_NAME="dance-lessons-coach-$BRANCH_NAME"
echo "🐳 Preparing container '$CONTAINER_NAME' on port $PORT..."
# Remove existing container if it exists
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
echo "⚠️ Container '$CONTAINER_NAME' already exists - removing it..."
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
echo "✅ Old container removed"
fi
# Also remove the generic test container if it exists
if docker ps -a --format '{{.Names}}' | grep -q "^dance-lessons-coach-test$"; then
echo "⚠️ Generic test container exists - removing it..."
docker stop dance-lessons-coach-test > /dev/null 2>&1 || true
docker rm dance-lessons-coach-test > /dev/null 2>&1 || true
echo "✅ Old generic container removed"
fi
echo "🐳 Starting container '$CONTAINER_NAME' on port $PORT..."
docker run -d -p $PORT:8080 --name "$CONTAINER_NAME" "$CONTAINER_IMAGE"
echo "✅ Container '$CONTAINER_NAME' started on port $PORT"
echo ""
# Wait for container to be ready
echo "🕒 Waiting for container to be ready..."
MAX_ATTEMPTS=10
ATTEMPT=1
READY=false
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
READY=true
break
fi
sleep 1
ATTEMPT=$((ATTEMPT + 1))
echo "🕒 Attempt $ATTEMPT/$MAX_ATTEMPTS..."
done
if [ "$READY" = true ]; then
echo "✅ Container is ready!"
else
echo " Container failed to start properly"
echo "📋 Container logs:"
docker logs dance-lessons-coach-test
echo ""
echo "💡 Check container status with: docker ps -a"
echo "💡 View full logs with: docker logs dance-lessons-coach-test"
continue # Skip endpoint testing
fi
echo "📋 Testing endpoints..."
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
echo "✅ Health check passed"
else
echo "❌ Health check failed"
fi
if curl -s http://localhost:$PORT/api/v1/greet/ | grep -q "Hello"; then
echo "✅ Greet endpoint working"
else
echo "❌ Greet endpoint failed"
fi
echo ""
echo "📖 Swagger UI available at: http://localhost:$PORT/swagger/"
echo "💡 Press Ctrl+C to stop the container when done"
echo " Or run: docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME"
fi
else
echo " Docker not available - skipping Docker build instructions"
fi
echo ""
@@ -238,18 +365,22 @@ echo "✅ LOCAL CI/CD TEST COMPLETE"
echo "==========================="
echo ""
echo "📋 What was tested:"
echo " ✅ Dependency hash calculation (matching CI workflow)"
echo " ✅ Docker cache detection and usage"
echo " ✅ PostgreSQL service with Docker Compose"
echo " ✅ Go dependencies installation"
echo " ✅ Swagger documentation generation"
echo " ✅ Code compilation"
echo " ✅ Unit tests with coverage"
echo " ✅ Binary build"
echo " ✅ Version bump simulation"
if [ "$HAS_DOCKER" = true ]; then
echo " ✅ Docker build (if chosen)"
fi
echo " ✅ Docker build (development and/or production if chosen)"
echo ""
echo "🎯 When ready for production:"
echo " Push to main branch to trigger full CI/CD pipeline"
echo " Docker image will be built and pushed to Gitea Container Registry"
echo ""
echo "💡 Local testing complete! Your changes are ready for CI/CD."
echo "💡 Local testing complete! Your changes are ready for CI/CD."
echo "💡 This script now matches the Gitea workflow structure and behavior."
# ⚠️ IMPORTANT: Local Dockerfile.prod uses 'latest' tag for testing only
# ✅ CI/CD workflow generates correct Dockerfile.prod with dependency hash

View File

@@ -1,15 +1,15 @@
#!/bin/bash
# DanceLessonsCoach OpenTelemetry Test Script
# dance-lessons-coach OpenTelemetry Test Script
# This script tests OpenTelemetry integration with Jaeger
set -e
echo -e "\033[1;34m=== DanceLessonsCoach OpenTelemetry Test ===\033[0m"
echo -e "\033[1;34m=== dance-lessons-coach OpenTelemetry Test ===\033[0m"
echo ""
# Configuration
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach"
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach"
SERVER_CMD="./scripts/start-server.sh"
LOG_FILE="server.log"
PID_FILE="server.pid"
@@ -47,7 +47,7 @@ fi
echo "Starting server with OpenTelemetry enabled..."
DLC_TELEMETRY_ENABLED=true DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317" DLC_TELEMETRY_INSECURE=true \
DLC_TELEMETRY_SERVICE_NAME="DanceLessonsCoach" $SERVER_CMD start
DLC_TELEMETRY_SERVICE_NAME="dance-lessons-coach" $SERVER_CMD start
sleep 3
echo "Testing API endpoints..."
@@ -77,7 +77,7 @@ echo -e "\033[0;32m✅ OpenTelemetry Test Complete!\033[0m"
echo ""
echo "To view traces in Jaeger:"
echo "1. Open http://localhost:16686 in your browser"
echo "2. Select 'DanceLessonsCoach' service"
echo "2. Select 'dance-lessons-coach' service"
echo "3. Click 'Find Traces' button"
echo ""
echo "You should see traces for:"

View File

@@ -171,4 +171,4 @@ echo "💡 Next Steps:"
echo " 1. Test with Docker: ./scripts/test-cicd-simple.sh"
echo " 2. Commit changes: git commit -m '🤖 ci: validate workflow'"
echo " 3. Push to trigger: git push origin main"
echo " 4. Monitor pipeline: https://gitea.arcodange.lab/arcodange/DanceLessonsCoach/actions"
echo " 4. Monitor pipeline: https://gitea.arcodange.lab/arcodange/dance-lessons-coach/actions"

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# DanceLessonsCoach Version Bump Script
# dance-lessons-coach Version Bump Script
# Usage: ./scripts/version-bump.sh [major|minor|patch|pre|release]
set -e
@@ -81,7 +81,7 @@ echo "🔜 New version: $NEW_VERSION"
# Update VERSION file
cat > "$VERSION_FILE" << VERSION_EOF
# DanceLessonsCoach Version
# dance-lessons-coach Version
# Current Version (Semantic Versioning)
MAJOR=$MAJOR
@@ -139,7 +139,7 @@ if [ -f "$README_MD" ]; then
# Use awk to update version badge
awk -v new_version="$NEW_VERSION" '{
if ($0 ~ /Version.*badge.*version/) {
print "[![Version](https://img.shields.io/badge/version-" new_version "-blue.svg)](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach/releases)"
print "[![Version](https://img.shields.io/badge/version-" new_version "-blue.svg)](https://gitea.arcodange.fr/arcodange/dance-lessons-coach/releases)"
} else {
print $0
}