# 16. CI/CD Pipeline Design for Multi-Platform Compatibility **Date:** 2026-04-05 **Status:** 🟡 Proposed **Authors:** Arcodange Team **Decision Date:** TBD **Implementation Status:** Not Started ## Context DanceLessonsCoach requires a robust CI/CD pipeline that: 1. **Primary Platform**: Gitea (self-hosted Git service) 2. **Mirror Support**: GitHub and GitLab mirrors for visibility and backup 3. **Tool Agnostic**: Easy migration between CI/CD platforms 4. **Feature Requirements**: - Automated builds and testing - Version management and changelog generation - Code quality checks (linting, formatting) - Status badges for README - Artifact publishing - Deployment capabilities ## Decision Drivers * **Gitea Compatibility**: Must work with Gitea's CI/CD capabilities * **GitHub Actions Compatibility**: Leverage widely-known workflow syntax * **GitLab CI Compatibility**: Support GitLab's pipeline format * **Portability**: Easy migration between platforms * **Open Source Friendly**: Use tools that work well with open source projects * **Minimal Vendor Lock-in**: Avoid platform-specific features when possible * **Badges & Status**: Visual indicators of build/test status * **Code Quality**: Automated linting and formatting checks * **Version Management**: Automatic version bumping based on conventional commits * **Artifact Publishing**: Binary releases and container images * **Mirror Sync**: Ensure mirrors stay in sync with CI/CD results ## Decision We will implement a **multi-platform compatible CI/CD pipeline** using **GitHub Actions workflow syntax** as the common denominator, with platform-specific adapters where needed. ### Selected Architecture: Portable Workflow Design ```mermaid graph TD A[Gitea Main Repo] -->|GitHub Actions YAML| B[Gitea CI/CD] A -->|Mirror Push| C[GitHub Mirror] A -->|Mirror Push| D[GitLab Mirror] C -->|GitHub Actions| E[GitHub CI/CD] D -->|GitLab CI YAML| F[GitLab CI/CD] B --> G[Status Badges] E --> G F --> G ``` ### Pipeline Components #### 1. Core Workflow (GitHub Actions YAML) - **File**: `.github/workflows/main.yml` (works on all platforms) - **Format**: Standard GitHub Actions workflow syntax - **Benefits**: Widely understood, well-documented, portable #### 2. Platform Adapters - **Gitea**: Native support for GitHub Actions workflows - **GitHub**: Native support (no adapter needed) - **GitLab**: Use `github-actions-importer` or manual conversion #### 3. Workflow Structure ```yaml # .github/workflows/main.yml name: DanceLessonsCoach CI/CD on: push: branches: [ main ] pull_request: branches: [ main ] workflow_dispatch: jobs: build: name: Build and Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: '1.26.1' - run: go mod tidy - run: go build ./... - run: go test ./... -cover lint: name: Lint and Format runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: '1.26.1' - run: go fmt ./... - run: go vet ./... - run: golangci-lint run version: name: Version Management runs-on: ubuntu-latest needs: [build, lint] if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - run: ./scripts/version-bump.sh auto # Auto-detect from commits - run: git config user.name "CI/CD Bot" - run: git config user.email "ci@dancelessonscoach.org" - run: git add VERSION - run: git commit -m "🤖 chore: auto version bump [skip ci]" - run: git push release: name: Create Release runs-on: ubuntu-latest needs: version if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: '1.26.1' - run: ./scripts/build-with-version.sh - uses: softprops/action-gh-release@v1 with: files: bin/* generate_release_notes: true ``` ### 4. Status Badges ```markdown # README.md [![Build Status](https://ci.dancelessonscoach.org/api/badges/project/status)](https://ci.dancelessonscoach.org) [![GitHub Mirror Status](https://github.com/yourorg/DanceLessonsCoach/actions/workflows/main.yml/badge.svg)](https://github.com/yourorg/DanceLessonsCoach/actions) [![GitLab Mirror Status](https://gitlab.com/yourorg/DanceLessonsCoach/badges/main/pipeline.svg)](https://gitlab.com/yourorg/DanceLessonsCoach/-/pipelines) [![Go Report Card](https://goreportcard.com/badge/github.com/yourorg/DanceLessonsCoach)](https://goreportcard.com/report/github.com/yourorg/DanceLessonsCoach) [![Code Coverage](https://codecov.io/gh/yourorg/DanceLessonsCoach/branch/main/graph/badge.svg)](https://codecov.io/gh/yourorg/DanceLessonsCoach) ``` ### 5. Mirror Synchronization Strategy ```mermaid graph LR A[Gitea Main] -->|Webhook| B[Gitea CI/CD] A -->|Mirror Push| C[GitHub Mirror] A -->|Mirror Push| D[GitLab Mirror] C -->|Webhook| E[GitHub Actions] D -->|Webhook| F[GitLab CI/CD] B -->|Status| G[Gitea Badges] E -->|Status| H[GitHub Badges] F -->|Status| I[GitLab Badges] ``` ## Implementation Plan ### Phase 1: Gitea CI/CD Setup (Week 1) - Arcodange Configuration ```bash # 1. Create .gitea/workflows directory (Arcodange convention) mkdir -p .gitea/workflows # 2. Create main workflow file with Arcodange-specific configuration cat > .gitea/workflows/ci-cd.yaml << 'EOF' name: DanceLessonsCoach CI/CD on: push: branches: [ main ] pull_request: branches: [ main ] # Arcodange-specific environment variables env: GITEA_INTERNAL: "https://gitea.arcodange.lab/" GITEA_EXTERNAL: "https://gitea.arcodange.fr/" CI_REGISTRY: "registry.arcodange.lab" jobs: build-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: '1.26.1' - run: go mod tidy - run: go build ./... - run: go test ./... -cover # Use internal URL for API calls within Arcodange network - name: Notify internal systems if: always() run: | curl -X POST "$GITEA_INTERNAL/api/v1/repos/yourorg/DanceLessonsCoach/statuses/$(git rev-parse HEAD)" \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"state\": \"$([ $? -eq 0 ] && echo 'success' || echo 'failure')\", \"context\": \"ci/build-test\"}" EOF # 3. Enable Gitea CI/CD in repo settings (Arcodange instance) # - Go to: https://gitea.arcodange.lab/arcodange/DanceLessonsCoach/settings/actions # - Enable GitHub Actions # - Configure runner to use internal network (192.168.1.202) # - Set up GITEA_TOKEN for API access # - SSH URL: ssh://git@192.168.1.202:2222/arcodange/DanceLessonsCoach.git # 4. Add STATUS_BADGES.md with Arcodange-specific URLs cat > STATUS_BADGES.md << 'EOF' ## Arcodange Gitea Badges ```markdown [![Build Status](https://gitea.arcodange.fr/api/badges/arcodange/DanceLessonsCoach/status)](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach) [![Pipeline](https://gitea.arcodange.fr/api/badges/arcodange/DanceLessonsCoach/pipeline.svg)](https://gitea.arcodange.fr/arcodange/DanceLessonsCoach/-/pipelines) ``` **Configuration Details:** - Organization: arcodange - Repository: DanceLessonsCoach - Internal URL: https://gitea.arcodange.lab/ - External URL: https://gitea.arcodange.fr/ - SSH URL: ssh://git@192.168.1.202:2222/arcodange/DanceLessonsCoach.git - Badges use external URL with full org/repo path - CI/CD uses internal URL for faster network access EOF # 5. Configure CI/CD runners on internal network # - Set up runners to access: https://gitea.arcodange.lab/ # - Configure SSH access: ssh://git@192.168.1.202:2222/arcodange/DanceLessonsCoach.git # - Ensure runners have network access to internal services (192.168.1.202:2222) # - Configure runners with proper GITEA_TOKEN # - Test connection: curl https://gitea.arcodange.lab/api/v1/version ``` ### Phase 2: Linting and Quality (Week 2) ```bash # 1. Add golangci-lint configuration cat > .golangci.yml << 'EOF' run: timeout: 5m issues-exit-code: 1 tests: false linters: enable: - errcheck - gofmt - goimports - gosimple - govet - ineffassign - staticcheck - unused EOF # 2. Update workflow to include linting cat >> .github/workflows/main.yml << 'EOF' lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: '1.26.1' - uses: golangci/golangci-lint-action@v3 with: version: v1.55 EOF # 3. Add pre-commit hooks cat > .pre-commit-config.yaml << 'EOF' repos: - repo: https://github.com/golangci/golangci-lint rev: v1.55.2 hooks: - id: golangci-lint - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml EOF ``` ### Phase 3: Version Management (Week 3) ```bash # 1. Enhance version-bump.sh for auto-detection cat > scripts/version-bump-auto.sh << 'EOF' #!/bin/bash # Auto-detect version bump based on conventional commits # Get last commit message LAST_COMMIT=$(git log -1 --pretty=%B) if echo "$LAST_COMMIT" | grep -q "^feat:"; then echo "📋 Detected feature commit, bumping MINOR version" ./scripts/version-bump.sh minor elif echo "$LAST_COMMIT" | grep -q "^fix:"; then echo "🐛 Detected fix commit, bumping PATCH version" ./scripts/version-bump.sh patch elif echo "$LAST_COMMIT" | grep -q "BREAKING CHANGE"; then echo "💥 Detected breaking change, bumping MAJOR version" ./scripts/version-bump.sh major else echo "⏭️ No version bump needed for this commit type" fi EOF # 2. Add version management to workflow # 3. Create CHANGELOG.md auto-generation ``` ### Phase 4: Badges and Status (Week 4) ```bash # 1. Add badge documentation cat > STATUS_BADGES.md << 'EOF' # CI/CD Status Badges ## Gitea (Primary) ```markdown [![Build Status](https://ci.your-gitea-instance.com/api/badges/project/status)](https://ci.your-gitea-instance.com) ``` ## GitHub Mirror ```markdown [![GitHub CI](https://github.com/yourorg/DanceLessonsCoach/actions/workflows/main.yml/badge.svg)](https://github.com/yourorg/DanceLessonsCoach/actions) ``` ## GitLab Mirror ```markdown [![GitLab CI](https://gitlab.com/yourorg/DanceLessonsCoach/badges/main/pipeline.svg)](https://gitlab.com/yourorg/DanceLessonsCoach/-/pipelines) ``` ## Code Quality ```markdown [![Go Report Card](https://goreportcard.com/badge/github.com/yourorg/DanceLessonsCoach)](https://goreportcard.com/report/github.com/yourorg/DanceLessonsCoach) [![Code Coverage](https://codecov.io/gh/yourorg/DanceLessonsCoach/branch/main/graph/badge.svg)](https://codecov.io/gh/yourorg/DanceLessonsCoach) ``` EOF # 2. Add badges to README.md # 3. Set up external badge services ``` ### Phase 5: Mirror CI/CD Setup (Optional) ```bash # For GitHub Mirror: # 1. Enable GitHub Actions on the mirror repo # 2. Use the same .github/workflows/main.yml # 3. Configure mirror to not trigger builds on mirror pushes # For GitLab Mirror: # 1. Create .gitlab-ci.yml that imports GitHub Actions # 2. Or manually convert workflow to GitLab CI syntax # 3. Configure mirror to not trigger duplicate builds ``` ## Pros and Cons of Selected Approach ### ✅ Advantages 1. **Portability**: GitHub Actions YAML works on Gitea and GitHub natively 2. **Familiarity**: Most developers know GitHub Actions syntax 3. **Documentation**: Excellent GitHub Actions documentation available 4. **Marketplace**: Access to GitHub Actions marketplace actions 5. **Mirror Compatibility**: Same workflow files work on mirrors 6. **Tool Agnostic**: Can convert to other formats if needed 7. **Badges**: Easy to generate status badges for all platforms 8. **Extensible**: Can add more jobs/stages as needed ### ❌ Disadvantages 1. **GitLab Conversion**: May need manual conversion for GitLab CI 2. **Vendor Features**: Some GitHub-specific features won't work on Gitea 3. **Learning Curve**: Team needs to learn GitHub Actions syntax 4. **Mirror Complexity**: Need to prevent duplicate builds on mirrors 5. **Badge Management**: Multiple badge URLs to maintain ## Validation **Does this meet our requirements?** - ✅ **Gitea Compatibility**: Uses Gitea's GitHub Actions support - ✅ **GitHub Mirror**: Native GitHub Actions support - ✅ **GitLab Mirror**: Can use conversion tools or manual setup - ✅ **Portability**: Common workflow format across platforms - ✅ **Badges**: Status badges for all platforms - ✅ **Linting**: Integrated code quality checks - ✅ **Version Management**: Automatic version bumping - ✅ **Artifacts**: Binary releases and publishing **What's still needed?** - ❌ **Implementation**: Actual CI/CD pipeline setup - ❌ **Gitea Configuration**: CI/CD runner setup on Gitea instance - ❌ **Mirror Configuration**: Prevent duplicate builds - ❌ **Badge Services**: Set up external badge providers - ❌ **Testing**: Validate pipeline works on all platforms - ❌ **Documentation**: Complete CI/CD setup guide ## 🐳 Docker-Based Local Testing (Primary Method) **Arcodange uses Docker as the primary method for local CI/CD testing.** ### Comprehensive Testing Script Use the provided script for complete Docker-based testing: ```bash # Run complete CI/CD test suite ./scripts/test-cicd-docker.sh ``` **What the script does:** 1. ✅ Checks Docker availability 2. ✅ Pulls required Docker images 3. ✅ Validates YAML syntax with yq 4. ✅ Lints YAML with yamllint 5. ✅ Executes workflow with act runner 6. ✅ Reports success/failure ### Manual Docker Testing For more control, run individual Docker commands: ```bash # 1. Validate YAML syntax docker run --rm \ -v $(pwd):/workspace \ -w /workspace \ mikefarah/yq:latest \ yq eval '.' .gitea/workflows/ci-cd.yaml # 2. Lint YAML docker run --rm \ -v $(pwd):/workspace \ -w /workspace \ cybertanium/yamllint:latest \ yamllint .gitea/workflows/ # 3. Run workflow with Arcodange environment 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/ci-cd.yaml --rm ``` ### Alternative: Using nektos/act for GitHub Actions Compatibility ```bash # 1. Install act (GitHub Actions runner) brew install act # 2. Run workflow locally act -W .gitea/workflows/ci-cd.yaml \ --secret-file .secrets \ --env-file .env \ --container-architecture linux/amd64 # 3. With specific event simulation act push -W .gitea/workflows/ci-cd.yaml \ --env GITEA_ORG=arcodange \ --env GITEA_REPO=DanceLessonsCoach ``` ### Pipeline Status Checking Scripts Create `scripts/check-pipeline-status.sh`: ```bash #!/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" | 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" | jq -r '.[] | .name + " (" + .file_name + ")"') 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" | 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" | 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" echo "📊 Jobs: $(yq eval '.jobs | keys | join(", ")' .gitea/workflows/ci-cd.yaml)" else echo "❌ .gitea/workflows/ci-cd.yaml: Not found" fi echo "" echo "🎯 Validation Summary" echo "================================" echo "✅ Local workflow file: .gitea/workflows/ci-cd.yaml" echo "✅ Syntax validation: $(yq eval '.' .gitea/workflows/ci-cd.yaml > /dev/null 2>&1 && echo 'Valid YAML' || echo 'Invalid YAML')" 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" ``` ### Workflow Validation Script Create `scripts/validate-workflow.sh`: ```bash #!/bin/bash # Validate CI/CD workflow syntax and structure set -e echo "🔍 Validating CI/CD Workflow" echo "================================" # 1. Check workflow file exists if [ ! -f ".gitea/workflows/ci-cd.yaml" ]; then echo "❌ Workflow file not found: .gitea/workflows/ci-cd.yaml" exit 1 fi echo "✅ Workflow file found" # 2. Validate YAML syntax if ! yq eval '.' .gitea/workflows/ci-cd.yaml > /dev/null 2>&1; then echo "❌ Invalid YAML syntax" yq eval '.' .gitea/workflows/ci-cd.yaml || true exit 1 fi echo "✅ YAML syntax valid" # 3. Check required fields MISSING_FIELDS=() if [ -z "$(yq eval '.name' .gitea/workflows/ci-cd.yaml)" ]; then MISSING_FIELDS+=("name") fi if [ -z "$(yq eval '.on' .gitea/workflows/ci-cd.yaml)" ]; then MISSING_FIELDS+=("on") fi if [ -z "$(yq eval '.jobs' .gitea/workflows/ci-cd.yaml)" ]; then MISSING_FIELDS+=("jobs") fi if [ ${#MISSING_FIELDS[@]} -gt 0 ]; then echo "❌ Missing required fields: ${MISSING_FIELDS[*]}" exit 1 fi echo "✅ All required fields present" # 4. Check jobs structure JOBS=$(yq eval '.jobs | keys' .gitea/workflows/ci-cd.yaml) 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)" ]; 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) echo " ✅ $job_str: $steps_count steps" done # 5. Check Arcodange-specific configurations if [ -n "$(yq eval '.env.GITEA_INTERNAL' .gitea/workflows/ci-cd.yaml)" ]; 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)" ]; 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)" ]; then echo "✅ Concurrency control configured" else echo "⚠️ No concurrency control (consider adding)" fi echo "" echo "🎉 Workflow Validation Successful!" echo "================================" echo "📁 Location: .gitea/workflows/ci-cd.yaml" echo "🔧 Jobs: $JOBS" echo "🎯 Ready for deployment" ``` ### Docker Compose for Full Local Testing Create `docker-compose.cicd-test.yml`: ```yaml version: '3.8' services: act-runner: image: gitea/act_runner:latest volumes: - .:/workspace working_dir: /workspace environment: - GITEA_INTERNAL=https://gitea.arcodange.lab/ - GITEA_EXTERNAL=https://gitea.arcodange.fr/ - GITEA_ORG=arcodange - GITEA_REPO=DanceLessonsCoach command: act -W .gitea/workflows/ci-cd.yaml --rm yamllint: image: cybertanium/yamllint:latest volumes: - .:/workspace working_dir: /workspace command: yamllint .gitea/workflows/ yq-validator: image: mikefarah/yq:latest volumes: - .:/workspace working_dir: /workspace command: yq eval '.' .gitea/workflows/ci-cd.yaml ``` ### Testing Commands Summary ```bash # 1. Validate YAML syntax yq eval '.' .gitea/workflows/ci-cd.yaml # 2. Run local test docker run --rm -v $(pwd):/workspace -w /workspace gitea/act_runner:latest \ act -W .gitea/workflows/ci-cd.yaml # 3. Check pipeline status ./scripts/check-pipeline-status.sh # 4. Validate workflow structure ./scripts/validate-workflow.sh # 5. Full test with docker compose docker compose -f docker-compose.cicd-test.yml up ``` ## Future Enhancements 1. **Container Builds**: Add Docker image building and publishing 2. **Security Scanning**: Integrate vulnerability scanning 3. **Performance Testing**: Add benchmark tests to pipeline 4. **Deployment**: Add deployment stages for different environments 5. **Release Notes**: Auto-generate release notes from commits 6. **Dependency Updates**: Automated dependency updates 7. **Multi-Arch Builds**: Support ARM64, Windows builds 8. **Matrix Testing**: Test across multiple Go versions ## References - [Gitea Actions Documentation](https://docs.gitea.com/next/usage/actions/) - [GitHub Actions Documentation](https://docs.github.com/en/actions) - [GitLab CI/CD Documentation](https://docs.gitlab.com/ee/ci/) - [Conventional Commits](https://www.conventionalcommits.org/) - [Semantic Versioning](https://semver.org/) - [GitHub Actions Marketplace](https://github.com/marketplace?type=actions) - [golangci-lint](https://golangci-lint.run/) - [Pre-commit Hooks](https://pre-commit.com/) --- **Status:** Proposed **Next Review:** 2026-04-12 **Implementation Owner:** Arcodange Team **Approvers Needed:** @gabrielradureau