name: Gated Tree CI/CD on: push: branches: - main - develop pull_request: branches: - main - develop permissions: contents: read security-events: write actions: read jobs: # Gate 1: Code Quality and Linting lint-cpp: name: Lint C++ Code runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install clang-format run: | sudo apt-get update sudo apt-get install -y clang-format - name: Check C++ formatting run: | # Check if there are any C++ files to format if find backend frontends/qt6 frontends/cli -name "*.cpp" -o -name "*.h" -o -name "*.hpp" | grep -q .; then echo "Checking C++ code formatting..." # Run clang-format in check mode (dry-run) find backend frontends/qt6 frontends/cli -name "*.cpp" -o -name "*.h" -o -name "*.hpp" | \ xargs clang-format --dry-run --Werror || \ (echo "❌ C++ code formatting issues found. Run 'clang-format -i' on the files." && exit 1) else echo "No C++ files found to check." fi lint-typescript: name: Lint TypeScript Code runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Setup bun uses: oven-sh/setup-bun@v2 - name: Install dependencies working-directory: frontends/nextjs run: bun install - name: Lint TypeScript working-directory: frontends/nextjs run: | # Run TypeScript compiler check bun run tsc --noEmit lint-python: name: Lint Python Code runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install ruff run: pip install ruff - name: Lint Python scripts run: | # Check if there are any Python files if find scripts -name "*.py" | grep -q .; then echo "Linting Python code..." ruff check scripts/ else echo "No Python files found to lint." fi # Gate 2: Build Components (depends on linting passing) build-backend: name: Build C++ Backend runs-on: ubuntu-latest needs: [lint-cpp, lint-python] steps: - uses: actions/checkout@v6 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ cmake \ ninja-build \ g++ \ libssl-dev \ zlib1g-dev \ libjsoncpp-dev \ uuid-dev \ libcurl4-openssl-dev - name: Setup Python for Conan uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install Conan run: pip install conan - name: Build backend working-directory: backend run: | # Create build directory mkdir -p build cd build # Configure with CMake cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release # Build ninja - name: Upload backend artifacts uses: actions/upload-artifact@v4 with: name: backend-build path: backend/build/wizardmerge-cli retention-days: 1 build-cli: name: Build CLI Frontend runs-on: ubuntu-latest needs: [lint-cpp] steps: - uses: actions/checkout@v6 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ cmake \ ninja-build \ g++ \ libcurl4-openssl-dev - name: Build CLI working-directory: frontends/cli run: | mkdir -p build cd build cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release ninja - name: Upload CLI artifacts uses: actions/upload-artifact@v4 with: name: cli-build path: frontends/cli/build/wizardmerge-cli-frontend retention-days: 1 build-qt6: name: Build Qt6 Frontend runs-on: ubuntu-latest needs: [lint-cpp] steps: - uses: actions/checkout@v6 - name: Install Qt6 run: | sudo apt-get update sudo apt-get install -y \ cmake \ ninja-build \ g++ \ qt6-base-dev \ qt6-declarative-dev \ libqt6svg6-dev - name: Build Qt6 Frontend working-directory: frontends/qt6 run: | mkdir -p build cd build cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release ninja - name: Upload Qt6 artifacts uses: actions/upload-artifact@v4 with: name: qt6-build path: frontends/qt6/build/wizardmerge-qt6 retention-days: 1 build-nextjs: name: Build Next.js Frontend runs-on: ubuntu-latest needs: [lint-typescript] steps: - uses: actions/checkout@v6 - name: Setup bun uses: oven-sh/setup-bun@v2 - name: Install dependencies working-directory: frontends/nextjs run: bun install - name: Build Next.js working-directory: frontends/nextjs run: bun run build - name: Upload Next.js artifacts uses: actions/upload-artifact@v4 with: name: nextjs-build path: frontends/nextjs/.next retention-days: 1 # Gate 3: Testing (depends on builds passing) test-backend: name: Test C++ Backend runs-on: ubuntu-latest needs: [build-backend] steps: - uses: actions/checkout@v6 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ cmake \ ninja-build \ g++ \ libssl-dev \ zlib1g-dev \ libjsoncpp-dev \ uuid-dev \ libcurl4-openssl-dev \ libgtest-dev - name: Setup Python for Conan uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install Conan run: pip install conan - name: Build and run tests working-directory: backend run: | mkdir -p build cd build cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON ninja # Run tests if they exist if [ -f "wizardmerge_tests" ]; then ./wizardmerge_tests else echo "No tests found, skipping test execution" fi test-tlaplus: name: TLA+ Specification Verification runs-on: ubuntu-latest needs: [lint-python] steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Install Java uses: actions/setup-java@v5 with: distribution: temurin java-version: 17 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Run TLC verification run: | python3 scripts/tlaplus.py run - name: Upload TLC results if: always() uses: actions/upload-artifact@v4 with: name: tlc-results path: ci-results/ retention-days: 7 # Gate 4: Security Scanning (depends on tests passing) security-codeql: name: CodeQL Security Analysis runs-on: ubuntu-latest needs: [test-backend, test-tlaplus] permissions: security-events: write actions: read contents: read steps: - uses: actions/checkout@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: cpp, python, javascript - name: Install dependencies for C++ run: | sudo apt-get update sudo apt-get install -y \ cmake \ ninja-build \ g++ \ libssl-dev \ zlib1g-dev \ libjsoncpp-dev \ uuid-dev \ libcurl4-openssl-dev - name: Setup Python for Conan uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install Conan run: pip install conan - name: Build for CodeQL working-directory: backend run: | mkdir -p build cd build cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release ninja - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:cpp,python,javascript" # Gate 5: Integration Tests (depends on security scanning) integration-tests: name: Integration Tests runs-on: ubuntu-latest needs: [security-codeql] steps: - uses: actions/checkout@v6 - name: Download backend artifact uses: actions/download-artifact@v4 with: name: backend-build path: backend/build - name: Make backend executable run: chmod +x backend/build/wizardmerge-cli - name: Run integration tests run: | echo "Starting backend server..." backend/build/wizardmerge-cli & SERVER_PID=$! # Wait for server to start sleep 5 # Test API endpoint echo "Testing merge API endpoint..." curl -X POST http://localhost:8080/api/merge \ -H "Content-Type: application/json" \ -d '{ "base": "line1\nline2\nline3", "ours": "line1\nmodified by us\nline3", "theirs": "line1\nmodified by them\nline3" }' || echo "API test completed" # Clean up kill $SERVER_PID || true # Gate 6: Deployment Gate (only on main branch, depends on all tests) deployment-ready: name: Deployment Ready runs-on: ubuntu-latest needs: [integration-tests] if: github.ref == 'refs/heads/main' steps: - name: Deployment gate passed run: | echo "✅ All gates passed!" echo "✅ Code quality checks passed" echo "✅ All components built successfully" echo "✅ Tests passed" echo "✅ Security scan completed" echo "✅ Integration tests passed" echo "" echo "🚀 Ready for deployment!" # Optional: Publish results to ci/test-results branch publish-results: name: Publish Test Results runs-on: ubuntu-latest needs: [integration-tests] if: github.ref == 'refs/heads/main' permissions: contents: write steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Download TLC results uses: actions/download-artifact@v4 with: name: tlc-results path: ci-results - name: Push results to ci/test-results branch run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git fetch origin git checkout -B ci/test-results mkdir -p ci/test-results cp -r ci-results/* ci/test-results/ || true git add ci/test-results git commit -m "CI results from run ${GITHUB_RUN_NUMBER}" || echo "No changes to commit" git push origin ci/test-results