📝 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:
@@ -1,6 +1,6 @@
|
||||
# Git Hooks for DanceLessonsCoach
|
||||
# Git Hooks for dance-lessons-coach
|
||||
|
||||
This directory contains Git hooks for the DanceLessonsCoach project.
|
||||
This directory contains Git hooks for the dance-lessons-coach project.
|
||||
|
||||
## Available Hooks
|
||||
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -26,3 +26,6 @@ pkg/server/docs/
|
||||
# CI/CD runner configuration
|
||||
config/runner
|
||||
.runner
|
||||
coverage.txt
|
||||
trigger.txt
|
||||
test_trigger.txt
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# DanceLessonsCoach YAML Lint Configuration
|
||||
# dance-lessons-coach YAML Lint Configuration
|
||||
# More practical limits for CI/CD workflow files
|
||||
|
||||
extends: default
|
||||
|
||||
38
AGENTS.md
38
AGENTS.md
@@ -1,10 +1,10 @@
|
||||
# DanceLessonsCoach - AI Agent Documentation
|
||||
# dance-lessons-coach - AI Agent Documentation
|
||||
|
||||
This file documents the AI agents, tools, and development workflow for the DanceLessonsCoach project.
|
||||
This file documents the AI agents, tools, and development workflow for the dance-lessons-coach project.
|
||||
|
||||
## 🎯 Project Overview
|
||||
|
||||
**DanceLessonsCoach** is a Go-based web service with CLI capabilities, featuring:
|
||||
**dance-lessons-coach** is a Go-based web service with CLI capabilities, featuring:
|
||||
- RESTful JSON API with Chi router
|
||||
- High-performance Zerolog logging
|
||||
- Interface-based architecture
|
||||
@@ -94,7 +94,7 @@ This file documents the AI agents, tools, and development workflow for the Dance
|
||||
## 🗺️ Project Structure
|
||||
|
||||
```
|
||||
DanceLessonsCoach/
|
||||
dance-lessons-coach/
|
||||
├── adr/ # Architecture Decision Records
|
||||
│ ├── README.md # ADR guidelines and index
|
||||
│ ├── 0001-go-1.26.1-standard.md
|
||||
@@ -138,7 +138,7 @@ DanceLessonsCoach/
|
||||
|
||||
### New Cobra CLI (Recommended)
|
||||
|
||||
DanceLessonsCoach now includes a modern CLI built with Cobra framework:
|
||||
dance-lessons-coach now includes a modern CLI built with Cobra framework:
|
||||
|
||||
```bash
|
||||
# Show help and available commands
|
||||
@@ -156,7 +156,7 @@ DanceLessonsCoach now includes a modern CLI built with Cobra framework:
|
||||
|
||||
**Available Commands:**
|
||||
- `version` - Print version information
|
||||
- `server` - Start the DanceLessonsCoach server
|
||||
- `server` - Start the dance-lessons-coach server
|
||||
- `greet [name]` - Greet someone by name
|
||||
- `help` - Built-in help system
|
||||
- `completion` - Generate shell completion scripts
|
||||
@@ -178,7 +178,7 @@ The server provides runtime version information:
|
||||
./bin/server --version
|
||||
|
||||
# Output:
|
||||
DanceLessonsCoach Version Information:
|
||||
dance-lessons-coach Version Information:
|
||||
Version: 1.0.0
|
||||
Commit: abc1234
|
||||
Built: 2026-04-05T10:00:00+0000
|
||||
@@ -191,7 +191,7 @@ A convenient shell script is provided for managing the server lifecycle:
|
||||
|
||||
```bash
|
||||
# Navigate to project directory
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
|
||||
|
||||
# Start the server
|
||||
./scripts/start-server.sh start
|
||||
@@ -223,7 +223,7 @@ If you prefer manual control:
|
||||
|
||||
```bash
|
||||
# Navigate to project directory
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
|
||||
|
||||
# Run server in background using control script
|
||||
./scripts/start-server.sh start
|
||||
@@ -535,7 +535,7 @@ Enable OpenTelemetry in your `config.yaml`:
|
||||
telemetry:
|
||||
enabled: true
|
||||
otlp_endpoint: "localhost:4317"
|
||||
service_name: "DanceLessonsCoach"
|
||||
service_name: "dance-lessons-coach"
|
||||
insecure: true
|
||||
sampler:
|
||||
type: "parentbased_always_on"
|
||||
@@ -547,7 +547,7 @@ Or via environment variables:
|
||||
```bash
|
||||
export DLC_TELEMETRY_ENABLED=true
|
||||
export DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317"
|
||||
export DLC_TELEMETRY_SERVICE_NAME="DanceLessonsCoach"
|
||||
export DLC_TELEMETRY_SERVICE_NAME="dance-lessons-coach"
|
||||
export DLC_TELEMETRY_INSECURE=true
|
||||
export DLC_TELEMETRY_SAMPLER_TYPE="parentbased_always_on"
|
||||
export DLC_TELEMETRY_SAMPLER_RATIO=1.0
|
||||
@@ -579,7 +579,7 @@ curl http://localhost:8080/api/v1/greet/John
|
||||
```
|
||||
|
||||
4. **View traces in Jaeger UI:**
|
||||
Open http://localhost:16686 and select the "DanceLessonsCoach" service.
|
||||
Open http://localhost:16686 and select the "dance-lessons-coach" service.
|
||||
|
||||
### Sampler Types
|
||||
|
||||
@@ -613,7 +613,7 @@ curl -s http://localhost:8080/api/health
|
||||
|
||||
### 2. Start Development Server
|
||||
```bash
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
|
||||
./scripts/start-server.sh start
|
||||
```
|
||||
|
||||
@@ -927,7 +927,7 @@ defer cancel()
|
||||
|
||||
## 📦 Version Management
|
||||
|
||||
DanceLessonsCoach uses a comprehensive version management system based on Semantic Versioning 2.0.0.
|
||||
dance-lessons-coach uses a comprehensive version management system based on Semantic Versioning 2.0.0.
|
||||
|
||||
### Version Information
|
||||
|
||||
@@ -990,9 +990,9 @@ curl http://localhost:8080/api/version
|
||||
# Release build
|
||||
go build -o bin/server \
|
||||
-ldflags="\
|
||||
-X 'DanceLessonsCoach/pkg/version.Version=1.0.0' \
|
||||
-X 'DanceLessonsCoach/pkg/version.Commit=$(git rev-parse --short HEAD)' \
|
||||
-X 'DanceLessonsCoach/pkg/version.Date=$(date +%Y-%m-%dT%H:%M:%S%z)' \
|
||||
-X 'dance-lessons-coach/pkg/version.Version=1.0.0' \
|
||||
-X 'dance-lessons-coach/pkg/version.Commit=$(git rev-parse --short HEAD)' \
|
||||
-X 'dance-lessons-coach/pkg/version.Date=$(date +%Y-%m-%dT%H:%M:%S%z)' \
|
||||
" \
|
||||
./cmd/server
|
||||
```
|
||||
@@ -1034,7 +1034,7 @@ The `pkg/version` package provides runtime access to version information:
|
||||
package main
|
||||
|
||||
import (
|
||||
"DanceLessonsCoach/pkg/version"
|
||||
"dance-lessons-coach/pkg/version"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
@@ -1267,7 +1267,7 @@ For issues or questions:
|
||||
4. Consult Go and Chi documentation
|
||||
5. Ask the AI agent for guidance
|
||||
|
||||
This documentation provides a complete guide to developing, testing, and maintaining the DanceLessonsCoach project using the established patterns and best practices.
|
||||
This documentation provides a complete guide to developing, testing, and maintaining the dance-lessons-coach project using the established patterns and best practices.
|
||||
## 📋 BDD Feature Structure
|
||||
|
||||
All user stories and BDD features follow the structure defined in ADR-0019:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Contributing to DanceLessonsCoach
|
||||
# Contributing to dance-lessons-coach
|
||||
|
||||
Thank you for your interest in contributing to DanceLessonsCoach! This guide will help you set up your development environment and understand our contribution process.
|
||||
Thank you for your interest in contributing to dance-lessons-coach! This guide will help you set up your development environment and understand our contribution process.
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
@@ -24,8 +24,8 @@ Thank you for your interest in contributing to DanceLessonsCoach! This guide wil
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://gitea.arcodange.lab/arcodange/DanceLessonsCoach.git
|
||||
cd DanceLessonsCoach
|
||||
git clone https://gitea.arcodange.lab/arcodange/dance-lessons-coach.git
|
||||
cd dance-lessons-coach
|
||||
|
||||
# Install dependencies
|
||||
go mod tidy
|
||||
@@ -260,7 +260,7 @@ Major architectural decisions are documented in the `adr/` directory. Please rev
|
||||
|
||||
## 🤖 AI Agent Contributions
|
||||
|
||||
AI agents play a crucial role in maintaining and improving DanceLessonsCoach. This section provides guidance for AI agents on how to effectively contribute.
|
||||
AI agents play a crucial role in maintaining and improving dance-lessons-coach. This section provides guidance for AI agents on how to effectively contribute.
|
||||
|
||||
### Key Files and Directories
|
||||
|
||||
@@ -342,7 +342,7 @@ AI agents play a crucial role in maintaining and improving DanceLessonsCoach. Th
|
||||
|
||||
## 📜 License
|
||||
|
||||
By contributing to DanceLessonsCoach, you agree that your contributions will be licensed under the MIT License.
|
||||
By contributing to dance-lessons-coach, you agree that your contributions will be licensed under the MIT License.
|
||||
|
||||
---
|
||||
|
||||
@@ -350,7 +350,7 @@ By contributing to DanceLessonsCoach, you agree that your contributions will be
|
||||
=======
|
||||
## 🤖 AI Agent Contributions
|
||||
|
||||
AI agents play a crucial role in maintaining and improving DanceLessonsCoach. This section provides guidance for AI agents on how to effectively contribute.
|
||||
AI agents play a crucial role in maintaining and improving dance-lessons-coach. This section provides guidance for AI agents on how to effectively contribute.
|
||||
|
||||
### Key Files and Directories
|
||||
|
||||
@@ -432,7 +432,7 @@ AI agents play a crucial role in maintaining and improving DanceLessonsCoach. Th
|
||||
|
||||
## 📜 License
|
||||
|
||||
By contributing to DanceLessonsCoach, you agree that your contributions will be licensed under the MIT License.
|
||||
By contributing to dance-lessons-coach, you agree that your contributions will be licensed under the MIT License.
|
||||
|
||||
---
|
||||
|
||||
|
||||
82
README.md
82
README.md
@@ -1,9 +1,11 @@
|
||||
# DanceLessonsCoach
|
||||
# dance-lessons-coach
|
||||
|
||||
[](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach)
|
||||
[](https://goreportcard.com/report/github.com/arcodange/DanceLessonsCoach)
|
||||
[](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach/releases)
|
||||
[](https://gitea.arcodange.fr/arcodange/dance-lessons-coach)
|
||||
[](https://goreportcard.com/report/github.com/arcodange/dance-lessons-coach)
|
||||
[](https://gitea.arcodange.fr/arcodange/dance-lessons-coach/releases)
|
||||
[](LICENSE)
|
||||
[](https://gitea.arcodange.lab/arcodange/dance-lessons-coach)
|
||||
[](https://gitea.arcodange.lab/arcodange/dance-lessons-coach)
|
||||
|
||||
A Go project demonstrating idiomatic package structure, CLI implementation, and JSON API with Chi router.
|
||||
=======
|
||||
@@ -42,11 +44,69 @@ go run ./cmd/greet
|
||||
|
||||
## CI/CD Pipeline
|
||||
|
||||
DanceLessonsCoach includes a portable CI/CD pipeline using GitHub Actions syntax:
|
||||
dance-lessons-coach features an optimized CI/CD pipeline using GitHub Actions with container/services architecture:
|
||||
|
||||
### Features
|
||||
- ✅ **Multi-platform**: Works on Gitea, GitHub, and GitLab
|
||||
- ✅ **Build & Test**: Automated Go builds and tests
|
||||
### Key Features
|
||||
- ✅ **Container-based execution**: All steps run in pre-built Docker cache images
|
||||
- ✅ **Service-based PostgreSQL**: Automatic database service provisioning
|
||||
- ✅ **Smart caching**: Dependency-aware cache invalidation
|
||||
- ✅ **Multi-platform**: Compatible with Gitea, GitHub, and GitLab
|
||||
- ✅ **Fast execution**: No Docker Compose overhead
|
||||
- ✅ **Reliable testing**: Full database connectivity with proper environment setup
|
||||
|
||||
### Architecture
|
||||
|
||||
The pipeline uses GitHub Actions' native `container` and `services` directives instead of Docker Compose:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
ci-pipeline:
|
||||
container:
|
||||
image: gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:${{ needs.build-cache.outputs.deps_hash }}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: dance_lessons_coach_bdd_test
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
1. **Performance**: Direct container execution without compose overhead
|
||||
2. **Reliability**: Service containers managed by GitHub Actions
|
||||
3. **Simplicity**: Cleaner workflow definition
|
||||
4. **Portability**: Works across CI platforms
|
||||
5. **Caching**: Intelligent dependency-based cache rebuilding
|
||||
|
||||
### Workflow Steps
|
||||
|
||||
1. **Build Cache**: Creates Docker image with Go tools and dependencies
|
||||
2. **CI Pipeline**: Runs tests, builds binaries, and generates documentation
|
||||
3. **Database Tests**: Connects to PostgreSQL service container
|
||||
4. **Coverage Reporting**: Updates coverage badges automatically
|
||||
5. **Artifact Publishing**: Builds and pushes Docker images (main branch only)
|
||||
|
||||
### Environment Configuration
|
||||
|
||||
The pipeline automatically sets up database environment variables:
|
||||
|
||||
```bash
|
||||
echo "DLC_DATABASE_HOST=postgres" >> $GITHUB_ENV
|
||||
echo "DLC_DATABASE_PORT=5432" >> $GITHUB_ENV
|
||||
echo "DLC_DATABASE_USER=postgres" >> $GITHUB_ENV
|
||||
echo "DLC_DATABASE_PASSWORD=postgres" >> $GITHUB_ENV
|
||||
echo "DLC_DATABASE_NAME=dance_lessons_coach_bdd_test" >> $GITHUB_ENV
|
||||
echo "DLC_DATABASE_SSL_MODE=disable" >> $GITHUB_ENV
|
||||
```
|
||||
|
||||
### Status
|
||||
|
||||
[](https://gitea.arcodange.fr/arcodange/dance-lessons-coach)
|
||||
|
||||
=======
|
||||
- ✅ **Linting**: Code quality checks with `go fmt` and `go vet`
|
||||
- ✅ **Version Management**: Automatic version detection
|
||||
- ✅ **Portable**: Uses standard GitHub Actions workflow format
|
||||
@@ -184,7 +244,7 @@ go test ./pkg/greet/
|
||||
|
||||
## CI/CD
|
||||
|
||||
DanceLessonsCoach includes a comprehensive CI/CD pipeline with multiple testing options:
|
||||
dance-lessons-coach includes a comprehensive CI/CD pipeline with multiple testing options:
|
||||
|
||||
### Local Testing (No Gitea Required)
|
||||
```bash
|
||||
@@ -215,7 +275,7 @@ DanceLessonsCoach includes a comprehensive CI/CD pipeline with multiple testing
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
DanceLessonsCoach/
|
||||
dance-lessons-coach/
|
||||
├── adr/ # Architecture Decision Records
|
||||
├── cmd/ # Entry points (greet CLI, server)
|
||||
├── pkg/ # Core packages (config, greet, server, telemetry)
|
||||
@@ -273,7 +333,7 @@ This project uses Architecture Decision Records (ADRs) to document key technical
|
||||
|
||||
## Gitea Integration
|
||||
|
||||
DanceLessonsCoach includes AI agent skills for Gitea integration to monitor CI/CD jobs and interact with pull requests.
|
||||
dance-lessons-coach includes AI agent skills for Gitea integration to monitor CI/CD jobs and interact with pull requests.
|
||||
|
||||
### Gitea Client Skill Setup
|
||||
|
||||
|
||||
2
VERSION
2
VERSION
@@ -1,4 +1,4 @@
|
||||
# DanceLessonsCoach Version
|
||||
# dance-lessons-coach Version
|
||||
|
||||
# Current Version (Semantic Versioning)
|
||||
MAJOR=1
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# DanceLessonsCoach Agent Usage Guide
|
||||
# dance-lessons-coach Agent Usage Guide
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Launch Programmer Agent
|
||||
```bash
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
|
||||
vibe start --agent dancelessonscoachprogrammer
|
||||
```
|
||||
|
||||
### Launch Product Owner Agent
|
||||
```bash
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
|
||||
vibe start --agent dancelessonscoach-product-owner
|
||||
```
|
||||
|
||||
@@ -141,7 +141,7 @@ skill changelog-manager add-entry \
|
||||
```toml
|
||||
# .mistral/dancelessonscoachprogrammer-agent.toml
|
||||
name: dancelessonscoachprogrammer
|
||||
role: DanceLessonsCoachProgrammer
|
||||
role: dance-lessons-coach-programmer
|
||||
goals: ["Follow BDD practices", "Use Gitmoji commits", "Respect ADR process"]
|
||||
```
|
||||
|
||||
@@ -149,7 +149,7 @@ goals: ["Follow BDD practices", "Use Gitmoji commits", "Respect ADR process"]
|
||||
```toml
|
||||
# .mistral/dancelessonscoach-product-owner-agent.toml
|
||||
name: dancelessonscoach-product-owner
|
||||
role: DanceLessonsCoachProductOwner
|
||||
role: dance-lessons-coach-product-owner
|
||||
goals: ["Facilitate stakeholder interviews", "Generate BDD tests", "Maintain documentation"]
|
||||
```
|
||||
|
||||
@@ -210,7 +210,7 @@ vibe validate --agent dancelessonscoach-product-owner
|
||||
```bash
|
||||
# List available skills
|
||||
ls /Users/gabrielradureau/Work/Vibe/.mistral/skills/
|
||||
ls /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach/.vibe/skills/
|
||||
ls /Users/gabrielradureau/Work/Vibe/dance-lessons-coach/.vibe/skills/
|
||||
|
||||
# Validate skill
|
||||
skill skill-creator validate .vibe/skills/product-owner-assistant
|
||||
@@ -222,7 +222,7 @@ skill skill-creator validate .mistral/skills/interview-facilitator
|
||||
```bash
|
||||
# Check file permissions
|
||||
chmod +x /Users/gabrielradureau/Work/Vibe/.mistral/skills/*/scripts/*
|
||||
chmod +x /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach/.vibe/skills/*/scripts/*
|
||||
chmod +x /Users/gabrielradureau/Work/Vibe/dance-lessons-coach/.vibe/skills/*/scripts/*
|
||||
```
|
||||
|
||||
## 📖 Related Documentation
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# BDD Testing Guide for DanceLessonsCoach
|
||||
# BDD Testing Guide for dance-lessons-coach
|
||||
|
||||
This guide explains how to work with BDD tests using Godog in the DanceLessonsCoach project.
|
||||
This guide explains how to work with BDD tests using Godog in the dance-lessons-coach project.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -33,7 +33,7 @@ The project already includes Godog as a dependency in `go.mod`. The BDD tests ar
|
||||
|
||||
```bash
|
||||
# From project root
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
|
||||
go test ./features/... -v
|
||||
```
|
||||
|
||||
@@ -112,7 +112,7 @@ Create a corresponding step definition file in `pkg/bdd/steps/`:
|
||||
package steps
|
||||
|
||||
import (
|
||||
"DanceLessonsCoach/pkg/bdd/testserver"
|
||||
"dance-lessons-coach/pkg/bdd/testserver"
|
||||
"github.com/cucumber/godog"
|
||||
)
|
||||
|
||||
@@ -213,7 +213,7 @@ Add BDD tests to your CI pipeline:
|
||||
|
||||
## Modern Go Testing Practices
|
||||
|
||||
The DanceLessonsCoach project follows modern Go testing practices:
|
||||
The dance-lessons-coach project follows modern Go testing practices:
|
||||
|
||||
1. **Standard library integration**: BDD tests use `go test`
|
||||
2. **No global installation required**: Godog is a Go module dependency
|
||||
|
||||
@@ -69,7 +69,7 @@ This workflow can be triggered manually or on test/feature branches.
|
||||
### 1. Run the Interactive Script
|
||||
|
||||
```bash
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
cd /Users/gabrielradureau/Work/Vibe/dance-lessons-coach
|
||||
./scripts/test-local-ci-cd.sh
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Version Management Guide
|
||||
|
||||
This guide provides comprehensive instructions for managing versions in the DanceLessonsCoach project.
|
||||
This guide provides comprehensive instructions for managing versions in the dance-lessons-coach project.
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
@@ -13,7 +13,7 @@ This guide provides comprehensive instructions for managing versions in the Danc
|
||||
|
||||
## 📖 Semantic Versioning
|
||||
|
||||
DanceLessonsCoach follows [Semantic Versioning 2.0.0](https://semver.org/):
|
||||
dance-lessons-coach follows [Semantic Versioning 2.0.0](https://semver.org/):
|
||||
|
||||
### Version Format: `MAJOR.MINOR.PATCH-PRERELEASE`
|
||||
|
||||
@@ -360,6 +360,6 @@ git push origin v1.0.1
|
||||
|
||||
---
|
||||
|
||||
**Maintained by:** DanceLessonsCoach Team
|
||||
**Maintained by:** dance-lessons-coach Team
|
||||
**Last Updated:** 2026-04-05
|
||||
**Version:** 1.0
|
||||
@@ -13,6 +13,11 @@ import (
|
||||
"dance-lessons-coach/pkg/version"
|
||||
)
|
||||
|
||||
// NewZerologWriter creates a zerolog writer based on configuration
|
||||
func NewZerologWriter() *os.File {
|
||||
return os.Stderr
|
||||
}
|
||||
|
||||
// Config represents the application configuration
|
||||
type Config struct {
|
||||
Server ServerConfig `mapstructure:"server"`
|
||||
@@ -20,6 +25,8 @@ type Config struct {
|
||||
Logging LoggingConfig `mapstructure:"logging"`
|
||||
Telemetry TelemetryConfig `mapstructure:"telemetry"`
|
||||
API APIConfig `mapstructure:"api"`
|
||||
Auth AuthConfig `mapstructure:"auth"`
|
||||
Database DatabaseConfig `mapstructure:"database"`
|
||||
}
|
||||
|
||||
// ServerConfig holds server-related configuration
|
||||
@@ -42,11 +49,17 @@ type LoggingConfig struct {
|
||||
|
||||
// TelemetryConfig holds OpenTelemetry-related configuration
|
||||
type TelemetryConfig struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
OTLPEndpoint string `mapstructure:"otlp_endpoint"`
|
||||
ServiceName string `mapstructure:"service_name"`
|
||||
Insecure bool `mapstructure:"insecure"`
|
||||
Sampler SamplerConfig `mapstructure:"sampler"`
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
OTLPEndpoint string `mapstructure:"otlp_endpoint"`
|
||||
ServiceName string `mapstructure:"service_name"`
|
||||
Insecure bool `mapstructure:"insecure"`
|
||||
Sampler SamplerConfig `mapstructure:"sampler"`
|
||||
Persistence PersistenceTelemetryConfig `mapstructure:"persistence"`
|
||||
}
|
||||
|
||||
// PersistenceTelemetryConfig holds persistence layer telemetry configuration
|
||||
type PersistenceTelemetryConfig struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
}
|
||||
|
||||
// APIConfig holds API version configuration
|
||||
@@ -54,6 +67,25 @@ type APIConfig struct {
|
||||
V2Enabled bool `mapstructure:"v2_enabled"`
|
||||
}
|
||||
|
||||
// AuthConfig holds authentication configuration
|
||||
type AuthConfig struct {
|
||||
JWTSecret string `mapstructure:"jwt_secret"`
|
||||
AdminMasterPassword string `mapstructure:"admin_master_password"`
|
||||
}
|
||||
|
||||
// DatabaseConfig holds database configuration
|
||||
type DatabaseConfig struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Port int `mapstructure:"port"`
|
||||
User string `mapstructure:"user"`
|
||||
Password string `mapstructure:"password"`
|
||||
Name string `mapstructure:"name"`
|
||||
SSLMode string `mapstructure:"ssl_mode"`
|
||||
MaxOpenConns int `mapstructure:"max_open_conns"`
|
||||
MaxIdleConns int `mapstructure:"max_idle_conns"`
|
||||
ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"`
|
||||
}
|
||||
|
||||
// VersionInfo holds application version information
|
||||
type VersionInfo struct {
|
||||
Version string `mapstructure:"-"` // Set via ldflags
|
||||
@@ -65,7 +97,7 @@ type VersionInfo struct {
|
||||
// VersionCommand handles version display
|
||||
func (c *Config) VersionCommand() string {
|
||||
// This will be enhanced when we integrate with cobra
|
||||
return fmt.Sprintf("DanceLessonsCoach %s (commit: %s, built: %s, go: %s)",
|
||||
return fmt.Sprintf("dance-lessons-coach %s (commit: %s, built: %s, go: %s)",
|
||||
version.Version, version.Commit, version.Date, version.GoVersion)
|
||||
}
|
||||
|
||||
@@ -96,14 +128,19 @@ func LoadConfig() (*Config, error) {
|
||||
// Telemetry defaults
|
||||
v.SetDefault("telemetry.enabled", false)
|
||||
v.SetDefault("telemetry.otlp_endpoint", "localhost:4317")
|
||||
v.SetDefault("telemetry.service_name", "DanceLessonsCoach")
|
||||
v.SetDefault("telemetry.service_name", "dance-lessons-coach")
|
||||
v.SetDefault("telemetry.insecure", true)
|
||||
v.SetDefault("telemetry.sampler.type", "parentbased_always_on")
|
||||
v.SetDefault("telemetry.sampler.ratio", 1.0)
|
||||
v.SetDefault("telemetry.persistence.enabled", false)
|
||||
|
||||
// API defaults
|
||||
v.SetDefault("api.v2_enabled", false)
|
||||
|
||||
// Auth defaults
|
||||
v.SetDefault("auth.jwt_secret", "default-secret-key-please-change-in-production")
|
||||
v.SetDefault("auth.admin_master_password", "admin123")
|
||||
|
||||
// Check for custom config file path via environment variable
|
||||
if configFile := os.Getenv("DLC_CONFIG_FILE"); configFile != "" {
|
||||
v.SetConfigFile(configFile)
|
||||
@@ -128,7 +165,7 @@ func LoadConfig() (*Config, error) {
|
||||
|
||||
// Bind environment variables
|
||||
v.AutomaticEnv()
|
||||
v.SetEnvPrefix("DLC") // DanceLessonsCoach prefix
|
||||
v.SetEnvPrefix("DLC") // dance-lessons-coach prefix
|
||||
v.BindEnv("server.host", "DLC_SERVER_HOST")
|
||||
v.BindEnv("server.port", "DLC_SERVER_PORT")
|
||||
v.BindEnv("shutdown.timeout", "DLC_SHUTDOWN_TIMEOUT")
|
||||
@@ -141,12 +178,24 @@ func LoadConfig() (*Config, error) {
|
||||
v.BindEnv("telemetry.otlp_endpoint", "DLC_TELEMETRY_OTLP_ENDPOINT")
|
||||
v.BindEnv("telemetry.service_name", "DLC_TELEMETRY_SERVICE_NAME")
|
||||
v.BindEnv("telemetry.insecure", "DLC_TELEMETRY_INSECURE")
|
||||
|
||||
// Auth environment variables
|
||||
v.BindEnv("auth.jwt_secret", "DLC_AUTH_JWT_SECRET")
|
||||
v.BindEnv("auth.admin_master_password", "DLC_AUTH_ADMIN_MASTER_PASSWORD")
|
||||
v.BindEnv("telemetry.sampler.type", "DLC_TELEMETRY_SAMPLER_TYPE")
|
||||
v.BindEnv("telemetry.sampler.ratio", "DLC_TELEMETRY_SAMPLER_RATIO")
|
||||
|
||||
// API environment variables
|
||||
v.BindEnv("api.v2_enabled", "DLC_API_V2_ENABLED")
|
||||
|
||||
// Database environment variables
|
||||
v.BindEnv("database.host", "DLC_DATABASE_HOST")
|
||||
v.BindEnv("database.port", "DLC_DATABASE_PORT")
|
||||
v.BindEnv("database.user", "DLC_DATABASE_USER")
|
||||
v.BindEnv("database.password", "DLC_DATABASE_PASSWORD")
|
||||
v.BindEnv("database.name", "DLC_DATABASE_NAME")
|
||||
v.BindEnv("database.ssl_mode", "DLC_DATABASE_SSL_MODE")
|
||||
|
||||
// Unmarshal into Config struct
|
||||
var config Config
|
||||
if err := v.Unmarshal(&config); err != nil {
|
||||
@@ -200,6 +249,11 @@ func (c *Config) GetServiceName() string {
|
||||
return c.Telemetry.ServiceName
|
||||
}
|
||||
|
||||
// GetPersistenceTelemetryEnabled returns whether persistence layer telemetry is enabled
|
||||
func (c *Config) GetPersistenceTelemetryEnabled() bool {
|
||||
return c.Telemetry.Enabled && c.Telemetry.Persistence.Enabled
|
||||
}
|
||||
|
||||
// GetTelemetryInsecure returns whether to use insecure connection
|
||||
func (c *Config) GetTelemetryInsecure() bool {
|
||||
return c.Telemetry.Insecure
|
||||
@@ -220,6 +274,21 @@ func (c *Config) GetV2Enabled() bool {
|
||||
return c.API.V2Enabled
|
||||
}
|
||||
|
||||
// GetJWTSecret returns the JWT secret
|
||||
func (c *Config) GetJWTSecret() string {
|
||||
return c.Auth.JWTSecret
|
||||
}
|
||||
|
||||
// GetAdminMasterPassword returns the admin master password
|
||||
func (c *Config) GetAdminMasterPassword() string {
|
||||
return c.Auth.AdminMasterPassword
|
||||
}
|
||||
|
||||
// GetLoggingJSON returns whether JSON logging is enabled
|
||||
func (c *Config) GetLoggingJSON() bool {
|
||||
return c.Logging.JSON
|
||||
}
|
||||
|
||||
// GetLogLevel returns the logging level
|
||||
func (c *Config) GetLogLevel() string {
|
||||
return c.Logging.Level
|
||||
@@ -230,6 +299,75 @@ func (c *Config) GetLogOutput() string {
|
||||
return c.Logging.Output
|
||||
}
|
||||
|
||||
// GetDatabaseHost returns the database host
|
||||
func (c *Config) GetDatabaseHost() string {
|
||||
if c.Database.Host == "" {
|
||||
return "localhost"
|
||||
}
|
||||
return c.Database.Host
|
||||
}
|
||||
|
||||
// GetDatabasePort returns the database port
|
||||
func (c *Config) GetDatabasePort() int {
|
||||
if c.Database.Port == 0 {
|
||||
return 5432
|
||||
}
|
||||
return c.Database.Port
|
||||
}
|
||||
|
||||
// GetDatabaseUser returns the database user
|
||||
func (c *Config) GetDatabaseUser() string {
|
||||
if c.Database.User == "" {
|
||||
return "postgres"
|
||||
}
|
||||
return c.Database.User
|
||||
}
|
||||
|
||||
// GetDatabasePassword returns the database password
|
||||
func (c *Config) GetDatabasePassword() string {
|
||||
return c.Database.Password
|
||||
}
|
||||
|
||||
// GetDatabaseName returns the database name
|
||||
func (c *Config) GetDatabaseName() string {
|
||||
if c.Database.Name == "" {
|
||||
return "dance_lessons_coach"
|
||||
}
|
||||
return c.Database.Name
|
||||
}
|
||||
|
||||
// GetDatabaseSSLMode returns the database SSL mode
|
||||
func (c *Config) GetDatabaseSSLMode() string {
|
||||
if c.Database.SSLMode == "" {
|
||||
return "disable"
|
||||
}
|
||||
return c.Database.SSLMode
|
||||
}
|
||||
|
||||
// GetDatabaseMaxOpenConns returns the maximum number of open connections
|
||||
func (c *Config) GetDatabaseMaxOpenConns() int {
|
||||
if c.Database.MaxOpenConns == 0 {
|
||||
return 25
|
||||
}
|
||||
return c.Database.MaxOpenConns
|
||||
}
|
||||
|
||||
// GetDatabaseMaxIdleConns returns the maximum number of idle connections
|
||||
func (c *Config) GetDatabaseMaxIdleConns() int {
|
||||
if c.Database.MaxIdleConns == 0 {
|
||||
return 5
|
||||
}
|
||||
return c.Database.MaxIdleConns
|
||||
}
|
||||
|
||||
// GetDatabaseConnMaxLifetime returns the maximum lifetime of connections
|
||||
func (c *Config) GetDatabaseConnMaxLifetime() time.Duration {
|
||||
if c.Database.ConnMaxLifetime == 0 {
|
||||
return time.Hour
|
||||
}
|
||||
return c.Database.ConnMaxLifetime
|
||||
}
|
||||
|
||||
// SetupLogging configures zerolog based on the configuration
|
||||
func (c *Config) SetupLogging() {
|
||||
// Parse log level
|
||||
|
||||
@@ -88,6 +88,7 @@ func (h *apiV1GreetHandler) RegisterRoutes(router chi.Router) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} GreetResponse "Successful response"
|
||||
// @Security BearerAuth
|
||||
// @Router /v1/greet [get]
|
||||
func (h *apiV1GreetHandler) handleGreetQuery(w http.ResponseWriter, r *http.Request) {
|
||||
name := r.URL.Query().Get("name")
|
||||
@@ -104,6 +105,7 @@ func (h *apiV1GreetHandler) handleGreetQuery(w http.ResponseWriter, r *http.Requ
|
||||
// @Param name path string true "Name to greet"
|
||||
// @Success 200 {object} GreetResponse "Successful response"
|
||||
// @Failure 400 {object} ErrorResponse "Invalid name parameter"
|
||||
// @Security BearerAuth
|
||||
// @Router /v1/greet/{name} [get]
|
||||
func (h *apiV1GreetHandler) handleGreetPath(w http.ResponseWriter, r *http.Request) {
|
||||
name := chi.URLParam(r, "name")
|
||||
|
||||
@@ -55,6 +55,7 @@ type greetResponse struct {
|
||||
// @Param request body GreetRequest true "Greeting request"
|
||||
// @Success 200 {object} GreetResponseV2 "Successful response"
|
||||
// @Failure 400 {object} ValidationError "Validation error"
|
||||
// @Security BearerAuth
|
||||
// @Router /v2/greet [post]
|
||||
func (h *apiV2GreetHandler) handleGreetPost(w http.ResponseWriter, r *http.Request) {
|
||||
// Read request body
|
||||
|
||||
@@ -3,21 +3,46 @@ package greet
|
||||
import (
|
||||
"context"
|
||||
|
||||
"dance-lessons-coach/pkg/user"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Context key for storing authenticated user
|
||||
type contextKey string
|
||||
|
||||
const (
|
||||
// UserContextKey is the context key for storing authenticated user
|
||||
UserContextKey contextKey = "authenticatedUser"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
// GetAuthenticatedUserFromContext extracts the authenticated user from context
|
||||
func GetAuthenticatedUserFromContext(ctx context.Context) (*user.User, bool) {
|
||||
user, ok := ctx.Value(UserContextKey).(*user.User)
|
||||
return user, ok
|
||||
}
|
||||
|
||||
// Greet returns a greeting message for the given name.
|
||||
// If name is empty, it defaults to "world".
|
||||
// If name is empty, it checks for authenticated user and uses their username.
|
||||
// If no authenticated user and no name, it defaults to "world".
|
||||
// Implements the Greeter interface.
|
||||
func (s *Service) Greet(ctx context.Context, name string) string {
|
||||
log.Trace().Ctx(ctx).Str("name", name).Msg("Greet function called")
|
||||
|
||||
// If no name provided, check for authenticated user
|
||||
if name == "" {
|
||||
if authenticatedUser, ok := GetAuthenticatedUserFromContext(ctx); ok {
|
||||
name = authenticatedUser.Username
|
||||
log.Trace().Ctx(ctx).Str("authenticated_user", name).Msg("Using authenticated username for greeting")
|
||||
}
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return "Hello world!"
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@ import (
|
||||
"dance-lessons-coach/pkg/config"
|
||||
"dance-lessons-coach/pkg/greet"
|
||||
"dance-lessons-coach/pkg/telemetry"
|
||||
"dance-lessons-coach/pkg/user"
|
||||
userapi "dance-lessons-coach/pkg/user/api"
|
||||
"dance-lessons-coach/pkg/validation"
|
||||
"dance-lessons-coach/pkg/version"
|
||||
"encoding/json"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
@@ -37,6 +40,8 @@ type Server struct {
|
||||
config *config.Config
|
||||
tracerProvider *sdktrace.TracerProvider
|
||||
validator *validation.Validator
|
||||
userRepo user.UserRepository
|
||||
userService user.UserService
|
||||
}
|
||||
|
||||
func NewServer(cfg *config.Config, readyCtx context.Context) *Server {
|
||||
@@ -48,17 +53,46 @@ func NewServer(cfg *config.Config, readyCtx context.Context) *Server {
|
||||
log.Trace().Msg("Validator created successfully")
|
||||
}
|
||||
|
||||
// Initialize user repository and services
|
||||
userRepo, userService, err := initializeUserServices(cfg)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to initialize user services, user functionality will be disabled")
|
||||
}
|
||||
|
||||
s := &Server{
|
||||
router: chi.NewRouter(),
|
||||
readyCtx: readyCtx,
|
||||
withOTEL: cfg.GetTelemetryEnabled(),
|
||||
config: cfg,
|
||||
validator: validator,
|
||||
router: chi.NewRouter(),
|
||||
readyCtx: readyCtx,
|
||||
withOTEL: cfg.GetTelemetryEnabled(),
|
||||
config: cfg,
|
||||
validator: validator,
|
||||
userRepo: userRepo,
|
||||
userService: userService,
|
||||
}
|
||||
s.setupRoutes()
|
||||
return s
|
||||
}
|
||||
|
||||
// initializeUserServices initializes the user repository and unified user service
|
||||
func initializeUserServices(cfg *config.Config) (user.UserRepository, user.UserService, error) {
|
||||
// Create user repository using PostgreSQL
|
||||
repo, err := user.NewPostgresRepository(cfg)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create PostgreSQL user repository: %w", err)
|
||||
}
|
||||
|
||||
// Create JWT config
|
||||
jwtConfig := user.JWTConfig{
|
||||
Secret: cfg.GetJWTSecret(),
|
||||
ExpirationTime: time.Hour * 24, // 24 hours
|
||||
Issuer: "dance-lessons-coach",
|
||||
}
|
||||
|
||||
// Create unified user service
|
||||
userService := user.NewUserService(repo, jwtConfig, cfg.GetAdminMasterPassword())
|
||||
|
||||
return repo, userService, nil
|
||||
}
|
||||
|
||||
func (s *Server) setupRoutes() {
|
||||
// Use Zerolog middleware instead of Chi's default logger
|
||||
s.router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{
|
||||
@@ -109,9 +143,31 @@ func (s *Server) setupRoutes() {
|
||||
func (s *Server) registerApiV1Routes(r chi.Router) {
|
||||
greetService := greet.NewService()
|
||||
greetHandler := greet.NewApiV1GreetHandler(greetService)
|
||||
|
||||
// Create auth middleware if available
|
||||
var authMiddleware *AuthMiddleware
|
||||
if s.userService != nil {
|
||||
authMiddleware = NewAuthMiddleware(s.userService)
|
||||
}
|
||||
|
||||
r.Route("/greet", func(r chi.Router) {
|
||||
// Add optional authentication middleware
|
||||
if authMiddleware != nil {
|
||||
r.Use(authMiddleware.Middleware)
|
||||
}
|
||||
greetHandler.RegisterRoutes(r)
|
||||
})
|
||||
|
||||
// Register user authentication routes
|
||||
if s.userService != nil && s.userRepo != nil {
|
||||
// Use unified user service - much simpler!
|
||||
if s.userService != nil {
|
||||
handler := userapi.NewAuthHandler(s.userService, s.userService, s.validator)
|
||||
r.Route("/auth", func(r chi.Router) {
|
||||
handler.RegisterRoutes(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) registerApiV2Routes(r chi.Router) {
|
||||
@@ -155,24 +211,75 @@ func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||
// handleReadiness godoc
|
||||
//
|
||||
// @Summary Readiness check
|
||||
// @Description Check if the service is ready to accept traffic
|
||||
// @Description Check if the service is ready to accept traffic including detailed connection status
|
||||
// @Tags System/Health
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]bool "Service is ready"
|
||||
// @Failure 503 {object} map[string]bool "Service is not ready"
|
||||
// @Success 200 {object} object "Service is ready with connection details"
|
||||
// @Failure 503 {object} object "Service is not ready with failure details"
|
||||
// @Router /ready [get]
|
||||
func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) {
|
||||
log.Trace().Msg("Readiness check requested")
|
||||
|
||||
// Check if server is shutting down
|
||||
select {
|
||||
case <-s.readyCtx.Done():
|
||||
log.Trace().Msg("Readiness check: not ready (shutting down)")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
w.Write([]byte(`{"ready":false}`))
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"ready": false,
|
||||
"reason": "server_shutting_down",
|
||||
"connections": map[string]interface{}{
|
||||
"database": "not_checked",
|
||||
},
|
||||
})
|
||||
return
|
||||
default:
|
||||
log.Trace().Msg("Readiness check: ready")
|
||||
w.Write([]byte(`{"ready":true}`))
|
||||
// Server is not shutting down, check all connections
|
||||
connectionStatus := make(map[string]interface{})
|
||||
allHealthy := true
|
||||
var failureReason string
|
||||
|
||||
// Check database if available
|
||||
if s.userRepo != nil {
|
||||
if err := s.userRepo.CheckDatabaseHealth(r.Context()); err != nil {
|
||||
log.Warn().Err(err).Msg("Database health check failed")
|
||||
connectionStatus["database"] = map[string]interface{}{
|
||||
"status": "unhealthy",
|
||||
"error": err.Error(),
|
||||
}
|
||||
allHealthy = false
|
||||
failureReason = "database_unhealthy"
|
||||
} else {
|
||||
connectionStatus["database"] = map[string]interface{}{
|
||||
"status": "healthy",
|
||||
}
|
||||
}
|
||||
} else {
|
||||
connectionStatus["database"] = map[string]interface{}{
|
||||
"status": "not_configured",
|
||||
}
|
||||
}
|
||||
|
||||
if allHealthy {
|
||||
log.Trace().Msg("Readiness check: ready")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"ready": true,
|
||||
"connections": connectionStatus,
|
||||
})
|
||||
} else {
|
||||
log.Warn().Str("reason", failureReason).Msg("Readiness check: not ready")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"ready": false,
|
||||
"reason": failureReason,
|
||||
"connections": connectionStatus,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Package telemetry provides OpenTelemetry instrumentation for the DanceLessonsCoach application
|
||||
// Package telemetry provides OpenTelemetry instrumentation for the dance-lessons-coach application
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Package version provides version information and management for DanceLessonsCoach
|
||||
// Package version provides version information and management for dance-lessons-coach
|
||||
package version
|
||||
|
||||
import (
|
||||
@@ -91,7 +91,7 @@ func getBuildDate() {
|
||||
|
||||
// Info returns formatted version information
|
||||
func Info() string {
|
||||
return fmt.Sprintf("DanceLessonsCoach %s (commit: %s, built: %s UTC, go: %s)", Version, Commit, Date, GoVersion)
|
||||
return fmt.Sprintf("dance-lessons-coach %s (commit: %s, built: %s UTC, go: %s)", Version, Commit, Date, GoVersion)
|
||||
}
|
||||
|
||||
// Short returns just the version number
|
||||
@@ -101,7 +101,7 @@ func Short() string {
|
||||
|
||||
// Full returns detailed version information
|
||||
func Full() string {
|
||||
return fmt.Sprintf(`DanceLessonsCoach Version Information:
|
||||
return fmt.Sprintf(`dance-lessons-coach Version Information:
|
||||
Version: %s
|
||||
Commit: %s
|
||||
Built: %s (UTC)
|
||||
|
||||
215
scripts/LOCAL_CI_GUIDE.md
Normal file
215
scripts/LOCAL_CI_GUIDE.md
Normal 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!
|
||||
@@ -1,6 +1,6 @@
|
||||
# DanceLessonsCoach Scripts
|
||||
# dance-lessons-coach Scripts
|
||||
|
||||
This directory contains automation and management scripts for the DanceLessonsCoach project.
|
||||
This directory contains automation and management scripts for the dance-lessons-coach project.
|
||||
|
||||
## 📁 Script Categories
|
||||
|
||||
@@ -22,7 +22,7 @@ This directory contains automation and management scripts for the DanceLessonsCo
|
||||
|
||||
### 1. Server Management (`start-server.sh`)
|
||||
|
||||
**Manage the DanceLessonsCoach server lifecycle**
|
||||
**Manage the dance-lessons-coach server lifecycle**
|
||||
|
||||
```bash
|
||||
# Start the server
|
||||
@@ -301,13 +301,13 @@ exit 0
|
||||
- [Git SCM](https://git-scm.com/)
|
||||
- [Go Build](https://golang.org/cmd/go/)
|
||||
|
||||
### DanceLessonsCoach Specific
|
||||
### dance-lessons-coach Specific
|
||||
- [ADR 0014: Version Management](adr/0014-version-management-lifecycle.md)
|
||||
- [AGENTS.md Scripts Section](#-scripts)
|
||||
- [Contributing Guide](CONTRIBUTING.md)
|
||||
|
||||
---
|
||||
|
||||
**Maintained by:** DanceLessonsCoach Team
|
||||
**Maintained by:** dance-lessons-coach Team
|
||||
**License:** MIT
|
||||
**Status:** Actively developed
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Build DanceLessonsCoach with version information
|
||||
# Build dance-lessons-coach with version information
|
||||
# Usage: ./scripts/build-with-version.sh [output_path]
|
||||
|
||||
set -e
|
||||
@@ -22,7 +22,7 @@ GIT_DATE=$(git log -1 --format=%cd --date=short 2>/dev/null || echo "unknown")
|
||||
# Build time (UTC for consistency)
|
||||
BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
echo "🔧 Building DanceLessonsCoach $VERSION"
|
||||
echo "🔧 Building dance-lessons-coach $VERSION"
|
||||
echo " Commit: $GIT_COMMIT"
|
||||
echo " Date: $GIT_DATE"
|
||||
echo " Output: $OUTPUT_PATH"
|
||||
@@ -31,9 +31,9 @@ echo " Output: $OUTPUT_PATH"
|
||||
go build \
|
||||
-o "$OUTPUT_PATH" \
|
||||
-ldflags="\
|
||||
-X DanceLessonsCoach/pkg/version.Version=$VERSION \
|
||||
-X DanceLessonsCoach/pkg/version.Commit=$GIT_COMMIT \
|
||||
-X DanceLessonsCoach/pkg/version.Date=$BUILD_DATE \
|
||||
-X dance-lessons-coach/pkg/version.Version=$VERSION \
|
||||
-X dance-lessons-coach/pkg/version.Commit=$GIT_COMMIT \
|
||||
-X dance-lessons-coach/pkg/version.Date=$BUILD_DATE \
|
||||
" \
|
||||
./cmd/server
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# DanceLessonsCoach Build Script
|
||||
# dance-lessons-coach Build Script
|
||||
# Builds binaries into the bin/ directory
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔨 Building DanceLessonsCoach binaries..."
|
||||
echo "🔨 Building dance-lessons-coach binaries..."
|
||||
|
||||
# Create bin directory if it doesn't exist
|
||||
mkdir -p bin
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/bin/bash
|
||||
# DanceLessonsCoach CI/CD Management Script
|
||||
# dance-lessons-coach CI/CD Management Script
|
||||
# Unified interface for all CI/CD operations
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPTS_DIR="$(dirname "$0")/cicd"
|
||||
|
||||
echo "🚀 DanceLessonsCoach CI/CD Management"
|
||||
echo "🚀 dance-lessons-coach CI/CD Management"
|
||||
echo "===================================="
|
||||
echo ""
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
@@ -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!"
|
||||
@@ -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!"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -6,10 +6,96 @@
|
||||
set -e
|
||||
|
||||
echo "🧪 Running BDD Tests..."
|
||||
cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach
|
||||
SCRIPTS_DIR=$(dirname `realpath ${BASH_SOURCE[0]}`)
|
||||
cd $SCRIPTS_DIR/..
|
||||
|
||||
# Check if we're in CI environment
|
||||
if [ -n "$GITHUB_ACTIONS" ] || [ -n "$GITEA_ACTIONS" ]; then
|
||||
# CI environment - PostgreSQL is already running as a service
|
||||
echo "🏗️ CI environment detected"
|
||||
echo "🐋 PostgreSQL service is already running"
|
||||
|
||||
# Check if database is accessible
|
||||
echo "📦 Checking PostgreSQL connectivity..."
|
||||
if ! pg_isready -h postgres -p 5432 -U postgres -d dance_lessons_coach_bdd_test; then
|
||||
echo "❌ PostgreSQL is not ready or accessible"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ PostgreSQL is ready!"
|
||||
else
|
||||
# Local environment - use docker compose
|
||||
echo "💻 Local environment detected"
|
||||
|
||||
# Check if PostgreSQL container is running, start it if not
|
||||
echo "🐋 Checking PostgreSQL container..."
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^dance-lessons-coach-postgres$"; then
|
||||
echo "🐋 Starting PostgreSQL container..."
|
||||
docker compose up -d postgres
|
||||
|
||||
# Wait for PostgreSQL to be ready
|
||||
echo "⏳ Waiting for PostgreSQL to be ready..."
|
||||
max_attempts=30
|
||||
attempt=0
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
if docker exec dance-lessons-coach-postgres pg_isready -U postgres 2>/dev/null; then
|
||||
echo "✅ PostgreSQL is ready!"
|
||||
break
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ $attempt -eq $max_attempts ]; then
|
||||
echo "❌ PostgreSQL failed to start"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create BDD test database (separate from development database)
|
||||
echo "📦 Creating BDD test database..."
|
||||
# Drop database if it exists, then create fresh
|
||||
docker exec dance-lessons-coach-postgres psql -U postgres -c "DROP DATABASE IF EXISTS dance_lessons_coach_bdd_test;"
|
||||
if docker exec dance-lessons-coach-postgres createdb -U postgres dance_lessons_coach_bdd_test; then
|
||||
echo "✅ BDD test database created successfully!"
|
||||
else
|
||||
echo "❌ Failed to create BDD test database"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "✅ PostgreSQL container is already running"
|
||||
|
||||
# Check if BDD test database exists, create if not
|
||||
echo "📦 Checking BDD test database..."
|
||||
if docker exec dance-lessons-coach-postgres psql -U postgres -lqt | cut -d \| -f 1 | grep -qw "dance_lessons_coach_bdd_test"; then
|
||||
echo "✅ BDD test database already exists"
|
||||
else
|
||||
echo "📦 Creating BDD test database..."
|
||||
if docker exec dance-lessons-coach-postgres createdb -U postgres dance_lessons_coach_bdd_test; then
|
||||
echo "✅ BDD test database created successfully!"
|
||||
else
|
||||
echo "❌ Failed to create BDD test database"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run the BDD tests
|
||||
test_output=$(go test ./features/... -v 2>&1)
|
||||
# For local environment, set database environment variables to use localhost
|
||||
# For CI environment, the database is already configured as a service
|
||||
if [ -z "$GITHUB_ACTIONS" ] && [ -z "$GITEA_ACTIONS" ]; then
|
||||
echo "🔧 Setting database environment variables for local environment..."
|
||||
export DLC_DATABASE_HOST="localhost"
|
||||
export DLC_DATABASE_PORT="5432"
|
||||
export DLC_DATABASE_USER="postgres"
|
||||
export DLC_DATABASE_PASSWORD="postgres"
|
||||
export DLC_DATABASE_NAME="dance_lessons_coach_bdd_test"
|
||||
export DLC_DATABASE_SSL_MODE="disable"
|
||||
else
|
||||
echo "🏗️ CI environment detected, using service configuration"
|
||||
fi
|
||||
|
||||
# Run tests with proper coverage measurement
|
||||
test_output=$(go test ./features/... -v -cover -coverpkg=./... -coverprofile=coverage.out 2>&1)
|
||||
test_exit_code=$?
|
||||
|
||||
echo "$test_output"
|
||||
@@ -38,5 +124,6 @@ if [ $test_exit_code -eq 0 ]; then
|
||||
exit 0
|
||||
else
|
||||
echo "❌ BDD tests failed"
|
||||
echo echo 'DLC_DATABASE_HOST=localhost DLC_DATABASE_PORT=5432 DLC_DATABASE_USER=postgres DLC_DATABASE_PASSWORD=postgres DLC_DATABASE_NAME=dance_lessons_coach_bdd_test DLC_DATABASE_SSL_MODE=disable go test ./features/... -v'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
177
scripts/run-bdd-tests.sh.backup
Executable file
177
scripts/run-bdd-tests.sh.backup
Executable 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
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# DanceLessonsCoach Server Start Script
|
||||
# dance-lessons-coach Server Start Script
|
||||
# This script starts the server in the background and provides control functions
|
||||
|
||||
# Configuration
|
||||
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach"
|
||||
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach"
|
||||
SERVER_CMD="go run ./cmd/server"
|
||||
LOG_FILE="server.log"
|
||||
PID_FILE="server.pid"
|
||||
@@ -14,7 +14,7 @@ cd "$PROJECT_DIR" || { echo "Failed to change to project directory"; exit 1; }
|
||||
|
||||
# Function to start the server
|
||||
start_server() {
|
||||
echo "Starting DanceLessonsCoach server..."
|
||||
echo "Starting dance-lessons-coach server..."
|
||||
|
||||
# Check if server is already running
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# DanceLessonsCoach Graceful Shutdown Test Script
|
||||
# dance-lessons-coach Graceful Shutdown Test Script
|
||||
# This script tests the complete server lifecycle with JSON logging
|
||||
# and validates that all shutdown logs are present
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach"
|
||||
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach"
|
||||
SERVER_CMD="./scripts/start-server.sh"
|
||||
LOG_FILE="server.log"
|
||||
PID_FILE="server.pid"
|
||||
TEST_LOG="shutdown_test.log"
|
||||
|
||||
# Colors for output - use simple echo -e with inline ANSI codes
|
||||
echo -e "\033[1;34m=== DanceLessonsCoach Graceful Shutdown Test ===\033[0m"
|
||||
echo -e "\033[1;34m=== dance-lessons-coach Graceful Shutdown Test ===\033[0m"
|
||||
echo ""
|
||||
|
||||
# Clean up any existing server
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Simulates the CI/CD pipeline but builds Docker image locally
|
||||
# Use this for local development and testing without Gitea
|
||||
|
||||
set -e
|
||||
set -eu
|
||||
|
||||
echo "🚀 Local CI/CD Testing"
|
||||
echo "======================"
|
||||
@@ -16,53 +16,162 @@ if ! command -v go >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Assume Docker is available (required for this workflow)
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "⚠️ Docker not found. Docker build steps will be skipped"
|
||||
HAS_DOCKER=false
|
||||
else
|
||||
HAS_DOCKER=true
|
||||
echo "❌ Docker is required for this CI/CD workflow"
|
||||
echo "Please install Docker and Docker Compose plugin"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for docker compose plugin
|
||||
if ! docker compose version >/dev/null 2>&1; then
|
||||
echo "⚠️ Docker Compose plugin not found. Installing..."
|
||||
sudo apt-get update && sudo apt-get install -y docker-compose-plugin
|
||||
fi
|
||||
|
||||
echo "✅ Environment ready"
|
||||
echo ""
|
||||
|
||||
# 2. Install dependencies
|
||||
echo "2. Installing dependencies..."
|
||||
go mod tidy
|
||||
echo "✅ Dependencies installed"
|
||||
# 2. Calculate dependency hash (match CI workflow)
|
||||
echo "2. Calculating dependency hash..."
|
||||
# Use shasum on macOS, sha256sum on Linux
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
export DEPS_HASH=$(sha256sum go.mod go.sum | sha256sum | cut -d' ' -f1 | head -c 12)
|
||||
else
|
||||
export DEPS_HASH=$(shasum -a 256 go.mod go.sum | shasum -a 256 | cut -d' ' -f1 | head -c 12)
|
||||
fi
|
||||
echo "Dependency hash: $DEPS_HASH"
|
||||
echo "✅ Dependency hash calculated"
|
||||
echo ""
|
||||
|
||||
# 3. Install swag and generate docs
|
||||
echo "3. Generating Swagger documentation..."
|
||||
if [ ! -f pkg/server/docs/swagger.json ]; then
|
||||
echo "📝 Generating Swagger docs..."
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
cd pkg/server && go generate
|
||||
cd ../..
|
||||
echo "✅ Swagger documentation generated"
|
||||
# 3. Check for Docker cache
|
||||
echo "3. Checking for Docker build cache..."
|
||||
IMAGE_NAME="gitea.arcodange.lab/arcodange/dance-lessons-coach-build-cache:$DEPS_HASH"
|
||||
|
||||
# Try to pull the cache image
|
||||
if docker pull "$IMAGE_NAME" >/dev/null 2>&1; then
|
||||
echo "✅ Cache hit - using existing build cache"
|
||||
USE_DOCKER_CACHE=true
|
||||
else
|
||||
echo "✅ Swagger documentation already exists"
|
||||
echo "⚠️ Cache miss - will build without cache"
|
||||
USE_DOCKER_CACHE=false
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 4. Build and test
|
||||
echo "4. Building and testing..."
|
||||
go build ./...
|
||||
# 4. Start PostgreSQL with Docker Compose
|
||||
echo "4. Starting PostgreSQL..."
|
||||
docker compose -f docker-compose.yml up -d postgres
|
||||
|
||||
# Wait for PostgreSQL to be ready
|
||||
echo "Waiting for PostgreSQL to be ready..."
|
||||
for i in {1..30}; do
|
||||
if docker exec dance-lessons-coach-postgres pg_isready -U postgres; then
|
||||
echo "✅ PostgreSQL is ready!"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for PostgreSQL... ($i/30)"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Set PostgreSQL environment variables for BDD tests
|
||||
export DLC_DATABASE_HOST="localhost" # PostgreSQL port is mapped to host
|
||||
export DLC_DATABASE_PORT=5432
|
||||
export DLC_DATABASE_USER=postgres
|
||||
export DLC_DATABASE_PASSWORD=postgres
|
||||
export DLC_DATABASE_NAME=dance_lessons_coach_bdd_test
|
||||
export DLC_DATABASE_SSL_MODE=disable
|
||||
echo ""
|
||||
|
||||
# 5. Install dependencies
|
||||
if [ "$USE_DOCKER_CACHE" = true ]; then
|
||||
echo "5. Checking dependencies..."
|
||||
echo "✅ Using pre-installed dependencies from Docker cache"
|
||||
else
|
||||
echo "5. Installing dependencies..."
|
||||
go mod tidy
|
||||
fi
|
||||
echo "✅ Dependencies ready"
|
||||
echo ""
|
||||
|
||||
# 6. Generate Swagger Docs
|
||||
if [ "$USE_DOCKER_CACHE" = true ]; then
|
||||
echo "6. Generating Swagger documentation..."
|
||||
echo "Running in Docker container..."
|
||||
docker run --rm \
|
||||
--network dance-lessons-coach-network \
|
||||
-v "$(pwd):/workspace" \
|
||||
-w /workspace/pkg/server \
|
||||
"$IMAGE_NAME" \
|
||||
sh -c "go generate"
|
||||
else
|
||||
echo "6. Generating Swagger documentation..."
|
||||
echo "Running natively..."
|
||||
cd pkg/server && go generate
|
||||
cd ../..
|
||||
fi
|
||||
echo "✅ Swagger documentation generated"
|
||||
echo ""
|
||||
|
||||
# 7. Build and test
|
||||
if [ "$USE_DOCKER_CACHE" = true ]; then
|
||||
echo "7. Building and testing..."
|
||||
echo "Running in Docker container..."
|
||||
docker run --rm \
|
||||
--network dance-lessons-coach-network \
|
||||
-v "$(pwd):/workspace" \
|
||||
-w /workspace \
|
||||
"$IMAGE_NAME" \
|
||||
sh -c "go build ./..."
|
||||
else
|
||||
echo "7. Building and testing..."
|
||||
echo "Running natively..."
|
||||
go build ./...
|
||||
fi
|
||||
echo "✅ Code compiled successfully"
|
||||
|
||||
go test ./... -cover -v
|
||||
if [ "$USE_DOCKER_CACHE" = true ]; then
|
||||
echo "Running in Docker container with PostgreSQL..."
|
||||
docker run --rm \
|
||||
--network dance-lessons-coach-network \
|
||||
-v "$(pwd):/workspace" \
|
||||
-w /workspace \
|
||||
-e DLC_DATABASE_HOST=dance-lessons-coach-postgres \
|
||||
-e DLC_DATABASE_PORT=5432 \
|
||||
-e DLC_DATABASE_USER=postgres \
|
||||
-e DLC_DATABASE_PASSWORD=postgres \
|
||||
-e DLC_DATABASE_NAME=dance_lessons_coach_bdd_test \
|
||||
-e DLC_DATABASE_SSL_MODE=disable \
|
||||
"$IMAGE_NAME" \
|
||||
sh -c "go test ./... -coverprofile=coverage.out -v && go tool cover -func=coverage.out > coverage.txt"
|
||||
else
|
||||
echo "Running natively with Docker Compose PostgreSQL..."
|
||||
go test ./... -coverprofile=coverage.out -v
|
||||
go tool cover -func=coverage.out > coverage.txt
|
||||
fi
|
||||
echo "✅ Tests passed"
|
||||
echo ""
|
||||
|
||||
# 5. Build binaries
|
||||
echo "5. Building binaries..."
|
||||
./scripts/build.sh
|
||||
# 8. Build binaries
|
||||
if [ "$USE_DOCKER_CACHE" = true ]; then
|
||||
echo "8. Building binaries..."
|
||||
echo "Running in Docker container..."
|
||||
docker run --rm \
|
||||
--network dance-lessons-coach-network \
|
||||
-v "$(pwd):/workspace" \
|
||||
-w /workspace \
|
||||
"$IMAGE_NAME" \
|
||||
sh -c "./scripts/build.sh"
|
||||
else
|
||||
echo "8. Building binaries..."
|
||||
echo "Running natively..."
|
||||
./scripts/build.sh
|
||||
fi
|
||||
echo "✅ Binaries built"
|
||||
ls -la bin/
|
||||
echo ""
|
||||
|
||||
# 6. Version bump simulation
|
||||
echo "6. Version bump simulation..."
|
||||
# 9. Version bump simulation
|
||||
echo "9. Version bump simulation..."
|
||||
LAST_COMMIT=$(git log -1 --pretty=%B | head -1)
|
||||
echo "Last commit: $LAST_COMMIT"
|
||||
|
||||
@@ -85,152 +194,170 @@ CURRENT_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}"
|
||||
echo "📊 Current version: $CURRENT_VERSION"
|
||||
echo ""
|
||||
|
||||
# 7. Local Docker build instructions
|
||||
if [ "$HAS_DOCKER" = true ]; then
|
||||
echo "🐳 LOCAL DOCKER BUILD INSTRUCTIONS"
|
||||
echo "================================"
|
||||
echo ""
|
||||
# 10. Local Docker build instructions
|
||||
echo "🐳 LOCAL DOCKER BUILD INSTRUCTIONS"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
echo "1. Build Docker image locally:"
|
||||
echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ."
|
||||
echo ""
|
||||
echo "1. Build Docker image locally (development):"
|
||||
echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ."
|
||||
echo ""
|
||||
|
||||
echo "2. Tag the image:"
|
||||
echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest"
|
||||
echo ""
|
||||
echo "2. Build production image using docker/Dockerfile.prod:"
|
||||
echo " # Note: Local docker/Dockerfile.prod uses 'latest' tag for testing"
|
||||
echo " docker build -t dance-lessons-coach-prod:$CURRENT_VERSION -f docker/Dockerfile.prod ."
|
||||
echo " # For CI/CD, the workflow generates correct docker/Dockerfile.prod with dependency hash"
|
||||
echo ""
|
||||
|
||||
echo "3. Test the local image (check port availability first):"
|
||||
echo " docker run -d -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION"
|
||||
echo " # Or use alternative port if 8080 is in use:"
|
||||
echo " docker run -d -p 8081:8080 dance-lessons-coach:$CURRENT_VERSION"
|
||||
echo ""
|
||||
echo "4. Branch-specific container naming (recommended):"
|
||||
echo " BRANCH=\"(git rev-parse --abbrev-ref HEAD | tr '/' '-')"
|
||||
echo " docker run -d -p 8080:8080 --name dance-lessons-coach-\"$BRANCH\" dance-lessons-coach:$CURRENT_VERSION"
|
||||
echo ""
|
||||
echo "3. Compare image sizes:"
|
||||
echo " docker images | grep dance-lessons-coach"
|
||||
echo ""
|
||||
|
||||
echo "5. Test API endpoints:"
|
||||
echo " curl http://localhost:8080/api/health"
|
||||
echo " curl http://localhost:8080/api/v1/greet/YourName"
|
||||
echo ""
|
||||
echo "4. Tag the image:"
|
||||
echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest"
|
||||
echo ""
|
||||
|
||||
echo "5. Clean up:"
|
||||
echo " docker stop <container_id> && docker rm <container_id>"
|
||||
echo ""
|
||||
echo "5. Test the local image (check port availability first):"
|
||||
echo " docker run -d -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION"
|
||||
echo " # Or use alternative port if 8080 is in use:"
|
||||
echo " docker run -d -p 8081:8080 dance-lessons-coach:$CURRENT_VERSION"
|
||||
echo ""
|
||||
echo "6. Branch-specific container naming (recommended):"
|
||||
echo " BRANCH=\"$(git rev-parse --abbrev-ref HEAD | tr '/' '-')\""
|
||||
echo " docker run -d -p 8080:8080 --name dance-lessons-coach-\"$BRANCH\" dance-lessons-coach:$CURRENT_VERSION"
|
||||
echo ""
|
||||
|
||||
echo "💡 Tip: Use 'docker images' to see your built images"
|
||||
echo "💡 Use 'docker ps' to see running containers"
|
||||
echo ""
|
||||
echo "7. Test API endpoints:"
|
||||
echo " curl http://localhost:8080/api/health"
|
||||
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 ""
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "🐳 Building Docker image..."
|
||||
echo "8. Clean up:"
|
||||
echo " docker stop <container_id> && docker rm <container_id>"
|
||||
echo ""
|
||||
|
||||
echo "💡 Tip: Use 'docker images' to see your built images"
|
||||
echo "💡 Use 'docker ps' to see running containers"
|
||||
echo ""
|
||||
|
||||
# Ask if user wants to build Docker image now
|
||||
read -p "🚀 Do you want to build the Docker image now? (y/n): " -n 1 -r
|
||||
echo ""
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "🐳 Building Docker image..."
|
||||
read -p "📋 Build (d)development or (p)production image? [d/p]: " -n 1 -r
|
||||
echo ""
|
||||
if [[ $REPLY =~ ^[Pp]$ ]]; then
|
||||
echo "🏗️ Building production image with docker/Dockerfile.prod..."
|
||||
docker build -t dance-lessons-coach-prod:$CURRENT_VERSION -f docker/Dockerfile.prod .
|
||||
docker tag dance-lessons-coach-prod:$CURRENT_VERSION dance-lessons-coach-prod:latest
|
||||
echo "✅ Production Docker image built: dance-lessons-coach-prod:$CURRENT_VERSION"
|
||||
CONTAINER_IMAGE="dance-lessons-coach-prod:$CURRENT_VERSION"
|
||||
else
|
||||
echo "🏗️ Building development image with Dockerfile..."
|
||||
docker build -t dance-lessons-coach:$CURRENT_VERSION .
|
||||
docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest
|
||||
echo "✅ Docker image built: dance-lessons-coach:$CURRENT_VERSION"
|
||||
echo ""
|
||||
echo "✅ Development Docker image built: dance-lessons-coach:$CURRENT_VERSION"
|
||||
CONTAINER_IMAGE="dance-lessons-coach:$CURRENT_VERSION"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check if port 8080 is available
|
||||
echo "🔍 Checking port availability..."
|
||||
if lsof -i :8080 > /dev/null 2>&1; then
|
||||
echo "⚠️ Port 8080 is already in use"
|
||||
read -p "🚀 Do you want to use a different port? (y/n): " -n 1 -r
|
||||
echo ""
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
read -p "Enter port number (e.g., 8081): " CUSTOM_PORT
|
||||
echo ""
|
||||
PORT=$CUSTOM_PORT
|
||||
else
|
||||
echo "ℹ️ Using port 8080 anyway (may fail if service is running)"
|
||||
PORT=8080
|
||||
fi
|
||||
# Check if port 8080 is available
|
||||
echo "🔍 Checking port availability..."
|
||||
if lsof -i :8080 > /dev/null 2>&1; then
|
||||
echo "⚠️ Port 8080 is already in use"
|
||||
read -p "🚀 Do you want to use a different port? (y/n): " -n 1 -r
|
||||
echo ""
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
read -p "Enter port number (e.g., 8081): " CUSTOM_PORT
|
||||
echo ""
|
||||
PORT=$CUSTOM_PORT
|
||||
else
|
||||
echo "✅ Port 8080 is available"
|
||||
echo "ℹ️ Using port 8080 anyway (may fail if service is running)"
|
||||
PORT=8080
|
||||
fi
|
||||
|
||||
read -p "🚀 Do you want to run the container now on port $PORT? (y/n): " -n 1 -r
|
||||
echo ""
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
# Get current branch name for container naming
|
||||
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '/' '-')
|
||||
CONTAINER_NAME="dance-lessons-coach-$BRANCH_NAME"
|
||||
|
||||
echo "🐳 Preparing container '$CONTAINER_NAME' on port $PORT..."
|
||||
|
||||
# Remove existing container if it exists
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
|
||||
echo "⚠️ Container '$CONTAINER_NAME' already exists - removing it..."
|
||||
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
|
||||
docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
|
||||
echo "✅ Old container removed"
|
||||
fi
|
||||
|
||||
# Also remove the generic test container if it exists
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^dance-lessons-coach-test$"; then
|
||||
echo "⚠️ Generic test container exists - removing it..."
|
||||
docker stop dance-lessons-coach-test > /dev/null 2>&1 || true
|
||||
docker rm dance-lessons-coach-test > /dev/null 2>&1 || true
|
||||
echo "✅ Old generic container removed"
|
||||
fi
|
||||
|
||||
echo "🐳 Starting container '$CONTAINER_NAME' on port $PORT..."
|
||||
docker run -d -p $PORT:8080 --name "$CONTAINER_NAME" dance-lessons-coach:$CURRENT_VERSION
|
||||
echo "✅ Container '$CONTAINER_NAME' started on port $PORT"
|
||||
echo ""
|
||||
|
||||
# Wait for container to be ready
|
||||
echo "🕒 Waiting for container to be ready..."
|
||||
MAX_ATTEMPTS=10
|
||||
ATTEMPT=1
|
||||
READY=false
|
||||
|
||||
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
|
||||
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
|
||||
READY=true
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
echo "🕒 Attempt $ATTEMPT/$MAX_ATTEMPTS..."
|
||||
done
|
||||
|
||||
if [ "$READY" = true ]; then
|
||||
echo "✅ Container is ready!"
|
||||
else
|
||||
echo "❌ Container failed to start properly"
|
||||
echo "📋 Container logs:"
|
||||
docker logs dance-lessons-coach-test
|
||||
echo ""
|
||||
echo "💡 Check container status with: docker ps -a"
|
||||
echo "💡 View full logs with: docker logs dance-lessons-coach-test"
|
||||
continue # Skip endpoint testing
|
||||
fi
|
||||
|
||||
echo "📋 Testing endpoints..."
|
||||
|
||||
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
|
||||
echo "✅ Health check passed"
|
||||
else
|
||||
echo "❌ Health check failed"
|
||||
fi
|
||||
|
||||
if curl -s http://localhost:$PORT/api/v1/greet/ | grep -q "Hello"; then
|
||||
echo "✅ Greet endpoint working"
|
||||
else
|
||||
echo "❌ Greet endpoint failed"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📖 Swagger UI available at: http://localhost:$PORT/swagger/"
|
||||
echo "💡 Press Ctrl+C to stop the container when done"
|
||||
echo " Or run: docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME"
|
||||
fi
|
||||
else
|
||||
echo "✅ Port 8080 is available"
|
||||
PORT=8080
|
||||
fi
|
||||
|
||||
read -p "🚀 Do you want to run the container now on port $PORT? (y/n): " -n 1 -r
|
||||
echo ""
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
# Get current branch name for container naming
|
||||
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '/' '-')
|
||||
CONTAINER_NAME="dance-lessons-coach-$BRANCH_NAME"
|
||||
|
||||
echo "🐳 Preparing container '$CONTAINER_NAME' on port $PORT..."
|
||||
|
||||
# Remove existing container if it exists
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
|
||||
echo "⚠️ Container '$CONTAINER_NAME' already exists - removing it..."
|
||||
docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
|
||||
docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
|
||||
echo "✅ Old container removed"
|
||||
fi
|
||||
|
||||
# Also remove the generic test container if it exists
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^dance-lessons-coach-test$"; then
|
||||
echo "⚠️ Generic test container exists - removing it..."
|
||||
docker stop dance-lessons-coach-test > /dev/null 2>&1 || true
|
||||
docker rm dance-lessons-coach-test > /dev/null 2>&1 || true
|
||||
echo "✅ Old generic container removed"
|
||||
fi
|
||||
|
||||
echo "🐳 Starting container '$CONTAINER_NAME' on port $PORT..."
|
||||
docker run -d -p $PORT:8080 --name "$CONTAINER_NAME" "$CONTAINER_IMAGE"
|
||||
echo "✅ Container '$CONTAINER_NAME' started on port $PORT"
|
||||
echo ""
|
||||
|
||||
# Wait for container to be ready
|
||||
echo "🕒 Waiting for container to be ready..."
|
||||
MAX_ATTEMPTS=10
|
||||
ATTEMPT=1
|
||||
READY=false
|
||||
|
||||
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
|
||||
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
|
||||
READY=true
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
echo "🕒 Attempt $ATTEMPT/$MAX_ATTEMPTS..."
|
||||
done
|
||||
|
||||
if [ "$READY" = true ]; then
|
||||
echo "✅ Container is ready!"
|
||||
else
|
||||
echo "❌ Container failed to start properly"
|
||||
echo "📋 Container logs:"
|
||||
docker logs dance-lessons-coach-test
|
||||
echo ""
|
||||
echo "💡 Check container status with: docker ps -a"
|
||||
echo "💡 View full logs with: docker logs dance-lessons-coach-test"
|
||||
continue # Skip endpoint testing
|
||||
fi
|
||||
|
||||
echo "📋 Testing endpoints..."
|
||||
|
||||
if curl -s http://localhost:$PORT/api/health | grep -q "healthy"; then
|
||||
echo "✅ Health check passed"
|
||||
else
|
||||
echo "❌ Health check failed"
|
||||
fi
|
||||
|
||||
if curl -s http://localhost:$PORT/api/v1/greet/ | grep -q "Hello"; then
|
||||
echo "✅ Greet endpoint working"
|
||||
else
|
||||
echo "❌ Greet endpoint failed"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📖 Swagger UI available at: http://localhost:$PORT/swagger/"
|
||||
echo "💡 Press Ctrl+C to stop the container when done"
|
||||
echo " Or run: docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ Docker not available - skipping Docker build instructions"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -238,18 +365,22 @@ echo "✅ LOCAL CI/CD TEST COMPLETE"
|
||||
echo "==========================="
|
||||
echo ""
|
||||
echo "📋 What was tested:"
|
||||
echo " ✅ Dependency hash calculation (matching CI workflow)"
|
||||
echo " ✅ Docker cache detection and usage"
|
||||
echo " ✅ PostgreSQL service with Docker Compose"
|
||||
echo " ✅ Go dependencies installation"
|
||||
echo " ✅ Swagger documentation generation"
|
||||
echo " ✅ Code compilation"
|
||||
echo " ✅ Unit tests with coverage"
|
||||
echo " ✅ Binary build"
|
||||
echo " ✅ Version bump simulation"
|
||||
if [ "$HAS_DOCKER" = true ]; then
|
||||
echo " ✅ Docker build (if chosen)"
|
||||
fi
|
||||
echo " ✅ Docker build (development and/or production if chosen)"
|
||||
echo ""
|
||||
echo "🎯 When ready for production:"
|
||||
echo " Push to main branch to trigger full CI/CD pipeline"
|
||||
echo " Docker image will be built and pushed to Gitea Container Registry"
|
||||
echo ""
|
||||
echo "💡 Local testing complete! Your changes are ready for CI/CD."
|
||||
echo "💡 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
|
||||
@@ -1,15 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# DanceLessonsCoach OpenTelemetry Test Script
|
||||
# dance-lessons-coach OpenTelemetry Test Script
|
||||
# This script tests OpenTelemetry integration with Jaeger
|
||||
|
||||
set -e
|
||||
|
||||
echo -e "\033[1;34m=== DanceLessonsCoach OpenTelemetry Test ===\033[0m"
|
||||
echo -e "\033[1;34m=== dance-lessons-coach OpenTelemetry Test ===\033[0m"
|
||||
echo ""
|
||||
|
||||
# Configuration
|
||||
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/DanceLessonsCoach"
|
||||
PROJECT_DIR="/Users/gabrielradureau/Work/Vibe/dance-lessons-coach"
|
||||
SERVER_CMD="./scripts/start-server.sh"
|
||||
LOG_FILE="server.log"
|
||||
PID_FILE="server.pid"
|
||||
@@ -47,7 +47,7 @@ fi
|
||||
|
||||
echo "Starting server with OpenTelemetry enabled..."
|
||||
DLC_TELEMETRY_ENABLED=true DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317" DLC_TELEMETRY_INSECURE=true \
|
||||
DLC_TELEMETRY_SERVICE_NAME="DanceLessonsCoach" $SERVER_CMD start
|
||||
DLC_TELEMETRY_SERVICE_NAME="dance-lessons-coach" $SERVER_CMD start
|
||||
sleep 3
|
||||
|
||||
echo "Testing API endpoints..."
|
||||
@@ -77,7 +77,7 @@ echo -e "\033[0;32m✅ OpenTelemetry Test Complete!\033[0m"
|
||||
echo ""
|
||||
echo "To view traces in Jaeger:"
|
||||
echo "1. Open http://localhost:16686 in your browser"
|
||||
echo "2. Select 'DanceLessonsCoach' service"
|
||||
echo "2. Select 'dance-lessons-coach' service"
|
||||
echo "3. Click 'Find Traces' button"
|
||||
echo ""
|
||||
echo "You should see traces for:"
|
||||
|
||||
@@ -171,4 +171,4 @@ echo "💡 Next Steps:"
|
||||
echo " 1. Test with Docker: ./scripts/test-cicd-simple.sh"
|
||||
echo " 2. Commit changes: git commit -m '🤖 ci: validate workflow'"
|
||||
echo " 3. Push to trigger: git push origin main"
|
||||
echo " 4. Monitor pipeline: https://gitea.arcodange.lab/arcodange/DanceLessonsCoach/actions"
|
||||
echo " 4. Monitor pipeline: https://gitea.arcodange.lab/arcodange/dance-lessons-coach/actions"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# DanceLessonsCoach Version Bump Script
|
||||
# dance-lessons-coach Version Bump Script
|
||||
# Usage: ./scripts/version-bump.sh [major|minor|patch|pre|release]
|
||||
|
||||
set -e
|
||||
@@ -81,7 +81,7 @@ echo "🔜 New version: $NEW_VERSION"
|
||||
|
||||
# Update VERSION file
|
||||
cat > "$VERSION_FILE" << VERSION_EOF
|
||||
# DanceLessonsCoach Version
|
||||
# dance-lessons-coach Version
|
||||
|
||||
# Current Version (Semantic Versioning)
|
||||
MAJOR=$MAJOR
|
||||
@@ -139,7 +139,7 @@ if [ -f "$README_MD" ]; then
|
||||
# Use awk to update version badge
|
||||
awk -v new_version="$NEW_VERSION" '{
|
||||
if ($0 ~ /Version.*badge.*version/) {
|
||||
print "[](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach/releases)"
|
||||
print "[](https://gitea.arcodange.fr/arcodange/dance-lessons-coach/releases)"
|
||||
} else {
|
||||
print $0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user