From 6b6b8cd93764ce429faf41747822f1f99d521020 Mon Sep 17 00:00:00 2001 From: codens-agent Date: Mon, 29 Jun 2026 01:46:17 +0000 Subject: [PATCH 1/3] chore: Create .github/workflows/ci.yml with path-filtered frontend and backend CI jobs, coverage gate, caching, and concurrency groups --- .github/workflows/ci.yml | 154 +++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 64 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e88384be..86d1f168 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,95 +2,121 @@ name: CI on: push: + branches: + - main + - develop pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: - changes: + frontend-ci: runs-on: ubuntu-latest - outputs: - backend: ${{ steps.filter.outputs.backend }} + timeout-minutes: 20 steps: - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 id: filter with: filters: | - backend: - - 'backend/**' - - 'go.work' - - 'go.work.sum' + app: + - 'app/**' + - 'components/**' + - 'lib/**' + - 'middleware.ts' + - 'next.config.mjs' + - 'package.json' + - 'tsconfig.json' + - 'vitest.config.ts' + - '*.ts' + - '*.tsx' - backend: - needs: changes - if: needs.changes.outputs.backend == 'true' - runs-on: ubuntu-latest - defaults: - run: - working-directory: backend - services: - postgres: - image: postgres:16-alpine - env: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: testdb - options: >- - --health-cmd "pg_isready -U postgres" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' + with: + node-version: 20 - - uses: actions/setup-go@v5 + - uses: actions/cache@v4 + if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' with: - go-version: '1.25' + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - - name: Download dependencies - run: go mod download + - name: Install dependencies + if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' + run: npm ci - - name: Run SQLite unit tests - run: go test ./... -count=1 -timeout 60s + - name: Lint + if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' + run: npm run lint - - name: Run PostgreSQL integration tests - run: go test -tags integration ./internal/infrastructure/... -count=1 -timeout 120s - env: - DB_TYPE: postgres - DB_DSN: postgres://postgres:postgres@localhost:5432/testdb?sslmode=disable + - name: Typecheck + if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' + run: npx tsc --noEmit - - name: golangci-lint - uses: golangci/golangci-lint-action@v6 - with: - version: v2.1 - working-directory: backend - args: --timeout=5m + - name: Test + if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' + run: npx vitest run - - name: Upload coverage artifact - uses: actions/upload-artifact@v4 - with: - name: coverage - path: backend/coverage.out + - name: Build + if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' + run: npm run build - compat-tests: + backend-ci: runs-on: ubuntu-latest + timeout-minutes: 20 + defaults: + run: + working-directory: backend steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: dorny/paths-filter@v3 + id: filter with: - go-version: '1.25' + filters: | + backend: + - 'backend/**' - - name: Run compatibility tests - run: cd backend && go test ./internal/compat/... -v -count=1 -timeout 120s 2>&1 | tee compat-results.txt + - uses: actions/setup-go@v5 + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' + with: + go-version: '1.24' - - name: Upload compat test results - uses: actions/upload-artifact@v4 - if: always() + - uses: actions/cache@v4 + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' with: - name: compat-test-results - path: backend/compat-results.txt + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - validate-dashboards: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: node -e "const fs=require('fs'); const dir='deploy/grafana/dashboards'; fs.readdirSync(dir).filter(f=>f.endsWith('.json')).forEach(f=>{const d=JSON.parse(fs.readFileSync(dir+'/'+f,'utf8'));if(!d.uid||!d.schemaVersion)throw new Error('missing uid/schemaVersion in '+f);if(!d.templating||!d.templating.list.some(v=>v.name==='organization_id'))throw new Error('missing organization_id variable in '+f);console.log('OK',f)})" + - name: Vet + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' + run: go vet ./... + + - name: Install golangci-lint + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' + run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.62.0 + + - name: golangci-lint + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' + run: golangci-lint run ./... --timeout=5m + + - name: Test with race detector + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' + run: go test -race -coverprofile=coverage.out -covermode=atomic ./... + + - name: Check coverage threshold + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' + run: | + total=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//') + if (( $(echo "$total < 60" | bc -l) )); then + echo "Coverage ${total}% below 60% threshold" + exit 1 + fi + + - name: Build + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' + run: go build ./cmd/server From dd15f13af75942d45fd712bed91fe5839bfb436f Mon Sep 17 00:00:00 2001 From: codens-agent Date: Mon, 29 Jun 2026 01:49:10 +0000 Subject: [PATCH 2/3] chore: Create .github/workflows/ci.yml with path-filtered frontend and backend CI jobs, coverage gate, caching, and concurrency groups --- .github/workflows/ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86d1f168..bf65d0cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: - 'vitest.config.ts' - '*.ts' - '*.tsx' + - '.github/**' - uses: actions/setup-node@v4 if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' @@ -80,11 +81,15 @@ jobs: filters: | backend: - 'backend/**' + - '.golangci.yml' + - 'go.work' + - 'go.work.sum' + - '.github/**' - uses: actions/setup-go@v5 if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' with: - go-version: '1.24' + go-version: '1.25' - uses: actions/cache@v4 if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' @@ -98,7 +103,7 @@ jobs: - name: Install golangci-lint if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' - run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.62.0 + run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.1.0 - name: golangci-lint if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' From d3da75004df348d50e7e053cbbd2a59d9ebfba18 Mon Sep 17 00:00:00 2001 From: codens-agent Date: Mon, 29 Jun 2026 01:52:23 +0000 Subject: [PATCH 3/3] chore: Create .github/workflows/ci.yml with path-filtered frontend and backend CI jobs, coverage gate, caching, and concurrency groups --- .github/workflows/ci.yml | 70 ++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf65d0cc..dad5ed36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,6 @@ name: CI on: push: - branches: - - main - - develop pull_request: concurrency: @@ -33,7 +30,6 @@ jobs: - 'vitest.config.ts' - '*.ts' - '*.tsx' - - '.github/**' - uses: actions/setup-node@v4 if: steps.filter.outputs.app == 'true' || github.ref == 'refs/heads/main' @@ -72,6 +68,17 @@ jobs: defaults: run: working-directory: backend + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: testdb + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 10s + --health-timeout 5s + --health-retries 5 steps: - uses: actions/checkout@v4 @@ -84,12 +91,11 @@ jobs: - '.golangci.yml' - 'go.work' - 'go.work.sum' - - '.github/**' - uses: actions/setup-go@v5 if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' with: - go-version: '1.25' + go-version: '1.24' - uses: actions/cache@v4 if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' @@ -97,31 +103,67 @@ jobs: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - - name: Vet + - name: Download dependencies if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' - run: go vet ./... + run: go mod download - - name: Install golangci-lint + - name: Vet if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' - run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.1.0 + run: go vet ./... - name: golangci-lint if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' - run: golangci-lint run ./... --timeout=5m + uses: golangci/golangci-lint-action@v6 + with: + version: v2.1 + working-directory: backend + args: --timeout=5m - name: Test with race detector if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' run: go test -race -coverprofile=coverage.out -covermode=atomic ./... + - name: Run PostgreSQL integration tests + if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' + run: go test -tags integration ./internal/infrastructure/... -count=1 -timeout 120s + env: + DB_TYPE: postgres + DB_DSN: postgres://postgres:postgres@localhost:5432/testdb?sslmode=disable + - name: Check coverage threshold if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' run: | - total=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//') - if (( $(echo "$total < 60" | bc -l) )); then + total=$(go tool cover -func=coverage.out | awk '/^total:/ {gsub(/%/,"",$3); print $3}') + awk -v total="$total" 'BEGIN { exit (total >= 60 ? 0 : 1) }' || { echo "Coverage ${total}% below 60% threshold" exit 1 - fi + } - name: Build if: steps.filter.outputs.backend == 'true' || github.ref == 'refs/heads/main' run: go build ./cmd/server + + compat-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: '1.24' + + - name: Run compatibility tests + run: cd backend && go test ./internal/compat/... -v -count=1 -timeout 120s 2>&1 | tee compat-results.txt + + - name: Upload compat test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: compat-test-results + path: backend/compat-results.txt + + validate-dashboards: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: node -e "const fs=require('fs'); const dir='deploy/grafana/dashboards'; fs.readdirSync(dir).filter(f=>f.endsWith('.json')).forEach(f=>{const d=JSON.parse(fs.readFileSync(dir+'/'+f,'utf8'));if(!d.uid||!d.schemaVersion)throw new Error('missing uid/schemaVersion in '+f);if(!d.templating||!d.templating.list.some(v=>v.name==='organization_id'))throw new Error('missing organization_id variable in '+f);console.log('OK',f)})"