diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b0393d1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "ci" + include: "scope" + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "deps" + include: "scope" diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 33c2372..d431ce2 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -12,16 +12,18 @@ jobs: permissions: contents: read packages: write + attestations: write + id-token: write steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a with: registry: ghcr.io username: ${{ github.actor }} @@ -29,7 +31,7 @@ jobs: - name: Extract metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 with: images: ghcr.io/${{ github.repository }} tags: | @@ -40,7 +42,8 @@ jobs: type=sha,format=long - name: Build and push - uses: docker/build-push-action@v5 + id: push + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 with: context: . push: true @@ -48,6 +51,14 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64 + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v3 + with: + subject-name: ghcr.io/${{ github.repository}} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true deploy: name: Deploy to Production @@ -57,7 +68,7 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.2.0 - name: Extract tag name id: tag diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 0000000..a2ae90f --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,197 @@ +name: CI/CD Pipeline + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + # Security scanning with CodeQL (only on main branch pushes) + security-scan: + name: Security Scan + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.2.0 + + - name: Initialize CodeQL + uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.12 + with: + languages: go + + - name: Setup Go + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35 # v5.4.0 + with: + go-version: "1.25" + cache: true + + - name: Install dependencies + run: go mod download + + - name: Build + run: go build -v ./... + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.12 + + # Linting (runs on PRs and pushes) + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.2.0 + + - name: Setup Go + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35 # v5.4.0 + with: + go-version: "1.25" + cache: true + + - name: golangci-lint + uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v6.1.1 + with: + version: latest + args: --timeout=5m + + # Testing and coverage (runs on PRs and pushes) + test: + name: Test + runs-on: ubuntu-latest + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: testdb + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + redis: + image: redis:7-alpine + ports: + - 6379:6379 + options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.2.0 + + - name: Setup Go + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35 # v5.4.0 + with: + go-version: "1.25" + cache: true + + - name: Install dependencies + run: go mod download + + - name: Run tests with coverage + run: | + go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + go tool cover -html=coverage.out -o coverage.html + + - name: Upload coverage reports + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: coverage-report + path: coverage.html + retention-days: 30 + + # Build and verify (runs on PRs and pushes) + build: + name: Build + runs-on: ubuntu-latest + permissions: + contents: read + attestations: write + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.2.0 + + - name: Setup Go + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35 # v5.4.0 + with: + go-version: "1.25" + cache: true + + - name: Install dependencies + run: go mod download + + - name: Verify dependencies + run: go mod verify + + - name: Build application + run: | + go build -v -o bin/tercul-backend ./cmd + ls -la bin/ + + - name: Test build + run: ./bin/tercul-backend --help || echo "Binary built successfully" + + - name: Upload build artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: terbul-backend-binary + path: bin/ + retention-days: 30 + + # Matrix testing across Go versions (only on main branch pushes) + test-matrix: + name: Test Matrix (Go ${{ matrix.go-version }}) + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + strategy: + matrix: + go-version: ["1.22", "1.23", "1.24", "1.25"] + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: testdb + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + redis: + image: redis:7-alpine + ports: + - 6379:6379 + options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.2.0 + + - name: Setup Go ${{ matrix.go-version }} + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35 # v5.4.0 + with: + go-version: ${{ matrix.go-version }} + cache: true + + - name: Install dependencies + run: go mod download + + - name: Run tests + run: go test -v -race ./... + + - name: Build + run: go build -v ./... diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15b63e2..b3c08bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,26 +2,40 @@ name: CI on: push: - branches: [ "main" ] + branches: [main] pull_request: - branches: [ "main" ] + branches: [main] jobs: lint-and-test: runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # For code scanning steps: - - uses: actions/checkout@v3 + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.2.0 - - name: Set up Go - uses: actions/setup-go@v4 + - name: Setup Go + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35 # v5.4.0 with: - go-version: '1.24.3' + go-version: "1.25" + cache: true - - name: Run lint-test - uses: golangci/golangci-lint-action@v3 + - name: Install dependencies + run: go mod download + + - name: Lint + uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v6.1.1 with: - version: v1.54 + version: latest args: --timeout=5m - - name: Run tests - run: go test ./... \ No newline at end of file + - name: Test + run: go test -v -race -coverprofile=coverage.out ./... + + - name: Upload coverage + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: coverage + path: coverage.out