diff --git a/.gitea/workflows/dockerimage.yaml b/.gitea/workflows/dockerimage.yaml index 2010c52..8f79707 100644 --- a/.gitea/workflows/dockerimage.yaml +++ b/.gitea/workflows/dockerimage.yaml @@ -1,4 +1,4 @@ ---- +--- # DanceLessonsCoach Docker Image Build Workflow # Based on Arcodange webapp conventions # Builds and publishes Docker images to Gitea Container Registry @@ -32,9 +32,66 @@ env: CI_REGISTRY: "gitea.arcodange.lab" jobs: + version-bump: + name: Version Bump + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.26.1' + + - name: Install swag and generate Swagger Docs + run: | + if [ ! -f pkg/server/docs/swagger.json ]; then + echo "๐Ÿ“ Generating Swagger documentation..." + go install github.com/swaggo/swag/cmd/swag@latest + cd pkg/server && go generate + echo "โœ… Swagger documentation generated" + else + echo "โœ… Swagger documentation already exists, skipping generation" + fi + + - name: Bump version based on commit type + run: | + # Analyze last commit message + LAST_COMMIT=$(git log -1 --pretty=%B | head -1) + + if echo "$LAST_COMMIT" | grep -q "^feat:"; then + echo "๐ŸŽฏ Feature commit detected - bumping MINOR version" + ./scripts/version-bump.sh minor + elif echo "$LAST_COMMIT" | grep -q "^fix:"; then + echo "๐Ÿ› Fix commit detected - bumping PATCH version" + ./scripts/version-bump.sh patch + elif echo "$LAST_COMMIT" | grep -q "BREAKING CHANGE"; then + echo "๐Ÿ’ฅ Breaking change detected - bumping MAJOR version" + ./scripts/version-bump.sh major + else + echo "โญ๏ธ No automatic version bump needed" + # Still ensure swagger version is updated + source VERSION + NEW_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}" + sed -i "s|// @version [0-9.]*|// @version $NEW_VERSION|" cmd/server/main.go + fi + + - name: Commit version changes + run: | + git config --global user.name "CI Bot" + git config --global user.email "ci@arcodange.fr" + git add VERSION cmd/server/main.go + git commit -m "chore: auto version bump [skip ci]" || echo "No changes to commit" + git push + build-and-push-image: name: Build and Push Docker Image runs-on: ubuntu-latest + needs: version-bump + if: always() && (needs.version-bump.result == 'success' || needs.version-bump.result == 'skipped') steps: - name: Login to Gitea Container Registry @@ -80,4 +137,4 @@ jobs: else echo " - ${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}:latest" echo " - ${{ env.CI_REGISTRY }}/${{ env.GITEA_ORG }}/${{ env.GITEA_REPO }}:${{ github.sha }}" - fi + fi \ No newline at end of file diff --git a/.gitea/workflows/go-ci-cd.yaml b/.gitea/workflows/go-ci-cd.yaml index a9d9a27..87f2082 100644 --- a/.gitea/workflows/go-ci-cd.yaml +++ b/.gitea/workflows/go-ci-cd.yaml @@ -91,9 +91,20 @@ jobs: with: go-version: '1.26.1' + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.26.1' + + - name: Install swag + run: go install github.com/swaggo/swag/cmd/swag@latest + - name: Run go fmt run: go fmt ./... + - name: Run swag fmt + run: swag fmt + - name: Check for formatting issues run: | if [ -n "$(go fmt ./...)" ]; then @@ -105,7 +116,7 @@ jobs: version-check: name: Version Management runs-on: ubuntu-latest - needs: [build-test, lint-format, workflow-validation] + needs: [build-test, lint-format] if: github.ref == 'refs/heads/main' steps: diff --git a/.gitea/workflows/test-local-ci-cd.yaml b/.gitea/workflows/test-local-ci-cd.yaml new file mode 100644 index 0000000..caa3bdc --- /dev/null +++ b/.gitea/workflows/test-local-ci-cd.yaml @@ -0,0 +1,156 @@ +--- +# Local CI/CD Testing Workflow +# Simulates the CI/CD pipeline but builds Docker image locally +# Use this for local development and testing + +name: Local CI/CD Test + +on: + workflow_dispatch: {} + push: + branches: + - 'test/**' + - 'feature/**' + paths-ignore: + - 'README.md' + - 'doc/**' + - 'adr/**' + - '.gitea/**' + +# Arcodange-specific environment variables +env: + GITEA_INTERNAL: "https://gitea.arcodange.lab/" + GITEA_EXTERNAL: "https://gitea.arcodange.fr/" + GITEA_ORG: "arcodange" + GITEA_REPO: "DanceLessonsCoach" + CI_REGISTRY: "gitea.arcodange.lab" + +jobs: + local-test: + name: Local CI/CD Test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.26.1' + cache: true + + - name: Install dependencies + run: go mod tidy + + - name: Install swag + run: go install github.com/swaggo/swag/cmd/swag@latest + + - name: Generate Swagger Docs + run: cd pkg/server && go generate + + - name: Build all packages + run: go build ./... + + - name: Run tests with coverage + run: go test ./... -cover -v + + - name: Build binaries + run: ./scripts/build.sh + + - name: List artifacts + run: ls -la bin/ + + - name: Version Bump Simulation + run: | + echo "๐Ÿ“‹ Simulating version bump based on commit type..." + LAST_COMMIT=$(git log -1 --pretty=%B | head -1) + echo "Last commit: $LAST_COMMIT" + + if echo "$LAST_COMMIT" | grep -q "^feat:"; then + echo "๐ŸŽฏ Feature commit detected - would bump MINOR version" + echo "Run: ./scripts/version-bump.sh minor" + elif echo "$LAST_COMMIT" | grep -q "^fix:"; then + echo "๐Ÿ› Fix commit detected - would bump PATCH version" + echo "Run: ./scripts/version-bump.sh patch" + elif echo "$LAST_COMMIT" | grep -q "BREAKING CHANGE"; then + echo "๐Ÿ’ฅ Breaking change detected - would bump MAJOR version" + echo "Run: ./scripts/version-bump.sh major" + else + echo "โญ๏ธ No automatic version bump needed" + fi + + # Show current version + source VERSION + echo "๐Ÿ“Š Current version: $MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}" + + - name: Local Docker Build Instructions + run: | + echo "๐Ÿณ LOCAL DOCKER BUILD INSTRUCTIONS" + echo "================================" + echo "" + + # Get current version + source VERSION + CURRENT_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}" + + echo "1. Build Docker image locally:" + echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ." + echo "" + + echo "2. Tag the image:" + echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest" + echo "" + + echo "3. Test the local image:" + echo " docker run -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION" + echo "" + + echo "4. Test API endpoints:" + echo " curl http://localhost:8080/api/health" + echo " curl http://localhost:8080/api/v1/greet/YourName" + echo "" + + echo "5. Clean up:" + echo " docker stop && docker rm " + echo "" + + echo "๐Ÿ’ก Tip: Use 'docker images' to see your built images" + echo "๐Ÿ’ก Use 'docker ps' to see running containers" + + - name: Show Swagger UI Access + run: | + echo "๐Ÿ“– SWAGGER UI ACCESS" + echo "====================" + echo "" + echo "After starting the container, access Swagger UI at:" + echo " http://localhost:8080/swagger/" + echo "" + echo "Swagger JSON spec available at:" + echo " http://localhost:8080/swagger/doc.json" + echo "" + echo "Generated Swagger files:" + ls -la pkg/server/docs/ 2>/dev/null || echo " (Swagger docs will be generated during Docker build)" + + - name: Test Summary + run: | + echo "โœ… LOCAL CI/CD TEST COMPLETE" + echo "============================" + echo "" + echo "๐Ÿ“‹ What was tested:" + echo " โœ… Go dependencies installation" + echo " โœ… Swagger documentation generation" + echo " โœ… Code compilation" + echo " โœ… Unit tests with coverage" + echo " โœ… Binary build" + echo " โœ… Version bump simulation" + echo "" + echo "๐Ÿณ Next steps:" + echo " 1. Build Docker image locally (see instructions above)" + echo " 2. Test the container" + echo " 3. Verify API endpoints" + echo " 4. Test Swagger UI" + 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" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0e59084 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,439 @@ +# Contributing to DanceLessonsCoach + +Thank you for your interest in contributing to DanceLessonsCoach! This guide will help you set up your development environment and understand our contribution process. + +## ๐Ÿ“‹ Table of Contents + +1. [Development Setup](#development-setup) +2. [Code Style](#code-style) +3. [Commit Process](#commit-process) +4. [Testing](#testing) +5. [Documentation](#documentation) +6. [Pull Request Process](#pull-request-process) + +## ๐Ÿ”ง Development Setup + +### Prerequisites + +- Go 1.26.1+ +- Docker (for local testing) +- Git +- Make (optional, for convenience scripts) + +### Installation + +```bash +# Clone the repository +git clone https://gitea.arcodange.lab/arcodange/DanceLessonsCoach.git +cd DanceLessonsCoach + +# Install dependencies +go mod tidy + +# Install development tools +go install github.com/swaggo/swag/cmd/swag@latest + +# Set up git hooks +cp .git/hooks/pre-commit.sample .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit +``` + +### Git Hooks + +We use git hooks to enforce code quality: + +- **pre-commit**: Runs `go fmt` and `swag fmt` automatically +- **Commit message validation**: Enforces conventional commits + +To enable hooks: +```bash +chmod +x .git/hooks/pre-commit +``` + +## ๐ŸŽจ Code Style + +### Go Formatting + +We use `go fmt` for Go code formatting: +```bash +go fmt ./... +``` + +### Swagger Formatting + +We use `swag fmt` to format swagger comments: +```bash +swag fmt +``` + +This is automatically run in: +- Pre-commit hook +- CI/CD lint-format job + +### Commit Messages + +We follow [Conventional Commits](https://www.conventionalcommits.org/): + +```bash +# Good examples +git commit -m "feat: add new API endpoint" +git commit -m "fix: resolve race condition" +git commit -m "docs: update README" +git commit -m "chore: update dependencies" + +# Types: +# - feat: New feature +# - fix: Bug fix +# - docs: Documentation changes +# - style: Formatting, missing semicolons, etc. +# - refactor: Code refactoring +# - perf: Performance improvements +# - test: Adding missing tests +# - chore: Maintenance tasks +``` + +## ๐Ÿ”„ Commit Process + +### Before Committing + +1. **Run tests**: Ensure all tests pass + ```bash + go test ./... + ``` + +2. **Format code**: Run formatting tools + ```bash + go fmt ./... + swag fmt + ``` + +3. **Build project**: Ensure it compiles + ```bash + go build ./... + ``` + +4. **Generate docs**: Update swagger documentation + ```bash + cd pkg/server && go generate + ``` + +### Making Changes + +1. **Create a branch**: Use a descriptive name + ```bash + git checkout -b feat/add-new-feature + git checkout -b fix/resolve-issue + ``` + +2. **Make your changes**: Follow code style guidelines + +3. **Commit**: Use conventional commit messages + ```bash + git add . + git commit -m "feat: add new feature" + ``` + +4. **Push**: Push to your fork or branch + ```bash + git push origin feat/add-new-feature + ``` + +## ๐Ÿงช Testing + +### Unit Tests + +```bash +# Run all tests +go test ./... + +# Run with coverage +go test ./... -cover + +# Run specific package +go test ./pkg/greet/ -v +``` + +### Integration Tests + +```bash +# Run local CI/CD test +./scripts/test-local-ci-cd.sh + +# Test Docker build +./scripts/test-local-ci-cd.sh # Follow prompts to build Docker image +``` + +### BDD Tests + +```bash +# Run BDD tests +./scripts/run-bdd-tests.sh +``` + +## ๐Ÿ“š Documentation + +### Swagger Documentation + +We use swaggo for API documentation: + +```bash +# Generate swagger docs +cd pkg/server && go generate + +# Access Swagger UI (after starting server) +open http://localhost:8080/swagger/ +``` + +### Adding Swagger Comments + +```go +// @Summary Get user by ID +// @Description Returns user information +// @Tags API/v1/Users +// @Accept json +// @Produce json +// @Param id path int true "User ID" +// @Success 200 {object} UserResponse +// @Failure 404 {object} ErrorResponse +// @Router /v1/users/{id} [get] +func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) { + // ... +} +``` + +## ๐Ÿ”€ Pull Request Process + +1. **Open a Pull Request**: Target the `main` branch +2. **Describe changes**: Explain what and why +3. **Link issues**: Reference related issues (e.g., "Fixes #123") +4. **Wait for review**: Address feedback from maintainers +5. **Merge**: Once approved, a maintainer will merge + +### CI/CD Pipeline + +All pull requests trigger our CI/CD pipeline: +- **Build & Test**: Runs tests and builds binaries +- **Lint & Format**: Checks formatting with `go fmt` and `swag fmt` +- **Version Check**: Analyzes commits for version bumps +- **Docker Build**: Builds Docker image (on main branch) + +## ๐ŸŽฏ Best Practices + +### Code Organization + +- Keep handlers thin, move logic to services +- Use interfaces for dependencies +- Separate route registration from handlers +- Group related functionality + +### Error Handling + +- Return proper HTTP status codes +- Log errors with context +- Don't expose internal errors to clients +- Use structured error responses + +### Performance + +- Avoid allocations in hot paths +- Use context timeouts for external calls +- Batch database operations +- Use efficient data structures + +### Testing + +- Test interfaces, not implementations +- Use table-driven tests +- Test error cases +- Mock dependencies + +## ๐Ÿ“ Architecture Decisions + +Major architectural decisions are documented in the `adr/` directory. Please review relevant ADRs before making significant changes. + +### Key ADRs + +- [ADR-0001: Go 1.26.1 Standard](adr/0001-go-1.26.1-standard.md) +- [ADR-0002: Chi Router](adr/0002-chi-router.md) +- [ADR-0003: Zerolog Logging](adr/0003-zerolog-logging.md) +- [ADR-0013: OpenAPI/Swagger Toolchain](adr/0013-openapi-swagger-toolchain.md) + +## ๐Ÿค– 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. + +### Key Files and Directories + +**Core System:** +- `cmd/server/main.go` - Main server entry point with swagger metadata +- `pkg/server/server.go` - Server implementation with `go:generate` directive +- `pkg/greet/` - Greet service implementation +- `.gitea/workflows/` - CI/CD workflows + +**Documentation:** +- `adr/` - Architecture Decision Records +- `CONTRIBUTING.md` - Contribution guidelines +- `README.md` - Project overview + +**Scripts:** +- `scripts/` - Utility scripts for development +- `.git/hooks/` - Git hooks for automation + +### Skills to Use/Improve + +**Existing Skills:** +- `bdd-testing` - Behavior-Driven Development testing +- `skill-creator` - Skill creation and management +- `gitea-client` - Gitea API interactions + +**Skills to Develop:** +- `ci-cd-optimization` - CI/CD pipeline improvements +- `version-management` - Automatic version bumping +- `artifact-management` - Build artifact optimization +- `documentation-generation` - Automatic doc updates + +### Continuous Improvement Areas + +**CI/CD Pipeline:** +- Optimize job dependencies and artifact passing +- Improve version bumping logic based on commit analysis +- Enhance Docker build caching and layer optimization +- Add more comprehensive test coverage + +**Code Quality:** +- Expand swag fmt integration to other comment types +- Add additional linting checks +- Improve test automation +- Enhance error handling patterns + +**Documentation:** +- Auto-generate ADR templates +- Improve API documentation completeness +- Add more examples and tutorials +- Keep documentation in sync with code + +**Monitoring:** +- Add CI/CD performance metrics +- Track test coverage trends +- Monitor build times +- Alert on failures + +### AI Agent Workflow + +1. **Analyze:** Review current implementation and identify improvements +2. **Plan:** Create detailed implementation plan with alternatives +3. **Implement:** Make changes with proper testing +4. **Document:** Update ADRs and documentation +5. **Validate:** Ensure CI/CD passes and tests are updated + +### Best Practices for AI Agents + +- **Follow existing patterns** - Match project conventions +- **Update documentation** - Keep docs in sync with changes +- **Add tests** - Ensure new functionality is tested +- **Small increments** - Make focused, reviewable changes +- **Clear commit messages** - Use conventional commits format + +## ๐Ÿค Community + +- **Issues**: Report bugs and request features +- **Discussions**: Ask questions and propose ideas +- **Contributions**: All contributions welcome! + +## ๐Ÿ“œ License + +By contributing to DanceLessonsCoach, you agree that your contributions will be licensed under the MIT License. + +--- + +**Thank you for contributing!** ๐ŸŽ‰ +======= +## ๐Ÿค– 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. + +### Key Files and Directories + +**Core System:** +- `cmd/server/main.go` - Main server entry point with swagger metadata +- `pkg/server/server.go` - Server implementation with `go:generate` directive +- `pkg/greet/` - Greet service implementation +- `.gitea/workflows/` - CI/CD workflows + +**Documentation:** +- `adr/` - Architecture Decision Records +- `CONTRIBUTING.md` - Contribution guidelines +- `README.md` - Project overview + +**Scripts:** +- `scripts/` - Utility scripts for development +- `.git/hooks/` - Git hooks for automation + +### Skills to Use/Improve + +**Existing Skills:** +- `bdd-testing` - Behavior-Driven Development testing +- `skill-creator` - Skill creation and management +- `gitea-client` - Gitea API interactions + +**Skills to Develop:** +- `ci-cd-optimization` - CI/CD pipeline improvements +- `version-management` - Automatic version bumping +- `artifact-management` - Build artifact optimization +- `documentation-generation` - Automatic doc updates + +### Continuous Improvement Areas + +**CI/CD Pipeline:** +- Optimize job dependencies and artifact passing +- Improve version bumping logic based on commit analysis +- Enhance Docker build caching and layer optimization +- Add more comprehensive test coverage + +**Code Quality:** +- Expand swag fmt integration to other comment types +- Add additional linting checks +- Improve test automation +- Enhance error handling patterns + +**Documentation:** +- Auto-generate ADR templates +- Improve API documentation completeness +- Add more examples and tutorials +- Keep documentation in sync with code + +**Monitoring:** +- Add CI/CD performance metrics +- Track test coverage trends +- Monitor build times +- Alert on failures + +### AI Agent Workflow + +1. **Analyze:** Review current implementation and identify improvements +2. **Plan:** Create detailed implementation plan with alternatives +3. **Implement:** Make changes with proper testing +4. **Document:** Update ADRs and documentation +5. **Validate:** Ensure CI/CD passes and tests are updated + +### Best Practices for AI Agents + +- **Follow existing patterns** - Match project conventions +- **Update documentation** - Keep docs in sync with changes +- **Add tests** - Ensure new functionality is tested +- **Small increments** - Make focused, reviewable changes +- **Clear commit messages** - Use conventional commits format + +## ๐Ÿค Community + +- **Issues**: Report bugs and request features +- **Discussions**: Ask questions and propose ideas +- **Contributions**: All contributions welcome! + +## ๐Ÿ“œ License + +By contributing to DanceLessonsCoach, you agree that your contributions will be licensed under the MIT License. + +--- + +**Thank you for contributing!** ๐ŸŽ‰ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 9a32343..ceaa438 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,16 @@ RUN go mod download # Copy source code COPY . ./ +# Install swag and generate Swagger docs only if they don't exist +RUN if [ ! -f pkg/server/docs/swagger.json ]; then \ + echo "๐Ÿ“ Generating Swagger documentation..." && \ + go install github.com/swaggo/swag/cmd/swag@latest && \ + cd pkg/server && go generate && \ + echo "โœ… Swagger documentation generated"; \ + else \ + echo "โœ… Swagger documentation already exists, skipping swag installation and generation"; \ + fi + # Build binary RUN CGO_ENABLED=0 GOOS=linux go build -o /dance-lessons-coach ./cmd/server diff --git a/adr/0013-openapi-swagger-toolchain.md b/adr/0013-openapi-swagger-toolchain.md index bb9f96d..cf61b8d 100644 --- a/adr/0013-openapi-swagger-toolchain.md +++ b/adr/0013-openapi-swagger-toolchain.md @@ -334,6 +334,116 @@ go install github.com/swaggo/swag/cmd/swag@latest // @host localhost:8080 // @BasePath /api package main +``` + +### Swag Formatting Integration + +To ensure consistent swagger comment formatting, we've integrated `swag fmt` into our workflow: + +#### Git Hooks +Added to `.git/hooks/pre-commit`: +```bash +# Run swag fmt to format swagger comments +echo "Running swag fmt..." +if command -v swag >/dev/null 2>&1; then + swag fmt + if [ $? -ne 0 ]; then + echo "ERROR: swag fmt failed" + exit 1 + fi +else + echo "swag not installed, skipping swag fmt" +fi +``` + +#### CI/CD Integration +Added to `.gitea/workflows/go-ci-cd.yaml` lint-format job: +```yaml +- name: Install swag + run: go install github.com/swaggo/swag/cmd/swag@latest + +- name: Run swag fmt + run: swag fmt +``` + +#### Benefits +- **Consistent Formatting**: Automatic formatting of swagger comments +- **Pre-Commit Validation**: Catches issues before commit +- **CI/CD Enforcement**: Ensures formatting in all pull requests +- **Team Consistency**: Everyone follows the same rules +- **Automatic Fixes**: Issues are fixed automatically + +#### Usage +```bash +# Format swagger comments manually +swag fmt + +# Format is automatically run in: +# - pre-commit hook +# - CI/CD lint-format job +``` +======= +### Final Implementation + +```bash +# 1. Install swaggo +go install github.com/swaggo/swag/cmd/swag@latest + +# 2. Add swagger metadata to main.go +// @title DanceLessonsCoach API +// @version 1.0 +// @description API for DanceLessonsCoach service +// @host localhost:8080 +// @BasePath /api +package main +``` + +### Swag Formatting Integration + +To ensure consistent swagger comment formatting, we've integrated `swag fmt` into our workflow: + +#### Git Hooks +Added to `.git/hooks/pre-commit`: +```bash +# Run swag fmt to format swagger comments +echo "Running swag fmt..." +if command -v swag >/dev/null 2>&1; then + swag fmt + if [ $? -ne 0 ]; then + echo "ERROR: swag fmt failed" + exit 1 + fi +else + echo "swag not installed, skipping swag fmt" +fi +``` + +#### CI/CD Integration +Added to `.gitea/workflows/go-ci-cd.yaml` lint-format job: +```yaml +- name: Install swag + run: go install github.com/swaggo/swag/cmd/swag@latest + +- name: Run swag fmt + run: swag fmt +``` + +#### Benefits +- **Consistent Formatting**: Automatic formatting of swagger comments +- **Pre-Commit Validation**: Catches issues before commit +- **CI/CD Enforcement**: Ensures formatting in all pull requests +- **Team Consistency**: Everyone follows the same rules +- **Automatic Fixes**: Issues are fixed automatically + +#### Usage +```bash +# Format swagger comments manually +swag fmt + +# Format is automatically run in: +# - pre-commit hook +# - CI/CD lint-format job +``` ### Annotation Placement Considerations diff --git a/cmd/cli/main.go b/cmd/cli/main.go index b6c7584..5b290a6 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -7,6 +7,7 @@ import ( "DanceLessonsCoach/pkg/config" "DanceLessonsCoach/pkg/server" "DanceLessonsCoach/pkg/version" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" diff --git a/cmd/server/main.go b/cmd/server/main.go index 62826fb..4200839 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -1,20 +1,20 @@ // Package main provides the DanceLessonsCoach server entry point // -// @title DanceLessonsCoach API -// @version 1.1.1 -// @description API for DanceLessonsCoach service providing greeting functionality -// @termsOfService http://swagger.io/terms/ +// @title DanceLessonsCoach API +// @version 1.1.1 +// @description API for DanceLessonsCoach service providing greeting functionality +// @termsOfService http://swagger.io/terms/ -// @contact.name API Support -// @contact.url http://www.arcodange.fr/support -// @contact.email support@arcodange.fr +// @contact.name API Support +// @contact.url http://www.arcodange.fr/support +// @contact.email support@arcodange.fr -// @license.name MIT -// @license.url https://opensource.org/licenses/MIT +// @license.name MIT +// @license.url https://opensource.org/licenses/MIT -// @host localhost:8080 -// @BasePath /api -// @schemes http https +// @host localhost:8080 +// @BasePath /api +// @schemes http https package main diff --git a/doc/local-ci-cd-testing.md b/doc/local-ci-cd-testing.md new file mode 100644 index 0000000..ce3baf4 --- /dev/null +++ b/doc/local-ci-cd-testing.md @@ -0,0 +1,305 @@ +# Local CI/CD Testing Guide + +This guide explains how to test the CI/CD pipeline locally without requiring a Gitea instance. + +## Overview + +The local CI/CD testing allows you to: +- Test the entire build process locally +- Simulate version bumping based on commit conventions +- Build Docker images with the correct version tags +- Test the container before pushing to production + +## Available Testing Methods + +### 1. Interactive Local Testing Script + +**File:** `scripts/test-local-ci-cd.sh` + +**What it does:** +- Installs dependencies +- Generates Swagger documentation +- Runs compilation and tests +- Builds binaries +- Simulates version bumping +- Provides Docker build instructions +- Optionally builds and runs Docker container + +**Usage:** +```bash +./scripts/test-local-ci-cd.sh +``` + +**Features:** +- Interactive prompts for Docker build and run +- Automatic endpoint testing +- Clear instructions for each step +- Version-aware Docker image tagging + +### 2. Local CI/CD Workflow + +**File:** `.gitea/workflows/test-local-ci-cd.yaml` + +**What it does:** +- Simulates the CI/CD pipeline in a workflow format +- Provides detailed instructions for local Docker builds +- Shows version bump simulation +- Lists all test results + +**Usage:** +This workflow can be triggered manually or on test/feature branches. + +### 3. Workflow Validation + +**File:** `scripts/cicd/validate-workflow.sh` + +**What it does:** +- Validates YAML syntax +- Checks required workflow fields +- Validates job structure +- Verifies Arcodange-specific configurations + +**Usage:** +```bash +./scripts/cicd/validate-workflow.sh +``` + +## Step-by-Step Local Testing + +### 1. Run the Interactive Script + +```bash +cd /Users/gabrielradureau/Work/Vibe/DanceLessonsCoach +./scripts/test-local-ci-cd.sh +``` + +### 2. Follow the Prompts + +The script will guide you through: +- Environment setup +- Dependency installation +- Code compilation and testing +- Binary building +- Version bump simulation +- Docker build options + +### 3. Docker Testing (Optional) + +If you choose to build the Docker image: + +```bash +# Build with current version +docker build -t dance-lessons-coach:$CURRENT_VERSION . + +# Tag as latest +docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest + +# Check if port is available +./scripts/check-port.sh 8080 8081 8082 + +# Run the container (use available port) +docker run -d -p 8080:8080 --name dance-lessons-coach-test dance-lessons-coach:$CURRENT_VERSION +# Or use alternative port if 8080 is in use +docker run -d -p 8081:8080 --name dance-lessons-coach-test dance-lessons-coach:$CURRENT_VERSION + +# Branch-specific naming (recommended for multiple branches) +BRANCH=$(git rev-parse --abbrev-ref HEAD | tr '/' '-') +docker run -d -p 8080:8080 --name "dance-lessons-coach-$BRANCH" dance-lessons-coach:$CURRENT_VERSION +``` + +**Port Checking:** +The script automatically checks if port 8080 is available and suggests alternatives. + +**Container Management:** +- Automatically removes existing containers with the same name +- Uses branch-based naming to avoid conflicts +- Supports multiple branch testing simultaneously + +### 4. Test the Container + +```bash +# Health check +curl http://localhost:8080/api/health + +# Greet endpoint +curl http://localhost:8080/api/v1/greet/YourName + +# Swagger UI +open http://localhost:8080/swagger/ +``` + +### 5. Clean Up + +```bash +# Stop and remove container (generic) +docker stop dance-lessons-coach-test +docker rm dance-lessons-coach-test + +# Stop and remove branch-specific container +BRANCH=$(git rev-parse --abbrev-ref HEAD | tr '/' '-') +docker stop "dance-lessons-coach-$BRANCH" +docker rm "dance-lessons-coach-$BRANCH" + +# Remove all test containers +docker stop $(docker ps -aq --filter "name=dance-lessons-coach") +docker rm $(docker ps -aq --filter "name=dance-lessons-coach") + +# Remove images (optional) +docker rmi dance-lessons-coach:$CURRENT_VERSION +docker rmi dance-lessons-coach:latest +``` + +**Cleanup Tips:** +- Use `docker ps -a` to see all containers +- Use `docker images` to see all images +- Use `docker system prune` to clean up unused objects + +## Version Management + +The local testing respects the same version conventions as the production CI/CD: + +- **feat:** commits โ†’ MINOR version bump +- **fix:** commits โ†’ PATCH version bump +- **BREAKING CHANGE:** โ†’ MAJOR version bump +- Other commits โ†’ No automatic bump + +**Manual version bump:** +```bash +# Bump patch version +./scripts/version-bump.sh patch + +# Bump minor version +./scripts/version-bump.sh minor + +# Bump major version +./scripts/version-bump.sh major +``` + +## Docker Build Optimization + +The Dockerfile is optimized to: +- Only install swag if swagger.json doesn't exist +- Reuse existing swagger.json from CI/CD workflow +- Generate documentation only when needed + +This means: +- **Local builds:** Will generate swagger docs if missing +- **CI/CD builds:** Will reuse existing swagger.json +- **Production builds:** Most efficient path + +## Testing Different Scenarios + +### Test Version Bumping + +```bash +# Make a feature commit +git commit -m "feat: add new feature" +./scripts/test-local-ci-cd.sh # Will show MINOR bump suggestion + +# Make a fix commit +git commit -m "fix: resolve issue" +./scripts/test-local-ci-cd.sh # Will show PATCH bump suggestion +``` + +### Test Without Docker + +```bash +# Run tests without Docker +./scripts/test-local-ci-cd.sh +# Answer 'n' when asked about Docker build +``` + +### Test Specific Components + +```bash +# Just build and test +go build ./... +go test ./... -cover + +# Just generate swagger docs +cd pkg/server && go generate + +# Just build binaries +./scripts/build.sh + +# Check port availability +./scripts/check-port.sh 8080 8081 8082 +``` + +## Production Deployment + +When you're ready to deploy: + +1. **Commit your changes** with appropriate commit message +2. **Push to main branch** to trigger production CI/CD +3. **Monitor the workflow** in Gitea Actions +4. **Verify the Docker image** in Gitea Container Registry + +```bash +# Example production deployment +git add . +git commit -m "feat: add new API endpoint" +git push origin main +``` + +## Troubleshooting + +### Port Conflicts + +```bash +# Check what's using port 8080 +./scripts/check-port.sh 8080 + +# Find and kill process on port 8080 +kill -9 $(lsof -ti :8080) + +# Use alternative port +docker run -d -p 8081:8080 --name dance-lessons-coach-test dance-lessons-coach:$CURRENT_VERSION +``` + +### Docker Build Issues + +```bash +# Clean build +docker system prune -a --volumes +rm -rf pkg/server/docs/ +./scripts/test-local-ci-cd.sh +``` + +### Dependency Issues + +```bash +# Clean and reinstall +go clean -cache +go mod tidy +./scripts/test-local-ci-cd.sh +``` + +### Test Failures + +```bash +# Run specific test +go test ./pkg/greet/ -v + +# Run with race detector +go test ./... -race +``` + +## Best Practices + +1. **Test locally first** before pushing to CI/CD +2. **Use commit conventions** for automatic version bumping +3. **Test Docker images** before production deployment +4. **Clean up containers** after testing +5. **Monitor CI/CD logs** for any issues + +## Summary + +The local CI/CD testing provides: +- โœ… Full build pipeline simulation +- โœ… Version-aware Docker builds +- โœ… Interactive testing experience +- โœ… Production-like environment +- โœ… Easy troubleshooting + +Use these tools to ensure your changes work correctly before deploying to production! \ No newline at end of file diff --git a/docker-compose.cicd-test.yml b/docker-compose.cicd-test.yml index a59eb5d..f82e9a0 100644 --- a/docker-compose.cicd-test.yml +++ b/docker-compose.cicd-test.yml @@ -12,7 +12,7 @@ services: - GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_REGISTRATION_TOKEN} - GITEA_RUNNER_NAME=${GITEA_RUNNER_NAME:-local-test-runner} - GITEA_RUNNER_LABELS=${GITEA_RUNNER_LABELS:-ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://gitea/act_runner:latest} - command: act -W .gitea/workflows/ci-cd.yaml --rm + command: act -W .gitea/workflows/go-ci-cd.yaml --rm yamllint: image: pipelinecomponents/yamllint:latest diff --git a/pkg/bdd/suite.go b/pkg/bdd/suite.go index 255679d..22bdd02 100644 --- a/pkg/bdd/suite.go +++ b/pkg/bdd/suite.go @@ -3,6 +3,7 @@ package bdd import ( "DanceLessonsCoach/pkg/bdd/steps" "DanceLessonsCoach/pkg/bdd/testserver" + "github.com/cucumber/godog" ) diff --git a/pkg/bdd/testserver/server.go b/pkg/bdd/testserver/server.go index 3091339..d61e06e 100644 --- a/pkg/bdd/testserver/server.go +++ b/pkg/bdd/testserver/server.go @@ -8,6 +8,7 @@ import ( "DanceLessonsCoach/pkg/config" "DanceLessonsCoach/pkg/server" + "github.com/rs/zerolog/log" ) diff --git a/pkg/greet/api_v1.go b/pkg/greet/api_v1.go index 7b5320c..8ec441c 100644 --- a/pkg/greet/api_v1.go +++ b/pkg/greet/api_v1.go @@ -10,34 +10,39 @@ import ( ) // GreetResponse represents a greeting response -// @Description GreetResponse represents a greeting response with a message -// @Property message string "The greeting message" example("Hello John!") +// +// @Description GreetResponse represents a greeting response with a message +// @Property message string "The greeting message" example("Hello John!") type GreetResponse struct { Message string `json:"message" example:"Hello John!"` } // GreetRequest represents a greeting request -// @Description GreetRequest represents a request with a name to greet -// @Property name string "The name to greet" example("John") +// +// @Description GreetRequest represents a request with a name to greet +// @Property name string "The name to greet" example("John") type GreetRequest struct { Name string `json:"name" example:"John"` } // ErrorResponse represents an error response -// @Description ErrorResponse represents an error response +// +// @Description ErrorResponse represents an error response type ErrorResponse struct { Error string `json:"error" example:"invalid_request"` Message string `json:"message" example:"Invalid name parameter"` } // GreetResponseV2 represents a v2 greeting response -// @Description GreetResponseV2 represents a v2 greeting response +// +// @Description GreetResponseV2 represents a v2 greeting response type GreetResponseV2 struct { Message string `json:"message" example:"Hello my friend John!"` } // ValidationError represents a validation error response -// @Description ValidationError represents a validation error with details +// +// @Description ValidationError represents a validation error with details type ValidationError struct { Error string `json:"error" example:"validation_failed"` Message string `json:"message" example:"Invalid request data"` @@ -45,7 +50,8 @@ type ValidationError struct { } // ValidationDetail represents a single validation error detail -// @Description ValidationDetail represents a single field validation error +// +// @Description ValidationDetail represents a single field validation error type ValidationDetail struct { Field string `json:"field" example:"name"` Error string `json:"error" example:"must be <= 100 characters"` @@ -75,28 +81,30 @@ func (h *apiV1GreetHandler) RegisterRoutes(router chi.Router) { } // handleGreetQuery godoc -// @Summary Get default greeting -// @Description Returns a default greeting message -// @Tags API/v1/Greeting -// @Accept json -// @Produce json -// @Success 200 {object} GreetResponse "Successful response" -// @Router /v1/greet [get] +// +// @Summary Get default greeting +// @Description Returns a default greeting message +// @Tags API/v1/Greeting +// @Accept json +// @Produce json +// @Success 200 {object} GreetResponse "Successful response" +// @Router /v1/greet [get] func (h *apiV1GreetHandler) handleGreetQuery(w http.ResponseWriter, r *http.Request) { name := r.URL.Query().Get("name") h.writeJSONResponse(w, h.greeter.Greet(r.Context(), name)) } // handleGreetPath godoc -// @Summary Get personalized greeting -// @Description Returns a greeting with the specified name -// @Tags API/v1/Greeting -// @Accept json -// @Produce json -// @Param name path string true "Name to greet" -// @Success 200 {object} GreetResponse "Successful response" -// @Failure 400 {object} ErrorResponse "Invalid name parameter" -// @Router /v1/greet/{name} [get] +// +// @Summary Get personalized greeting +// @Description Returns a greeting with the specified name +// @Tags API/v1/Greeting +// @Accept json +// @Produce json +// @Param name path string true "Name to greet" +// @Success 200 {object} GreetResponse "Successful response" +// @Failure 400 {object} ErrorResponse "Invalid name parameter" +// @Router /v1/greet/{name} [get] func (h *apiV1GreetHandler) handleGreetPath(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") h.writeJSONResponse(w, h.greeter.Greet(r.Context(), name)) diff --git a/pkg/greet/api_v2.go b/pkg/greet/api_v2.go index 42a45f0..516a1cb 100644 --- a/pkg/greet/api_v2.go +++ b/pkg/greet/api_v2.go @@ -9,6 +9,7 @@ import ( "strconv" "DanceLessonsCoach/pkg/validation" + "github.com/go-chi/chi/v5" "github.com/rs/zerolog/log" ) @@ -45,15 +46,16 @@ type greetResponse struct { } // handleGreetPost godoc -// @Summary Get greeting (v2) -// @Description Returns a greeting message with validation (v2) -// @Tags API/v2/Greeting -// @Accept json -// @Produce json -// @Param request body GreetRequest true "Greeting request" -// @Success 200 {object} GreetResponseV2 "Successful response" -// @Failure 400 {object} ValidationError "Validation error" -// @Router /v2/greet [post] +// +// @Summary Get greeting (v2) +// @Description Returns a greeting message with validation (v2) +// @Tags API/v2/Greeting +// @Accept json +// @Produce json +// @Param request body GreetRequest true "Greeting request" +// @Success 200 {object} GreetResponseV2 "Successful response" +// @Failure 400 {object} ValidationError "Validation error" +// @Router /v2/greet [post] func (h *apiV2GreetHandler) handleGreetPost(w http.ResponseWriter, r *http.Request) { // Read request body body, err := io.ReadAll(r.Body) diff --git a/pkg/greet/greet.go b/pkg/greet/greet.go index d750840..2ca7ec4 100644 --- a/pkg/greet/greet.go +++ b/pkg/greet/greet.go @@ -2,6 +2,7 @@ package greet import ( "context" + "github.com/rs/zerolog/log" ) diff --git a/pkg/greet/greet_v2.go b/pkg/greet/greet_v2.go index 8ed0786..465a05c 100644 --- a/pkg/greet/greet_v2.go +++ b/pkg/greet/greet_v2.go @@ -2,6 +2,7 @@ package greet import ( "context" + "github.com/rs/zerolog/log" ) diff --git a/pkg/server/server.go b/pkg/server/server.go index f8aad58..6cb1598 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -1,4 +1,4 @@ -//go:generate swag init -g ../../cmd/server/main.go --parseDependency --parseInternal +//go:generate swag init -g ../../cmd/server/main.go -d ../../pkg/greet,../../pkg/server --parseDependency && mv ../../docs/* ./docs/ package server @@ -139,27 +139,29 @@ func (s *Server) getAllMiddlewares() []func(http.Handler) http.Handler { } // handleHealth godoc -// @Summary Health check -// @Description Check if the service is healthy -// @Tags System/Health -// @Accept json -// @Produce json -// @Success 200 {object} map[string]string "Service is healthy" -// @Router /health [get] +// +// @Summary Health check +// @Description Check if the service is healthy +// @Tags System/Health +// @Accept json +// @Produce json +// @Success 200 {object} map[string]string "Service is healthy" +// @Router /health [get] func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { log.Trace().Msg("Health check requested") w.Write([]byte(`{"status":"healthy"}`)) } // handleReadiness godoc -// @Summary Readiness check -// @Description Check if the service is ready to accept traffic -// @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" -// @Router /ready [get] +// +// @Summary Readiness check +// @Description Check if the service is ready to accept traffic +// @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" +// @Router /ready [get] func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) { log.Trace().Msg("Readiness check requested") @@ -175,14 +177,15 @@ func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) { } // handleVersion godoc -// @Summary Get API version -// @Description Returns the API version information -// @Tags System/Version -// @Accept plain,json -// @Produce plain,json -// @Param format query string false "Response format (plain, full, json)" Enums(plain, full, json) default(plain) -// @Success 200 {string} string "Version information" -// @Router /version [get] +// +// @Summary Get API version +// @Description Returns the API version information +// @Tags System/Version +// @Accept plain,json +// @Produce plain,json +// @Param format query string false "Response format (plain, full, json)" Enums(plain, full, json) default(plain) +// @Success 200 {string} string "Version information" +// @Router /version [get] func (s *Server) handleVersion(w http.ResponseWriter, r *http.Request) { log.Trace().Msg("Version check requested") diff --git a/scripts/check-port.sh b/scripts/check-port.sh new file mode 100755 index 0000000..fa22907 --- /dev/null +++ b/scripts/check-port.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# Port Availability Checker +# Checks if a specific port is available and suggests alternatives + +set -e + +if [ $# -eq 0 ]; then + echo "Usage: $0 [alternative_port1] [alternative_port2] ..." + echo "Example: $0 8080 8081 8082" + exit 1 +fi + +TARGET_PORT=$1 +shift +ALTERNATIVE_PORTS=("$@") + +echo "๐Ÿ” Checking port $TARGET_PORT availability..." + +if lsof -i :$TARGET_PORT > /dev/null 2>&1; then + echo "โŒ Port $TARGET_PORT is already in use" + echo "" + + # Show what's using the port + echo "๐Ÿ“‹ Process using port $TARGET_PORT:" + lsof -i :$TARGET_PORT + echo "" + + # Suggest alternatives + if [ ${#ALTERNATIVE_PORTS[@]} -gt 0 ]; then + echo "๐Ÿ’ก Alternative ports to try:" + for alt_port in "${ALTERNATIVE_PORTS[@]}"; do + if lsof -i :$alt_port > /dev/null 2>&1; then + echo " โŒ $alt_port - in use" + else + echo " โœ… $alt_port - available" + fi + done + else + echo "๐Ÿ’ก Try these alternative ports:" + for alt_port in 8081 8082 8083 8888 9090; do + if lsof -i :$alt_port > /dev/null 2>&1; then + echo " โŒ $alt_port - in use" + else + echo " โœ… $alt_port - available" + break + fi + done + fi + + echo "" + echo "๐Ÿ”ง To free up port $TARGET_PORT:" + echo " kill -9 \$(lsof -ti :$TARGET_PORT)" + echo " (Be careful - this will terminate the process using the port)" + + exit 1 +else + echo "โœ… Port $TARGET_PORT is available" + exit 0 +fi \ No newline at end of file diff --git a/scripts/cicd/test-cicd-docker.sh b/scripts/cicd/test-cicd-docker.sh index 70e0807..cf08b92 100755 --- a/scripts/cicd/test-cicd-docker.sh +++ b/scripts/cicd/test-cicd-docker.sh @@ -32,7 +32,7 @@ docker run --rm \ -v $(pwd):/workspace \ -w /workspace \ mikefarah/yq:latest \ - yq eval .gitea/workflows/ci-cd.yaml > /dev/null 2>&1 + yq eval .gitea/workflows/go-ci-cd.yaml > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "โœ… YAML syntax is valid" @@ -42,7 +42,7 @@ else -v $(pwd):/workspace \ -w /workspace \ mikefarah/yq:latest \ - yq eval .gitea/workflows/ci-cd.yaml || true + yq eval .gitea/workflows/go-ci-cd.yaml || true exit 1 fi @@ -73,7 +73,7 @@ docker run --rm \ -e GITEA_ORG="arcodange" \ -e GITEA_REPO="DanceLessonsCoach" \ gitea/act_runner:latest \ - act -W .gitea/workflows/ci-cd.yaml --rm + act -W .gitea/workflows/go-ci-cd.yaml --rm if [ $? -eq 0 ]; then echo "โœ… Workflow executed successfully" diff --git a/scripts/cicd/test-cicd-local.sh b/scripts/cicd/test-cicd-local.sh index 0f37b22..25d3791 100755 --- a/scripts/cicd/test-cicd-local.sh +++ b/scripts/cicd/test-cicd-local.sh @@ -9,7 +9,8 @@ echo "==============================" # 1. Validate YAML syntax echo "1. Validating YAML syntax..." if command -v yq >/dev/null 2>&1; then - yq eval '.' .gitea/workflows/ci-cd.yaml > /dev/null + 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" @@ -32,7 +33,8 @@ fi # 4. Check for required files echo "4. Checking required files..." REQUIRED_FILES=( - ".gitea/workflows/ci-cd.yaml" + ".gitea/workflows/go-ci-cd.yaml" + ".gitea/workflows/dockerimage.yaml" "docker-compose.cicd-test.yml" "config/runner.example" ) diff --git a/scripts/test-local-ci-cd.sh b/scripts/test-local-ci-cd.sh new file mode 100755 index 0000000..ce9020a --- /dev/null +++ b/scripts/test-local-ci-cd.sh @@ -0,0 +1,255 @@ +#!/bin/bash +# Local CI/CD Testing Script +# Simulates the CI/CD pipeline but builds Docker image locally +# Use this for local development and testing without Gitea + +set -e + +echo "๐Ÿš€ Local CI/CD Testing" +echo "======================" +echo "" + +# 1. Setup +echo "1. Setting up environment..." +if ! command -v go >/dev/null 2>&1; then + echo "โŒ Go not found. Please install Go 1.26.1+" + exit 1 +fi + +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 +fi + +echo "โœ… Environment ready" +echo "" + +# 2. Install dependencies +echo "2. Installing dependencies..." +go mod tidy +echo "โœ… Dependencies installed" +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" +else + echo "โœ… Swagger documentation already exists" +fi +echo "" + +# 4. Build and test +echo "4. Building and testing..." +go build ./... +echo "โœ… Code compiled successfully" + +go test ./... -cover -v +echo "โœ… Tests passed" +echo "" + +# 5. Build binaries +echo "5. Building binaries..." +./scripts/build.sh +echo "โœ… Binaries built" +ls -la bin/ +echo "" + +# 6. Version bump simulation +echo "6. Version bump simulation..." +LAST_COMMIT=$(git log -1 --pretty=%B | head -1) +echo "Last commit: $LAST_COMMIT" + +if echo "$LAST_COMMIT" | grep -q "^feat:"; then + echo "๐ŸŽฏ Feature commit detected - would bump MINOR version" + echo "Run: ./scripts/version-bump.sh minor" +elif echo "$LAST_COMMIT" | grep -q "^fix:"; then + echo "๐Ÿ› Fix commit detected - would bump PATCH version" + echo "Run: ./scripts/version-bump.sh patch" +elif echo "$LAST_COMMIT" | grep -q "BREAKING CHANGE"; then + echo "๐Ÿ’ฅ Breaking change detected - would bump MAJOR version" + echo "Run: ./scripts/version-bump.sh major" +else + echo "โญ๏ธ No automatic version bump needed" +fi + +# Show current version +source VERSION +CURRENT_VERSION="$MAJOR.$MINOR.$PATCH${PRERELEASE:+-$PRERELEASE}" +echo "๐Ÿ“Š Current version: $CURRENT_VERSION" +echo "" + +# 7. Local Docker build instructions +if [ "$HAS_DOCKER" = true ]; then + echo "๐Ÿณ LOCAL DOCKER BUILD INSTRUCTIONS" + echo "================================" + echo "" + + echo "1. Build Docker image locally:" + echo " docker build -t dance-lessons-coach:$CURRENT_VERSION ." + echo "" + + echo "2. Tag the image:" + echo " docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest" + echo "" + + echo "3. Test the local image (check port availability first):" + echo " docker run -d -p 8080:8080 dance-lessons-coach:$CURRENT_VERSION" + echo " # Or use alternative port if 8080 is in use:" + echo " docker run -d -p 8081:8080 dance-lessons-coach:$CURRENT_VERSION" + echo "" + echo "4. Branch-specific container naming (recommended):" + echo " BRANCH=\"(git rev-parse --abbrev-ref HEAD | tr '/' '-')" + echo " docker run -d -p 8080:8080 --name dance-lessons-coach-\"$BRANCH\" dance-lessons-coach:$CURRENT_VERSION" + echo "" + + echo "5. Test API endpoints:" + echo " curl http://localhost:8080/api/health" + echo " curl http://localhost:8080/api/v1/greet/YourName" + echo "" + + echo "5. Clean up:" + echo " docker stop && docker rm " + 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..." + docker build -t dance-lessons-coach:$CURRENT_VERSION . + docker tag dance-lessons-coach:$CURRENT_VERSION dance-lessons-coach:latest + echo "โœ… Docker image built: dance-lessons-coach:$CURRENT_VERSION" + echo "" + + # Check if port 8080 is available + echo "๐Ÿ” Checking port availability..." + if lsof -i :8080 > /dev/null 2>&1; then + echo "โš ๏ธ Port 8080 is already in use" + read -p "๐Ÿš€ Do you want to use a different port? (y/n): " -n 1 -r + echo "" + if [[ $REPLY =~ ^[Yy]$ ]]; then + read -p "Enter port number (e.g., 8081): " CUSTOM_PORT + echo "" + PORT=$CUSTOM_PORT + else + echo "โ„น๏ธ Using port 8080 anyway (may fail if service is running)" + PORT=8080 + fi + 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" 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 + fi +else + echo "โ„น๏ธ Docker not available - skipping Docker build instructions" +fi + +echo "" +echo "โœ… LOCAL CI/CD TEST COMPLETE" +echo "===========================" +echo "" +echo "๐Ÿ“‹ What was tested:" +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 "" +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." \ No newline at end of file