name: CI on: push: branches: [master, main, develop] pull_request: branches: [master, main, develop] permissions: contents: read checks: write pull-requests: write concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: GO_VERSION: '1.24' jobs: # Stage 1: Format Check (fastest - fail immediately if code isn't formatted) fmt: name: Format Check runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} - name: Check formatting run: | unformatted=$(gofmt -s -l . | grep -v vendor || true) if [ -n "$unformatted" ]; then echo "❌ The following files are not properly formatted:" echo "$unformatted" echo "" echo "Run 'gofmt -s -w .' to fix formatting issues" exit 1 fi echo "✅ All files are properly formatted" # Stage 2: Lint (depends on fmt) lint: name: Lint runs-on: ubuntu-latest needs: fmt steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} - name: Cache Go modules uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go-${{ env.GO_VERSION }}- - name: Download dependencies run: go mod download - name: Run go vet run: go vet ./... - name: Run golangci-lint uses: golangci/golangci-lint-action@v6 with: version: latest args: --timeout=5m --out-format=github-actions # Stage 3: Test with Coverage (depends on lint) test: name: Test & Coverage runs-on: ubuntu-latest needs: lint steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 # Full history for SonarCloud - name: Set up Go uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} - name: Cache Go modules uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go-${{ env.GO_VERSION }}- - name: Download dependencies run: go mod download - name: Run tests with coverage run: | go test -v -race -covermode=atomic -coverprofile=coverage.out -json ./... > test-report.json 2>&1 || true # Ensure coverage file exists even if tests fail if [ ! -f coverage.out ]; then echo "mode: atomic" > coverage.out fi - name: Display coverage summary run: | echo "📊 Coverage Summary:" go tool cover -func=coverage.out | tail -20 - name: Upload coverage artifact uses: actions/upload-artifact@v4 with: name: coverage-reports path: | coverage.out test-report.json retention-days: 7 - name: Upload to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.out flags: unittests name: codecov-onvif-go fail_ci_if_error: true verbose: true # Stage 4: SonarCloud Analysis (depends on test) sonarcloud: name: SonarCloud Analysis runs-on: ubuntu-latest needs: test steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 # Full history for accurate blame information - name: Download coverage reports uses: actions/download-artifact@v4 with: name: coverage-reports - name: Verify coverage file run: | echo "📁 Downloaded files:" ls -la if [ -f coverage.out ]; then echo "✅ Coverage file found" head -5 coverage.out else echo "⚠️ Coverage file not found, creating empty one" echo "mode: atomic" > coverage.out fi - name: SonarCloud Scan uses: SonarSource/sonarcloud-github-action@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Stage 5: Build Verification (depends on test, runs in parallel with sonarcloud) build: name: Build Verification runs-on: ubuntu-latest needs: test steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} - name: Cache Go modules uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go-${{ env.GO_VERSION }}- - name: Download dependencies run: go mod download - name: Build library run: go build -v ./... - name: Build CLI tools run: | echo "🔨 Building CLI tools..." go build -v -o bin/onvif-cli ./cmd/onvif-cli go build -v -o bin/onvif-quick ./cmd/onvif-quick go build -v -o bin/onvif-server ./cmd/onvif-server go build -v -o bin/onvif-diagnostics ./cmd/onvif-diagnostics echo "✅ All CLI tools built successfully" # Final status check ci-success: name: CI Success runs-on: ubuntu-latest needs: [fmt, lint, test, sonarcloud, build] if: always() steps: - name: Check all jobs status run: | if [[ "${{ needs.fmt.result }}" != "success" ]]; then echo "❌ Format check failed" exit 1 fi if [[ "${{ needs.lint.result }}" != "success" ]]; then echo "❌ Lint check failed" exit 1 fi if [[ "${{ needs.test.result }}" != "success" ]]; then echo "❌ Tests failed" exit 1 fi if [[ "${{ needs.sonarcloud.result }}" != "success" ]]; then echo "❌ SonarCloud analysis failed" exit 1 fi if [[ "${{ needs.build.result }}" != "success" ]]; then echo "❌ Build verification failed" exit 1 fi echo "✅ All CI checks passed successfully!"