📝 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 ## Available Hooks

3
.gitignore vendored
View File

@@ -26,3 +26,6 @@ pkg/server/docs/
# CI/CD runner configuration # CI/CD runner configuration
config/runner config/runner
.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 # More practical limits for CI/CD workflow files
extends: default 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 ## 🎯 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 - RESTful JSON API with Chi router
- High-performance Zerolog logging - High-performance Zerolog logging
- Interface-based architecture - Interface-based architecture
@@ -94,7 +94,7 @@ This file documents the AI agents, tools, and development workflow for the Dance
## 🗺️ Project Structure ## 🗺️ Project Structure
``` ```
DanceLessonsCoach/ dance-lessons-coach/
├── adr/ # Architecture Decision Records ├── adr/ # Architecture Decision Records
│ ├── README.md # ADR guidelines and index │ ├── README.md # ADR guidelines and index
│ ├── 0001-go-1.26.1-standard.md │ ├── 0001-go-1.26.1-standard.md
@@ -138,7 +138,7 @@ DanceLessonsCoach/
### New Cobra CLI (Recommended) ### 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 ```bash
# Show help and available commands # Show help and available commands
@@ -156,7 +156,7 @@ DanceLessonsCoach now includes a modern CLI built with Cobra framework:
**Available Commands:** **Available Commands:**
- `version` - Print version information - `version` - Print version information
- `server` - Start the DanceLessonsCoach server - `server` - Start the dance-lessons-coach server
- `greet [name]` - Greet someone by name - `greet [name]` - Greet someone by name
- `help` - Built-in help system - `help` - Built-in help system
- `completion` - Generate shell completion scripts - `completion` - Generate shell completion scripts
@@ -178,7 +178,7 @@ The server provides runtime version information:
./bin/server --version ./bin/server --version
# Output: # Output:
DanceLessonsCoach Version Information: dance-lessons-coach Version Information:
Version: 1.0.0 Version: 1.0.0
Commit: abc1234 Commit: abc1234
Built: 2026-04-05T10:00:00+0000 Built: 2026-04-05T10:00:00+0000
@@ -191,7 +191,7 @@ A convenient shell script is provided for managing the server lifecycle:
```bash ```bash
# Navigate to project directory # Navigate to project directory
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
# Start the server # Start the server
./scripts/start-server.sh start ./scripts/start-server.sh start
@@ -223,7 +223,7 @@ If you prefer manual control:
```bash ```bash
# Navigate to project directory # 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 # Run server in background using control script
./scripts/start-server.sh start ./scripts/start-server.sh start
@@ -535,7 +535,7 @@ Enable OpenTelemetry in your `config.yaml`:
telemetry: telemetry:
enabled: true enabled: true
otlp_endpoint: "localhost:4317" otlp_endpoint: "localhost:4317"
service_name: "DanceLessonsCoach" service_name: "dance-lessons-coach"
insecure: true insecure: true
sampler: sampler:
type: "parentbased_always_on" type: "parentbased_always_on"
@@ -547,7 +547,7 @@ Or via environment variables:
```bash ```bash
export DLC_TELEMETRY_ENABLED=true export DLC_TELEMETRY_ENABLED=true
export DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317" 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_INSECURE=true
export DLC_TELEMETRY_SAMPLER_TYPE="parentbased_always_on" export DLC_TELEMETRY_SAMPLER_TYPE="parentbased_always_on"
export DLC_TELEMETRY_SAMPLER_RATIO=1.0 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:** 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 ### Sampler Types
@@ -613,7 +613,7 @@ curl -s http://localhost:8080/api/health
### 2. Start Development Server ### 2. Start Development Server
```bash ```bash
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
./scripts/start-server.sh start ./scripts/start-server.sh start
``` ```
@@ -927,7 +927,7 @@ defer cancel()
## 📦 Version Management ## 📦 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 ### Version Information
@@ -990,9 +990,9 @@ curl http://localhost:8080/api/version
# Release build # Release build
go build -o bin/server \ go build -o bin/server \
-ldflags="\ -ldflags="\
-X 'DanceLessonsCoach/pkg/version.Version=1.0.0' \ -X 'dance-lessons-coach/pkg/version.Version=1.0.0' \
-X 'DanceLessonsCoach/pkg/version.Commit=$(git rev-parse --short HEAD)' \ -X 'dance-lessons-coach/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.Date=$(date +%Y-%m-%dT%H:%M:%S%z)' \
" \ " \
./cmd/server ./cmd/server
``` ```
@@ -1034,7 +1034,7 @@ The `pkg/version` package provides runtime access to version information:
package main package main
import ( import (
"DanceLessonsCoach/pkg/version" "dance-lessons-coach/pkg/version"
"fmt" "fmt"
) )
@@ -1267,7 +1267,7 @@ For issues or questions:
4. Consult Go and Chi documentation 4. Consult Go and Chi documentation
5. Ask the AI agent for guidance 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 ## 📋 BDD Feature Structure
All user stories and BDD features follow the structure defined in ADR-0019: 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 ## 📋 Table of Contents
@@ -24,8 +24,8 @@ Thank you for your interest in contributing to DanceLessonsCoach! This guide wil
```bash ```bash
# Clone the repository # Clone the repository
git clone https://gitea.arcodange.lab/arcodange/DanceLessonsCoach.git git clone https://gitea.arcodange.lab/arcodange/dance-lessons-coach.git
cd DanceLessonsCoach cd dance-lessons-coach
# Install dependencies # Install dependencies
go mod tidy go mod tidy
@@ -260,7 +260,7 @@ Major architectural decisions are documented in the `adr/` directory. Please rev
## 🤖 AI Agent Contributions ## 🤖 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 ### Key Files and Directories
@@ -342,7 +342,7 @@ AI agents play a crucial role in maintaining and improving DanceLessonsCoach. Th
## 📜 License ## 📜 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 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 ### Key Files and Directories
@@ -432,7 +432,7 @@ AI agents play a crucial role in maintaining and improving DanceLessonsCoach. Th
## 📜 License ## 📜 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) [![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/DanceLessonsCoach)](https://goreportcard.com/report/github.com/arcodange/DanceLessonsCoach) [![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/DanceLessonsCoach/releases) [![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) [![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. 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 ## 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 ### Key Features
-**Multi-platform**: Works on Gitea, GitHub, and GitLab -**Container-based execution**: All steps run in pre-built Docker cache images
-**Build & Test**: Automated Go builds and tests -**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` -**Linting**: Code quality checks with `go fmt` and `go vet`
-**Version Management**: Automatic version detection -**Version Management**: Automatic version detection
-**Portable**: Uses standard GitHub Actions workflow format -**Portable**: Uses standard GitHub Actions workflow format
@@ -184,7 +244,7 @@ go test ./pkg/greet/
## CI/CD ## 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) ### Local Testing (No Gitea Required)
```bash ```bash
@@ -215,7 +275,7 @@ DanceLessonsCoach includes a comprehensive CI/CD pipeline with multiple testing
## Project Structure ## Project Structure
``` ```
DanceLessonsCoach/ dance-lessons-coach/
├── adr/ # Architecture Decision Records ├── adr/ # Architecture Decision Records
├── cmd/ # Entry points (greet CLI, server) ├── cmd/ # Entry points (greet CLI, server)
├── pkg/ # Core packages (config, greet, server, telemetry) ├── pkg/ # Core packages (config, greet, server, telemetry)
@@ -273,7 +333,7 @@ This project uses Architecture Decision Records (ADRs) to document key technical
## Gitea Integration ## 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 ### Gitea Client Skill Setup

View File

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

View File

@@ -1,16 +1,16 @@
# DanceLessonsCoach Agent Usage Guide # dance-lessons-coach Agent Usage Guide
## 🚀 Quick Start ## 🚀 Quick Start
### Launch Programmer Agent ### Launch Programmer Agent
```bash ```bash
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
vibe start --agent dancelessonscoachprogrammer vibe start --agent dancelessonscoachprogrammer
``` ```
### Launch Product Owner Agent ### Launch Product Owner Agent
```bash ```bash
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
vibe start --agent dancelessonscoach-product-owner vibe start --agent dancelessonscoach-product-owner
``` ```
@@ -141,7 +141,7 @@ skill changelog-manager add-entry \
```toml ```toml
# .mistral/dancelessonscoachprogrammer-agent.toml # .mistral/dancelessonscoachprogrammer-agent.toml
name: dancelessonscoachprogrammer name: dancelessonscoachprogrammer
role: DanceLessonsCoachProgrammer role: dance-lessons-coach-programmer
goals: ["Follow BDD practices", "Use Gitmoji commits", "Respect ADR process"] 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 ```toml
# .mistral/dancelessonscoach-product-owner-agent.toml # .mistral/dancelessonscoach-product-owner-agent.toml
name: dancelessonscoach-product-owner name: dancelessonscoach-product-owner
role: DanceLessonsCoachProductOwner role: dance-lessons-coach-product-owner
goals: ["Facilitate stakeholder interviews", "Generate BDD tests", "Maintain documentation"] goals: ["Facilitate stakeholder interviews", "Generate BDD tests", "Maintain documentation"]
``` ```
@@ -210,7 +210,7 @@ vibe validate --agent dancelessonscoach-product-owner
```bash ```bash
# List available skills # List available skills
ls /Users/gabrielradureau/Work/Vibe/.mistral/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 # Validate skill
skill skill-creator validate .vibe/skills/product-owner-assistant skill skill-creator validate .vibe/skills/product-owner-assistant
@@ -222,7 +222,7 @@ skill skill-creator validate .mistral/skills/interview-facilitator
```bash ```bash
# Check file permissions # Check file permissions
chmod +x /Users/gabrielradureau/Work/Vibe/.mistral/skills/*/scripts/* 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 ## 📖 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 ## Installation
@@ -33,7 +33,7 @@ The project already includes Godog as a dependency in `go.mod`. The BDD tests ar
```bash ```bash
# From project root # From project root
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
go test ./features/... -v go test ./features/... -v
``` ```
@@ -112,7 +112,7 @@ Create a corresponding step definition file in `pkg/bdd/steps/`:
package steps package steps
import ( import (
"DanceLessonsCoach/pkg/bdd/testserver" "dance-lessons-coach/pkg/bdd/testserver"
"github.com/cucumber/godog" "github.com/cucumber/godog"
) )
@@ -213,7 +213,7 @@ Add BDD tests to your CI pipeline:
## Modern Go Testing Practices ## 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` 1. **Standard library integration**: BDD tests use `go test`
2. **No global installation required**: Godog is a Go module dependency 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 ### 1. Run the Interactive Script
```bash ```bash
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
./scripts/test-local-ci-cd.sh ./scripts/test-local-ci-cd.sh
``` ```

View File

@@ -1,6 +1,6 @@
# Version Management Guide # 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 ## 📋 Table of Contents
@@ -13,7 +13,7 @@ This guide provides comprehensive instructions for managing versions in the Danc
## 📖 Semantic Versioning ## 📖 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` ### 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 **Last Updated:** 2026-04-05
**Version:** 1.0 **Version:** 1.0

View File

@@ -13,6 +13,11 @@ import (
"dance-lessons-coach/pkg/version" "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 // Config represents the application configuration
type Config struct { type Config struct {
Server ServerConfig `mapstructure:"server"` Server ServerConfig `mapstructure:"server"`
@@ -20,6 +25,8 @@ type Config struct {
Logging LoggingConfig `mapstructure:"logging"` Logging LoggingConfig `mapstructure:"logging"`
Telemetry TelemetryConfig `mapstructure:"telemetry"` Telemetry TelemetryConfig `mapstructure:"telemetry"`
API APIConfig `mapstructure:"api"` API APIConfig `mapstructure:"api"`
Auth AuthConfig `mapstructure:"auth"`
Database DatabaseConfig `mapstructure:"database"`
} }
// ServerConfig holds server-related configuration // ServerConfig holds server-related configuration
@@ -42,11 +49,17 @@ type LoggingConfig struct {
// TelemetryConfig holds OpenTelemetry-related configuration // TelemetryConfig holds OpenTelemetry-related configuration
type TelemetryConfig struct { type TelemetryConfig struct {
Enabled bool `mapstructure:"enabled"` Enabled bool `mapstructure:"enabled"`
OTLPEndpoint string `mapstructure:"otlp_endpoint"` OTLPEndpoint string `mapstructure:"otlp_endpoint"`
ServiceName string `mapstructure:"service_name"` ServiceName string `mapstructure:"service_name"`
Insecure bool `mapstructure:"insecure"` Insecure bool `mapstructure:"insecure"`
Sampler SamplerConfig `mapstructure:"sampler"` 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 // APIConfig holds API version configuration
@@ -54,6 +67,25 @@ type APIConfig struct {
V2Enabled bool `mapstructure:"v2_enabled"` 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 // VersionInfo holds application version information
type VersionInfo struct { type VersionInfo struct {
Version string `mapstructure:"-"` // Set via ldflags Version string `mapstructure:"-"` // Set via ldflags
@@ -65,7 +97,7 @@ type VersionInfo struct {
// VersionCommand handles version display // VersionCommand handles version display
func (c *Config) VersionCommand() string { func (c *Config) VersionCommand() string {
// This will be enhanced when we integrate with cobra // 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) version.Version, version.Commit, version.Date, version.GoVersion)
} }
@@ -96,14 +128,19 @@ func LoadConfig() (*Config, error) {
// Telemetry defaults // Telemetry defaults
v.SetDefault("telemetry.enabled", false) v.SetDefault("telemetry.enabled", false)
v.SetDefault("telemetry.otlp_endpoint", "localhost:4317") 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.insecure", true)
v.SetDefault("telemetry.sampler.type", "parentbased_always_on") v.SetDefault("telemetry.sampler.type", "parentbased_always_on")
v.SetDefault("telemetry.sampler.ratio", 1.0) v.SetDefault("telemetry.sampler.ratio", 1.0)
v.SetDefault("telemetry.persistence.enabled", false)
// API defaults // API defaults
v.SetDefault("api.v2_enabled", false) 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 // Check for custom config file path via environment variable
if configFile := os.Getenv("DLC_CONFIG_FILE"); configFile != "" { if configFile := os.Getenv("DLC_CONFIG_FILE"); configFile != "" {
v.SetConfigFile(configFile) v.SetConfigFile(configFile)
@@ -128,7 +165,7 @@ func LoadConfig() (*Config, error) {
// Bind environment variables // Bind environment variables
v.AutomaticEnv() v.AutomaticEnv()
v.SetEnvPrefix("DLC") // DanceLessonsCoach prefix v.SetEnvPrefix("DLC") // dance-lessons-coach prefix
v.BindEnv("server.host", "DLC_SERVER_HOST") v.BindEnv("server.host", "DLC_SERVER_HOST")
v.BindEnv("server.port", "DLC_SERVER_PORT") v.BindEnv("server.port", "DLC_SERVER_PORT")
v.BindEnv("shutdown.timeout", "DLC_SHUTDOWN_TIMEOUT") 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.otlp_endpoint", "DLC_TELEMETRY_OTLP_ENDPOINT")
v.BindEnv("telemetry.service_name", "DLC_TELEMETRY_SERVICE_NAME") v.BindEnv("telemetry.service_name", "DLC_TELEMETRY_SERVICE_NAME")
v.BindEnv("telemetry.insecure", "DLC_TELEMETRY_INSECURE") 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.type", "DLC_TELEMETRY_SAMPLER_TYPE")
v.BindEnv("telemetry.sampler.ratio", "DLC_TELEMETRY_SAMPLER_RATIO") v.BindEnv("telemetry.sampler.ratio", "DLC_TELEMETRY_SAMPLER_RATIO")
// API environment variables // API environment variables
v.BindEnv("api.v2_enabled", "DLC_API_V2_ENABLED") 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 // Unmarshal into Config struct
var config Config var config Config
if err := v.Unmarshal(&config); err != nil { if err := v.Unmarshal(&config); err != nil {
@@ -200,6 +249,11 @@ func (c *Config) GetServiceName() string {
return c.Telemetry.ServiceName 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 // GetTelemetryInsecure returns whether to use insecure connection
func (c *Config) GetTelemetryInsecure() bool { func (c *Config) GetTelemetryInsecure() bool {
return c.Telemetry.Insecure return c.Telemetry.Insecure
@@ -220,6 +274,21 @@ func (c *Config) GetV2Enabled() bool {
return c.API.V2Enabled 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 // GetLogLevel returns the logging level
func (c *Config) GetLogLevel() string { func (c *Config) GetLogLevel() string {
return c.Logging.Level return c.Logging.Level
@@ -230,6 +299,75 @@ func (c *Config) GetLogOutput() string {
return c.Logging.Output 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 // SetupLogging configures zerolog based on the configuration
func (c *Config) SetupLogging() { func (c *Config) SetupLogging() {
// Parse log level // Parse log level

View File

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

View File

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

View File

@@ -3,21 +3,46 @@ package greet
import ( import (
"context" "context"
"dance-lessons-coach/pkg/user"
"github.com/rs/zerolog/log" "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{} type Service struct{}
func NewService() *Service { func NewService() *Service {
return &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. // 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. // Implements the Greeter interface.
func (s *Service) Greet(ctx context.Context, name string) string { func (s *Service) Greet(ctx context.Context, name string) string {
log.Trace().Ctx(ctx).Str("name", name).Msg("Greet function called") 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 == "" { if name == "" {
return "Hello world!" return "Hello world!"
} }

View File

@@ -20,8 +20,11 @@ import (
"dance-lessons-coach/pkg/config" "dance-lessons-coach/pkg/config"
"dance-lessons-coach/pkg/greet" "dance-lessons-coach/pkg/greet"
"dance-lessons-coach/pkg/telemetry" "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/validation"
"dance-lessons-coach/pkg/version" "dance-lessons-coach/pkg/version"
"encoding/json"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
sdktrace "go.opentelemetry.io/otel/sdk/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace"
@@ -37,6 +40,8 @@ type Server struct {
config *config.Config config *config.Config
tracerProvider *sdktrace.TracerProvider tracerProvider *sdktrace.TracerProvider
validator *validation.Validator validator *validation.Validator
userRepo user.UserRepository
userService user.UserService
} }
func NewServer(cfg *config.Config, readyCtx context.Context) *Server { 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") 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{ s := &Server{
router: chi.NewRouter(), router: chi.NewRouter(),
readyCtx: readyCtx, readyCtx: readyCtx,
withOTEL: cfg.GetTelemetryEnabled(), withOTEL: cfg.GetTelemetryEnabled(),
config: cfg, config: cfg,
validator: validator, validator: validator,
userRepo: userRepo,
userService: userService,
} }
s.setupRoutes() s.setupRoutes()
return s 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() { func (s *Server) setupRoutes() {
// Use Zerolog middleware instead of Chi's default logger // Use Zerolog middleware instead of Chi's default logger
s.router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{ s.router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{
@@ -109,9 +143,31 @@ func (s *Server) setupRoutes() {
func (s *Server) registerApiV1Routes(r chi.Router) { func (s *Server) registerApiV1Routes(r chi.Router) {
greetService := greet.NewService() greetService := greet.NewService()
greetHandler := greet.NewApiV1GreetHandler(greetService) 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) { r.Route("/greet", func(r chi.Router) {
// Add optional authentication middleware
if authMiddleware != nil {
r.Use(authMiddleware.Middleware)
}
greetHandler.RegisterRoutes(r) 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) { func (s *Server) registerApiV2Routes(r chi.Router) {
@@ -155,24 +211,75 @@ func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
// handleReadiness godoc // handleReadiness godoc
// //
// @Summary Readiness check // @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 // @Tags System/Health
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Success 200 {object} map[string]bool "Service is ready" // @Success 200 {object} object "Service is ready with connection details"
// @Failure 503 {object} map[string]bool "Service is not ready" // @Failure 503 {object} object "Service is not ready with failure details"
// @Router /ready [get] // @Router /ready [get]
func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) { func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) {
log.Trace().Msg("Readiness check requested") log.Trace().Msg("Readiness check requested")
// Check if server is shutting down
select { select {
case <-s.readyCtx.Done(): case <-s.readyCtx.Done():
log.Trace().Msg("Readiness check: not ready (shutting down)") log.Trace().Msg("Readiness check: not ready (shutting down)")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusServiceUnavailable) 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: default:
log.Trace().Msg("Readiness check: ready") // Server is not shutting down, check all connections
w.Write([]byte(`{"ready":true}`)) 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 package telemetry
import ( 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 package version
import ( import (
@@ -91,7 +91,7 @@ func getBuildDate() {
// Info returns formatted version information // Info returns formatted version information
func Info() string { 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 // Short returns just the version number
@@ -101,7 +101,7 @@ func Short() string {
// Full returns detailed version information // Full returns detailed version information
func Full() string { func Full() string {
return fmt.Sprintf(`DanceLessonsCoach Version Information: return fmt.Sprintf(`dance-lessons-coach Version Information:
Version: %s Version: %s
Commit: %s Commit: %s
Built: %s (UTC) 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 ## 📁 Script Categories
@@ -22,7 +22,7 @@ This directory contains automation and management scripts for the DanceLessonsCo
### 1. Server Management (`start-server.sh`) ### 1. Server Management (`start-server.sh`)
**Manage the DanceLessonsCoach server lifecycle** **Manage the dance-lessons-coach server lifecycle**
```bash ```bash
# Start the server # Start the server
@@ -301,13 +301,13 @@ exit 0
- [Git SCM](https://git-scm.com/) - [Git SCM](https://git-scm.com/)
- [Go Build](https://golang.org/cmd/go/) - [Go Build](https://golang.org/cmd/go/)
### DanceLessonsCoach Specific ### dance-lessons-coach Specific
- [ADR 0014: Version Management](adr/0014-version-management-lifecycle.md) - [ADR 0014: Version Management](adr/0014-version-management-lifecycle.md)
- [AGENTS.md Scripts Section](#-scripts) - [AGENTS.md Scripts Section](#-scripts)
- [Contributing Guide](CONTRIBUTING.md) - [Contributing Guide](CONTRIBUTING.md)
--- ---
**Maintained by:** DanceLessonsCoach Team **Maintained by:** dance-lessons-coach Team
**License:** MIT **License:** MIT
**Status:** Actively developed **Status:** Actively developed

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
#!/bin/bash #!/bin/bash
# DanceLessonsCoach CI/CD Management Script # dance-lessons-coach CI/CD Management Script
# Unified interface for all CI/CD operations # Unified interface for all CI/CD operations
set -e set -e
SCRIPTS_DIR="$(dirname "$0")/cicd" SCRIPTS_DIR="$(dirname "$0")/cicd"
echo "🚀 DanceLessonsCoach CI/CD Management" echo "🚀 dance-lessons-coach CI/CD Management"
echo "====================================" echo "===================================="
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 set -e
echo "🧪 Running BDD Tests..." 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 # 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=$? test_exit_code=$?
echo "$test_output" echo "$test_output"
@@ -38,5 +124,6 @@ if [ $test_exit_code -eq 0 ]; then
exit 0 exit 0
else else
echo "❌ BDD tests failed" 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 exit 1
fi 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 #!/bin/bash
# DanceLessonsCoach Server Start Script # dance-lessons-coach Server Start Script
# This script starts the server in the background and provides control functions # This script starts the server in the background and provides control functions
# Configuration # Configuration
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach" PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach"
SERVER_CMD="go run ./cmd/server" SERVER_CMD="go run ./cmd/server"
LOG_FILE="server.log" LOG_FILE="server.log"
PID_FILE="server.pid" 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 # Function to start the server
start_server() { start_server() {
echo "Starting DanceLessonsCoach server..." echo "Starting dance-lessons-coach server..."
# Check if server is already running # Check if server is already running
if [ -f "$PID_FILE" ]; then if [ -f "$PID_FILE" ]; then

View File

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

View File

@@ -3,7 +3,7 @@
# Simulates the CI/CD pipeline but builds Docker image locally # Simulates the CI/CD pipeline but builds Docker image locally
# Use this for local development and testing without Gitea # Use this for local development and testing without Gitea
set -e set -eu
echo "🚀 Local CI/CD Testing" echo "🚀 Local CI/CD Testing"
echo "======================" echo "======================"
@@ -16,53 +16,162 @@ if ! command -v go >/dev/null 2>&1; then
exit 1 exit 1
fi fi
# Assume Docker is available (required for this workflow)
if ! command -v docker >/dev/null 2>&1; then if ! command -v docker >/dev/null 2>&1; then
echo "⚠️ Docker not found. Docker build steps will be skipped" echo " Docker is required for this CI/CD workflow"
HAS_DOCKER=false echo "Please install Docker and Docker Compose plugin"
else exit 1
HAS_DOCKER=true 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 fi
echo "✅ Environment ready" echo "✅ Environment ready"
echo "" echo ""
# 2. Install dependencies # 2. Calculate dependency hash (match CI workflow)
echo "2. Installing dependencies..." echo "2. Calculating dependency hash..."
go mod tidy # Use shasum on macOS, sha256sum on Linux
echo "✅ Dependencies installed" 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 "" echo ""
# 3. Install swag and generate docs # 3. Check for Docker cache
echo "3. Generating Swagger documentation..." echo "3. Checking for Docker build cache..."
if [ ! -f pkg/server/docs/swagger.json ]; then IMAGE_NAME="gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:$DEPS_HASH"
echo "📝 Generating Swagger docs..."
go install github.com/swaggo/swag/cmd/swag@latest # Try to pull the cache image
cd pkg/server && go generate if docker pull "$IMAGE_NAME" >/dev/null 2>&1; then
cd ../.. echo "✅ Cache hit - using existing build cache"
echo "✅ Swagger documentation generated" USE_DOCKER_CACHE=true
else else
echo "✅ Swagger documentation already exists" echo "⚠️ Cache miss - will build without cache"
USE_DOCKER_CACHE=false
fi fi
echo "" echo ""
# 4. Build and test # 4. Start PostgreSQL with Docker Compose
echo "4. Building and testing..." echo "4. Starting PostgreSQL..."
go build ./... 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" 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 "✅ Tests passed"
echo "" echo ""
# 5. Build binaries # 8. Build binaries
echo "5. Building binaries..." if [ "$USE_DOCKER_CACHE" = true ]; then
./scripts/build.sh 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" echo "✅ Binaries built"
ls -la bin/ ls -la bin/
echo "" echo ""
# 6. Version bump simulation # 9. Version bump simulation
echo "6. Version bump simulation..." echo "9. Version bump simulation..."
LAST_COMMIT=$(git log -1 --pretty=%B | head -1) LAST_COMMIT=$(git log -1 --pretty=%B | head -1)
echo "Last commit: $LAST_COMMIT" echo "Last commit: $LAST_COMMIT"
@@ -85,152 +194,170 @@ CURRENT_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}"
echo "📊 Current version: $CURRENT_VERSION" echo "📊 Current version: $CURRENT_VERSION"
echo "" echo ""
# 7. Local Docker build instructions # 10. Local Docker build instructions
if [ "$HAS_DOCKER" = true ]; then echo "🐳 LOCAL DOCKER BUILD INSTRUCTIONS"
echo "🐳 LOCAL DOCKER BUILD INSTRUCTIONS" echo "================================"
echo "================================" echo ""
echo ""
echo "1. Build Docker image locally (development):"
echo "1. Build Docker image locally:" echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ."
echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ." echo ""
echo ""
echo "2. Build production image using docker/Dockerfile.prod:"
echo "2. Tag the image:" echo " # Note: Local docker/Dockerfile.prod uses 'latest' tag for testing"
echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest" echo " docker build -t dance-lessons-coach-prod:$CURRENT_VERSION -f docker/Dockerfile.prod ."
echo "" echo " # For CI/CD, the workflow generates correct docker/Dockerfile.prod with dependency hash"
echo ""
echo "3. Test the local image (check port availability first):"
echo " docker run -d -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION" echo "3. Compare image sizes:"
echo " # Or use alternative port if 8080 is in use:" echo " docker images | grep dance-lessons-coach"
echo " docker run -d -p 8081:8080 dance-lessons-coach:$CURRENT_VERSION" echo ""
echo ""
echo "4. Branch-specific container naming (recommended):" echo "4. Tag the image:"
echo " BRANCH=\"(git rev-parse --abbrev-ref HEAD | tr '/' '-')" echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest"
echo " docker run -d -p 8080:8080 --name dance-lessons-coach-\"$BRANCH\" dance-lessons-coach:$CURRENT_VERSION" echo ""
echo ""
echo "5. Test the local image (check port availability first):"
echo "5. Test API endpoints:" echo " docker run -d -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION"
echo " curl http://localhost:8080/api/health" echo " # Or use alternative port if 8080 is in use:"
echo " curl http://localhost:8080/api/v1/greet/YourName" echo " docker run -d -p 8081:8080 dance-lessons-coach:$CURRENT_VERSION"
echo "" echo ""
echo "6. Branch-specific container naming (recommended):"
echo "5. Clean up:" echo " BRANCH=\"$(git rev-parse --abbrev-ref HEAD | tr '/' '-')\""
echo " docker stop <container_id> && docker rm <container_id>" echo " docker run -d -p 8080:8080 --name dance-lessons-coach-\"$BRANCH\" dance-lessons-coach:$CURRENT_VERSION"
echo "" echo ""
echo "💡 Tip: Use 'docker images' to see your built images" echo "7. Test API endpoints:"
echo "💡 Use 'docker ps' to see running containers" echo " curl http://localhost:8080/api/health"
echo "" echo " curl http://localhost:8080/api/v1/greet/YourName"
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 "8. Clean up:"
echo "" echo " docker stop <container_id> && docker rm <container_id>"
if [[ $REPLY =~ ^[Yy]$ ]]; then echo ""
echo "🐳 Building Docker image..."
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 build -t dance-lessons-coach:$CURRENT_VERSION .
docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest
echo "✅ Docker image built: dance-lessons-coach:$CURRENT_VERSION" echo " Development Docker image built: dance-lessons-coach:$CURRENT_VERSION"
echo "" CONTAINER_IMAGE="dance-lessons-coach:$CURRENT_VERSION"
fi
# Check if port 8080 is available echo ""
echo "🔍 Checking port availability..."
if lsof -i :8080 > /dev/null 2>&1; then # Check if port 8080 is available
echo "⚠️ Port 8080 is already in use" echo "🔍 Checking port availability..."
read -p "🚀 Do you want to use a different port? (y/n): " -n 1 -r if lsof -i :8080 > /dev/null 2>&1; then
echo "" echo "⚠️ Port 8080 is already in use"
if [[ $REPLY =~ ^[Yy]$ ]]; then read -p "🚀 Do you want to use a different port? (y/n): " -n 1 -r
read -p "Enter port number (e.g., 8081): " CUSTOM_PORT echo ""
echo "" if [[ $REPLY =~ ^[Yy]$ ]]; then
PORT=$CUSTOM_PORT read -p "Enter port number (e.g., 8081): " CUSTOM_PORT
else echo ""
echo " Using port 8080 anyway (may fail if service is running)" PORT=$CUSTOM_PORT
PORT=8080
fi
else else
echo "✅ Port 8080 is available" echo " Using port 8080 anyway (may fail if service is running)"
PORT=8080 PORT=8080
fi fi
else
read -p "🚀 Do you want to run the container now on port $PORT? (y/n): " -n 1 -r echo "✅ Port 8080 is available"
echo "" PORT=8080
if [[ $REPLY =~ ^[Yy]$ ]]; then fi
# Get current branch name for container naming
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '/' '-') read -p "🚀 Do you want to run the container now on port $PORT? (y/n): " -n 1 -r
CONTAINER_NAME="dance-lessons-coach-$BRANCH_NAME" echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "🐳 Preparing container '$CONTAINER_NAME' on port $PORT..." # Get current branch name for container naming
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '/' '-')
# Remove existing container if it exists CONTAINER_NAME="dance-lessons-coach-$BRANCH_NAME"
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
echo "⚠️ Container '$CONTAINER_NAME' already exists - removing it..." echo "🐳 Preparing container '$CONTAINER_NAME' on port $PORT..."
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true # Remove existing container if it exists
echo "✅ Old container removed" if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
fi echo "⚠️ Container '$CONTAINER_NAME' already exists - removing it..."
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
# Also remove the generic test container if it exists docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
if docker ps -a --format '{{.Names}}' | grep -q "^dance-lessons-coach-test$"; then echo "✅ Old container removed"
echo "⚠️ Generic test container exists - removing it..." fi
docker stop dance-lessons-coach-test > /dev/null 2>&1 || true
docker rm dance-lessons-coach-test > /dev/null 2>&1 || true # Also remove the generic test container if it exists
echo "✅ Old generic container removed" if docker ps -a --format '{{.Names}}' | grep -q "^dance-lessons-coach-test$"; then
fi echo "⚠️ Generic test container exists - removing it..."
docker stop dance-lessons-coach-test > /dev/null 2>&1 || true
echo "🐳 Starting container '$CONTAINER_NAME' on port $PORT..." docker rm dance-lessons-coach-test > /dev/null 2>&1 || true
docker run -d -p $PORT:8080 --name "$CONTAINER_NAME" dance-lessons-coach:$CURRENT_VERSION echo "✅ Old generic container removed"
echo "✅ Container '$CONTAINER_NAME' started on port $PORT" fi
echo ""
echo "🐳 Starting container '$CONTAINER_NAME' on port $PORT..."
# Wait for container to be ready docker run -d -p $PORT:8080 --name "$CONTAINER_NAME" "$CONTAINER_IMAGE"
echo "🕒 Waiting for container to be ready..." echo "✅ Container '$CONTAINER_NAME' started on port $PORT"
MAX_ATTEMPTS=10 echo ""
ATTEMPT=1
READY=false # Wait for container to be ready
echo "🕒 Waiting for container to be ready..."
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do MAX_ATTEMPTS=10
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then ATTEMPT=1
READY=true READY=false
break
fi while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
sleep 1 if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
ATTEMPT=$((ATTEMPT + 1)) READY=true
echo "🕒 Attempt $ATTEMPT/$MAX_ATTEMPTS..." break
done fi
sleep 1
if [ "$READY" = true ]; then ATTEMPT=$((ATTEMPT + 1))
echo "✅ Container is ready!" echo "🕒 Attempt $ATTEMPT/$MAX_ATTEMPTS..."
else done
echo "❌ Container failed to start properly"
echo "📋 Container logs:" if [ "$READY" = true ]; then
docker logs dance-lessons-coach-test echo "✅ Container is ready!"
echo "" else
echo "💡 Check container status with: docker ps -a" echo " Container failed to start properly"
echo "💡 View full logs with: docker logs dance-lessons-coach-test" echo "📋 Container logs:"
continue # Skip endpoint testing docker logs dance-lessons-coach-test
fi echo ""
echo "💡 Check container status with: docker ps -a"
echo "📋 Testing endpoints..." echo "💡 View full logs with: docker logs dance-lessons-coach-test"
continue # Skip endpoint testing
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then fi
echo "✅ Health check passed"
else echo "📋 Testing endpoints..."
echo "❌ Health check failed"
fi if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
echo "✅ Health check passed"
if curl -s http://localhost:$PORT/api/v1/greet/ | grep -q "Hello"; then else
echo "✅ Greet endpoint working" echo "❌ Health check failed"
else fi
echo "❌ Greet endpoint failed"
fi if curl -s http://localhost:$PORT/api/v1/greet/ | grep -q "Hello"; then
echo "✅ Greet endpoint working"
echo "" else
echo "📖 Swagger UI available at: http://localhost:$PORT/swagger/" echo "❌ Greet endpoint failed"
echo "💡 Press Ctrl+C to stop the container when done" fi
echo " Or run: docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME"
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 fi
else
echo " Docker not available - skipping Docker build instructions"
fi fi
echo "" echo ""
@@ -238,18 +365,22 @@ echo "✅ LOCAL CI/CD TEST COMPLETE"
echo "===========================" echo "==========================="
echo "" echo ""
echo "📋 What was tested:" 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 " ✅ Go dependencies installation"
echo " ✅ Swagger documentation generation" echo " ✅ Swagger documentation generation"
echo " ✅ Code compilation" echo " ✅ Code compilation"
echo " ✅ Unit tests with coverage" echo " ✅ Unit tests with coverage"
echo " ✅ Binary build" echo " ✅ Binary build"
echo " ✅ Version bump simulation" echo " ✅ Version bump simulation"
if [ "$HAS_DOCKER" = true ]; then echo " ✅ Docker build (development and/or production if chosen)"
echo " ✅ Docker build (if chosen)"
fi
echo "" echo ""
echo "🎯 When ready for production:" echo "🎯 When ready for production:"
echo " Push to main branch to trigger full CI/CD pipeline" echo " Push to main branch to trigger full CI/CD pipeline"
echo " Docker image will be built and pushed to Gitea Container Registry" echo " Docker image will be built and pushed to Gitea Container Registry"
echo "" 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 #!/bin/bash
# DanceLessonsCoach OpenTelemetry Test Script # dance-lessons-coach OpenTelemetry Test Script
# This script tests OpenTelemetry integration with Jaeger # This script tests OpenTelemetry integration with Jaeger
set -e 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 "" echo ""
# Configuration # Configuration
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach" PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach"
SERVER_CMD="./scripts/start-server.sh" SERVER_CMD="./scripts/start-server.sh"
LOG_FILE="server.log" LOG_FILE="server.log"
PID_FILE="server.pid" PID_FILE="server.pid"
@@ -47,7 +47,7 @@ fi
echo "Starting server with OpenTelemetry enabled..." echo "Starting server with OpenTelemetry enabled..."
DLC_TELEMETRY_ENABLED=true DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317" DLC_TELEMETRY_INSECURE=true \ 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 sleep 3
echo "Testing API endpoints..." echo "Testing API endpoints..."
@@ -77,7 +77,7 @@ echo -e "\033[0;32m✅ OpenTelemetry Test Complete!\033[0m"
echo "" echo ""
echo "To view traces in Jaeger:" echo "To view traces in Jaeger:"
echo "1. Open http://localhost:16686 in your browser" 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 "3. Click 'Find Traces' button"
echo "" echo ""
echo "You should see traces for:" 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 " 1. Test with Docker: ./scripts/test-cicd-simple.sh"
echo " 2. Commit changes: git commit -m '🤖 ci: validate workflow'" echo " 2. Commit changes: git commit -m '🤖 ci: validate workflow'"
echo " 3. Push to trigger: git push origin main" 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 #!/bin/bash
# DanceLessonsCoach Version Bump Script # dance-lessons-coach Version Bump Script
# Usage: ./scripts/version-bump.sh [major|minor|patch|pre|release] # Usage: ./scripts/version-bump.sh [major|minor|patch|pre|release]
set -e set -e
@@ -81,7 +81,7 @@ echo "🔜 New version: $NEW_VERSION"
# Update VERSION file # Update VERSION file
cat > "$VERSION_FILE" << VERSION_EOF cat > "$VERSION_FILE" << VERSION_EOF
# DanceLessonsCoach Version # dance-lessons-coach Version
# Current Version (Semantic Versioning) # Current Version (Semantic Versioning)
MAJOR=$MAJOR MAJOR=$MAJOR
@@ -139,7 +139,7 @@ if [ -f "$README_MD" ]; then
# Use awk to update version badge # Use awk to update version badge
awk -v new_version="$NEW_VERSION" '{ awk -v new_version="$NEW_VERSION" '{
if ($0 ~ /Version.*badge.*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 { } else {
print $0 print $0
} }