diff --git a/.github/workflows/accessibility.yml b/.github/workflows/accessibility.yml.disabled similarity index 100% rename from .github/workflows/accessibility.yml rename to .github/workflows/accessibility.yml.disabled diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml.disabled similarity index 100% rename from .github/workflows/cd.yml rename to .github/workflows/cd.yml.disabled diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml.disabled similarity index 100% rename from .github/workflows/chromatic.yml rename to .github/workflows/chromatic.yml.disabled diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b580f616c..c23a564c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Veza CI/CD +name: Veza CI on: push: @@ -13,27 +13,14 @@ env: jobs: # =========================================================================== - # TMT Vital — Backend (Go) + # Backend (Go) — build, test, lint, security # =========================================================================== - vital-backend: - name: TMT Vital — Backend (Go) + backend: + name: Backend (Go) runs-on: ubuntu-latest + timeout-minutes: 15 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - - name: Check VERSION matches git tag - run: | - current_tag=$(git describe --tags --exact-match 2>/dev/null || true) - if [ -n "$current_tag" ]; then - version_file=$(cat VERSION) - tag_version=${current_tag#v} - if [ "$version_file" != "$tag_version" ]; then - echo "VERSION mismatch: VERSION=$version_file, current tag=$current_tag" - exit 1 - fi - fi - name: Set up Go uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 @@ -46,50 +33,39 @@ jobs: go install golang.org/x/vuln/cmd/govulncheck@latest go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - - name: Install TMT - run: apt-get update && apt-get install -y rsync && pip install tmt + - name: Build + run: go build ./... + working-directory: veza-backend-api - - name: Run TMT Vital Backend - run: tmt --root tmt run plan --name /vital-backend + - name: Test + run: go test ./... -count=1 -timeout 300s -coverprofile=coverage.out + working-directory: veza-backend-api - # =========================================================================== - # TMT Vital — Rust Services (Stream) - # =========================================================================== - vital-services: - name: TMT Vital — Rust Services - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Lint + run: golangci-lint run ./... --timeout 5m + working-directory: veza-backend-api - - name: Set up Rust + - name: Vet + run: go vet ./... + working-directory: veza-backend-api + + - name: Vulnerability check + run: govulncheck ./... + working-directory: veza-backend-api + + - name: Coverage summary run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --component rustfmt,clippy - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - - - name: Cache Cargo registry - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Install cargo-audit - run: cargo install cargo-audit - - - name: Install TMT - run: pip install tmt - - - name: Run TMT Vital Services - run: tmt --root tmt run plan --name /vital-services + COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}') + echo "## Backend Coverage: $COVERAGE" >> $GITHUB_STEP_SUMMARY + working-directory: veza-backend-api # =========================================================================== - # TMT Vital — Frontend (Web) + # Frontend (Web) — lint, typecheck, build, unit tests # =========================================================================== - vital-frontend: - name: TMT Vital — Frontend (Web) + frontend: + name: Frontend (Web) runs-on: ubuntu-latest + timeout-minutes: 15 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -100,288 +76,79 @@ jobs: cache: "npm" cache-dependency-path: package-lock.json - - name: Install Dependencies + - name: Install dependencies run: npm ci - - name: Cache Generated Types + - name: Lint + run: npx eslint --max-warnings=0 . + working-directory: apps/web + + - name: Typecheck + run: npx tsc --noEmit + working-directory: apps/web + + - name: Build + run: npm run build + working-directory: apps/web + + - name: Unit tests + run: npx vitest run --reporter=verbose + working-directory: apps/web + + # =========================================================================== + # Rust (Stream Server) — build, test, lint, audit + # =========================================================================== + rust: + name: Rust (Stream Server) + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set up Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --component rustfmt,clippy + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + + - name: Cache Cargo uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: - path: apps/web/src/types/generated - key: ${{ runner.os }}-generated-types-${{ hashFiles('veza-backend-api/openapi.yaml') }} - restore-keys: | - ${{ runner.os }}-generated-types- - - - name: Install TMT - run: pip install tmt - - - name: Run TMT Vital Frontend - run: tmt --root tmt run plan --name /vital-frontend - - # =========================================================================== - # Storybook Audit (kept outside TMT — tier 3 candidate) - # =========================================================================== - storybook: - name: Storybook Audit - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Set up Node - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 - with: - node-version: "20" - cache: "npm" - cache-dependency-path: package-lock.json - - - name: Install dependencies - run: npm ci - - - name: Build Storybook - run: npm run build-storybook - working-directory: apps/web - - - name: Serve Storybook and run audit - run: | - npx serve -s storybook-static -l 6007 & - for i in $(seq 1 30); do - if curl -sf http://localhost:6007 >/dev/null; then - echo "Storybook ready" - break - fi - sleep 2 - done - curl -sf http://localhost:6007 >/dev/null || (echo "Storybook failed to start"; exit 1) - npm run test:storybook - working-directory: apps/web - - # =========================================================================== - # E2E Critical (Playwright) — fast smoke, blocks PR - # =========================================================================== - e2e-critical: - name: E2E Critical (@critical) - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Set up Node - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 - with: - node-version: "20" - cache: "npm" - cache-dependency-path: package-lock.json - - - name: Set up Go - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 - with: - go-version: "1.24" - cache-dependency-path: veza-backend-api/go.sum - - - name: Install dependencies - run: npm ci - - - name: Add veza.fr to hosts (for Vite proxy) - run: echo "127.0.0.1 veza.fr" | sudo tee -a /etc/hosts - - - name: Start backend services (Postgres, Redis, RabbitMQ) - run: | - docker compose up -d postgres redis rabbitmq - echo "Waiting for Postgres..." - for i in $(seq 1 30); do - if docker exec veza_postgres pg_isready -U veza 2>/dev/null; then - echo "Postgres ready" - break - fi - sleep 2 - done - docker compose ps - - - name: Run database migrations - env: - DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable - run: | - cd veza-backend-api - go run cmd/migrate_tool/main.go - - - name: Create E2E test user - env: - DATABASE_URL: postgresql://veza:${{ secrets.E2E_DB_PASSWORD || 'devpassword' }}@localhost:15432/veza?sslmode=disable - TEST_EMAIL: e2e@test.com - TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }} - TEST_USERNAME: e2e - run: | - cd veza-backend-api - go run cmd/tools/create_test_user/main.go - - - name: Start backend API - env: - APP_ENV: development - APP_PORT: "18080" - DATABASE_URL: postgresql://veza:${{ secrets.E2E_DB_PASSWORD || 'devpassword' }}@localhost:15432/veza?sslmode=disable - REDIS_URL: redis://localhost:16379 - JWT_SECRET: ${{ secrets.E2E_JWT_SECRET }} - COOKIE_SECURE: "false" - CORS_ALLOWED_ORIGINS: http://veza.fr:5173,http://veza.fr:5174,http://localhost:5173,http://localhost:5174 - RABBITMQ_URL: ${{ secrets.E2E_RABBITMQ_URL }} - DISABLE_RATE_LIMIT_FOR_TESTS: "true" - ACCOUNT_LOCKOUT_EXEMPT_EMAILS: "e2e@test.com" - run: | - cd veza-backend-api - go build -o veza-api ./cmd/api/main.go - ./veza-api & - sleep 10 - curl -sf http://localhost:18080/api/v1/health > /tmp/health.json || (echo "Backend health check failed"; exit 1) - jq -e '.status == "ok"' /tmp/health.json || (echo "Health response invalid"; exit 1) - echo "Health check OK (status, DB/Redis connectivity verified)" - - - name: Install Playwright Browsers - run: npx playwright install --with-deps chromium - - - name: Run @critical E2E tests - run: npx playwright test --config=tests/e2e/playwright.config.ts --grep @critical --project=chromium - env: - PORT: "5174" - VITE_API_URL: "/api/v1" - VITE_DOMAIN: veza.fr - VITE_BACKEND_PORT: "18080" - PLAYWRIGHT_BASE_URL: "http://localhost:5174" - TEST_EMAIL: e2e@test.com - TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }} - - - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 - if: failure() - with: - name: playwright-critical-report - path: tests/e2e/playwright-report/ - retention-days: 7 - - # =========================================================================== - # E2E Full (Playwright) — sharded across 4 runners, all browsers - # =========================================================================== - e2e-full: - name: E2E Full (shard ${{ matrix.shard }}) - runs-on: ubuntu-latest - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - shard: [1/4, 2/4, 3/4, 4/4] - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Set up Node - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 - with: - node-version: "20" - cache: "npm" - cache-dependency-path: package-lock.json - - - name: Set up Go - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 - with: - go-version: "1.24" - cache-dependency-path: veza-backend-api/go.sum - - - name: Install dependencies - run: npm ci - - - name: Add veza.fr to hosts (for Vite proxy) - run: echo "127.0.0.1 veza.fr" | sudo tee -a /etc/hosts - - - name: Start backend services (Postgres, Redis, RabbitMQ) - run: | - docker compose up -d postgres redis rabbitmq - echo "Waiting for Postgres..." - for i in $(seq 1 30); do - if docker exec veza_postgres pg_isready -U veza 2>/dev/null; then - echo "Postgres ready" - break - fi - sleep 2 - done - docker compose ps - - - name: Run database migrations - env: - DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable - run: | - cd veza-backend-api - go run cmd/migrate_tool/main.go - - - name: Create E2E test user - env: - DATABASE_URL: postgresql://veza:${{ secrets.E2E_DB_PASSWORD || 'devpassword' }}@localhost:15432/veza?sslmode=disable - TEST_EMAIL: e2e@test.com - TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }} - TEST_USERNAME: e2e - run: | - cd veza-backend-api - go run cmd/tools/create_test_user/main.go - - - name: Start backend API - env: - APP_ENV: development - APP_PORT: "18080" - DATABASE_URL: postgresql://veza:${{ secrets.E2E_DB_PASSWORD || 'devpassword' }}@localhost:15432/veza?sslmode=disable - REDIS_URL: redis://localhost:16379 - JWT_SECRET: ${{ secrets.E2E_JWT_SECRET }} - COOKIE_SECURE: "false" - CORS_ALLOWED_ORIGINS: http://veza.fr:5173,http://veza.fr:5174,http://localhost:5173,http://localhost:5174 - RABBITMQ_URL: ${{ secrets.E2E_RABBITMQ_URL }} - DISABLE_RATE_LIMIT_FOR_TESTS: "true" - ACCOUNT_LOCKOUT_EXEMPT_EMAILS: "e2e@test.com" - run: | - cd veza-backend-api - go build -o veza-api ./cmd/api/main.go - ./veza-api & - sleep 10 - curl -sf http://localhost:18080/api/v1/health > /tmp/health.json || (echo "Backend health check failed"; exit 1) - jq -e '.status == "ok"' /tmp/health.json || (echo "Health response invalid"; exit 1) - echo "Health check OK (status, DB/Redis connectivity verified)" - - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - - name: Run E2E tests (sharded) - run: npx playwright test --config=tests/e2e/playwright.config.ts --shard=${{ matrix.shard }} - env: - PORT: "5174" - VITE_API_URL: "/api/v1" - VITE_DOMAIN: veza.fr - VITE_BACKEND_PORT: "18080" - PLAYWRIGHT_BASE_URL: "http://localhost:5174" - TEST_EMAIL: e2e@test.com - TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }} - - - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 - if: always() - with: - name: playwright-report-shard-${{ strategy.job-index }} path: | - tests/e2e/playwright-report/ - tests/e2e/test-results/ - retention-days: 7 + ~/.cargo/registry + ~/.cargo/git + veza-stream-server/target + key: ${{ runner.os }}-cargo-${{ hashFiles('veza-stream-server/Cargo.lock') }} + + - name: Build + run: cargo build + working-directory: veza-stream-server + + - name: Test + run: cargo test --workspace + working-directory: veza-stream-server + + - name: Clippy + run: cargo clippy --all-targets -- -D warnings + working-directory: veza-stream-server + + - name: Format check + run: cargo fmt -- --check + working-directory: veza-stream-server + + - name: Security audit + run: | + cargo install cargo-audit 2>/dev/null || true + cargo audit + working-directory: veza-stream-server # =========================================================================== # Notify on failure # =========================================================================== notify-failure: name: Notify on failure - needs: - [ - vital-backend, - vital-services, - vital-frontend, - storybook, - e2e-critical, - e2e-full, - ] + needs: [backend, frontend, rust] if: failure() runs-on: ubuntu-latest steps: - - name: Slack notification - if: secrets.SLACK_WEBHOOK_URL != '' - run: | - curl -X POST -H 'Content-type: application/json' \ - --data "{\"text\":\"CI failed on ${{ github.repository }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \ - "${{ secrets.SLACK_WEBHOOK_URL }}" + - name: Summary + run: echo "## ❌ CI Failed" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml.disabled similarity index 100% rename from .github/workflows/commitlint.yml rename to .github/workflows/commitlint.yml.disabled diff --git a/.github/workflows/container-scan.yml b/.github/workflows/container-scan.yml.disabled similarity index 100% rename from .github/workflows/container-scan.yml rename to .github/workflows/container-scan.yml.disabled diff --git a/.github/workflows/contract-testing.yml b/.github/workflows/contract-testing.yml.disabled similarity index 100% rename from .github/workflows/contract-testing.yml rename to .github/workflows/contract-testing.yml.disabled diff --git a/.github/workflows/flaky-report.yml b/.github/workflows/flaky-report.yml.disabled similarity index 100% rename from .github/workflows/flaky-report.yml rename to .github/workflows/flaky-report.yml.disabled diff --git a/.github/workflows/load-test-nightly.yml b/.github/workflows/load-test-nightly.yml.disabled similarity index 100% rename from .github/workflows/load-test-nightly.yml rename to .github/workflows/load-test-nightly.yml.disabled diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml.disabled similarity index 100% rename from .github/workflows/mutation-testing.yml rename to .github/workflows/mutation-testing.yml.disabled diff --git a/.github/workflows/openapi-lint.yml b/.github/workflows/openapi-lint.yml.disabled similarity index 100% rename from .github/workflows/openapi-lint.yml rename to .github/workflows/openapi-lint.yml.disabled diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml.disabled similarity index 100% rename from .github/workflows/performance.yml rename to .github/workflows/performance.yml.disabled diff --git a/.github/workflows/rust-mutation.yml b/.github/workflows/rust-mutation.yml.disabled similarity index 100% rename from .github/workflows/rust-mutation.yml rename to .github/workflows/rust-mutation.yml.disabled diff --git a/.github/workflows/sast.yml b/.github/workflows/sast.yml.disabled similarity index 100% rename from .github/workflows/sast.yml rename to .github/workflows/sast.yml.disabled diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 03196523a..adbdb89d1 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -5,11 +5,9 @@ on: branches: [main] pull_request: branches: [main] - workflow_dispatch: env: GIT_SSL_NO_VERIFY: "true" - NODE_TLS_REJECT_UNAUTHORIZED: "0" jobs: gitleaks: @@ -20,7 +18,11 @@ jobs: with: fetch-depth: 0 - - name: Run Gitleaks - uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196e88a9c30 # v2.3.8 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Install gitleaks + run: | + wget -q https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz + tar xzf gitleaks_8.21.2_linux_x64.tar.gz + chmod +x gitleaks + + - name: Run gitleaks + run: ./gitleaks detect --source . --no-banner -v diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml.disabled similarity index 100% rename from .github/workflows/semgrep.yml rename to .github/workflows/semgrep.yml.disabled diff --git a/.github/workflows/staging-validation.yml b/.github/workflows/staging-validation.yml.disabled similarity index 100% rename from .github/workflows/staging-validation.yml rename to .github/workflows/staging-validation.yml.disabled diff --git a/.github/workflows/storybook-audit.yml b/.github/workflows/storybook-audit.yml.disabled similarity index 100% rename from .github/workflows/storybook-audit.yml rename to .github/workflows/storybook-audit.yml.disabled diff --git a/.github/workflows/stream-ci.yml b/.github/workflows/stream-ci.yml index 3d0ceb472..0d552642a 100644 --- a/.github/workflows/stream-ci.yml +++ b/.github/workflows/stream-ci.yml @@ -36,9 +36,10 @@ jobs: run: cargo clippy --all-targets -- -D warnings - name: Audit dependencies - uses: actions-rust-lang/audit@v1 # TODO: pin to SHA — no known mapping provided - with: - token: ${{ secrets.GITHUB_TOKEN }} + run: | + cargo install cargo-audit 2>/dev/null || true + cargo audit + working-directory: veza-stream-server - name: Run tests run: cargo test --all diff --git a/.github/workflows/trivy-fs.yml b/.github/workflows/trivy-fs.yml index e380f73c8..7741bb4e0 100644 --- a/.github/workflows/trivy-fs.yml +++ b/.github/workflows/trivy-fs.yml @@ -3,43 +3,22 @@ name: Trivy Filesystem Scan on: pull_request: branches: [main] + workflow_dispatch: env: GIT_SSL_NO_VERIFY: "true" - NODE_TLS_REJECT_UNAUTHORIZED: "0" jobs: trivy-scan: + name: Trivy FS Scan runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Run Trivy filesystem scan - uses: aquasecurity/trivy-action@master - with: - scan-type: "fs" - scan-ref: "." - severity: "CRITICAL,HIGH" - exit-code: "1" - format: "table" + - name: Install Trivy + run: | + wget -qO- https://github.com/aquasecurity/trivy/releases/download/v0.58.1/trivy_0.58.1_Linux-64bit.tar.gz | tar xz + chmod +x trivy - - name: Run Trivy (SARIF output) - if: always() - uses: aquasecurity/trivy-action@master - with: - scan-type: "fs" - scan-ref: "." - severity: "CRITICAL,HIGH" - exit-code: "0" - format: "sarif" - output: "trivy-results.sarif" - - - name: Upload Trivy SARIF - if: always() - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 - with: - name: trivy-results - path: trivy-results.sarif - retention-days: 30 + - name: Scan filesystem + run: ./trivy fs --severity HIGH,CRITICAL --exit-code 1 . diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml.disabled similarity index 100% rename from .github/workflows/visual-regression.yml rename to .github/workflows/visual-regression.yml.disabled diff --git a/.github/workflows/zap-dast.yml b/.github/workflows/zap-dast.yml.disabled similarity index 100% rename from .github/workflows/zap-dast.yml rename to .github/workflows/zap-dast.yml.disabled