fix(ui): standardize spinners to Loader2 + normalize bg colors #1121
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: NDSEP CI/CD Pipeline | |
| on: | |
| push: | |
| branches: [main, develop, staging, 'devin/**'] | |
| pull_request: | |
| branches: [main, develop] | |
| env: | |
| NODE_VERSION: '22' | |
| GO_VERSION: '1.21' | |
| PYTHON_VERSION: '3.11' | |
| RUST_VERSION: 'stable' | |
| jobs: | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Node.js / TypeScript — tRPC server + React client | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| node-ci: | |
| name: Node.js CI (TypeScript + Tests) | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: ndsep_user | |
| POSTGRES_PASSWORD: ndsep_test_pass | |
| POSTGRES_DB: ndsep_test | |
| ports: ['5432:5432'] | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| env: | |
| DATABASE_URL: postgresql://ndsep_user:ndsep_test_pass@localhost:5432/ndsep_test | |
| JWT_SECRET: test-jwt-secret-for-ci-only | |
| FIELD_ENCRYPTION_KEY: 0123456789abcdef0123456789abcdef | |
| NODE_ENV: test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v3 | |
| with: { version: '10.4.1' } | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm exec drizzle-kit push --force | |
| name: Apply database migrations | |
| - run: pnpm exec tsc --noEmit | |
| name: TypeScript check | |
| - run: pnpm test --reporter=verbose | |
| name: Unit tests | |
| - run: pnpm run build | |
| name: Build | |
| - uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: test-results | |
| path: test-results/ | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Go workers — build + vet + test all 15 workers | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| go-ci: | |
| name: Go CI (Build + Vet + Test) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| cache-dependency-path: workers/go/go.sum | |
| - name: Download Go dependencies | |
| working-directory: workers/go | |
| run: go mod download | |
| - name: Go vet (all workers) | |
| working-directory: workers/go | |
| run: | | |
| for dir in cmd/*/; do | |
| echo "Vetting $dir..." | |
| go vet ./$dir || exit 1 | |
| done | |
| - name: Go build (all workers) | |
| working-directory: workers/go | |
| run: | | |
| mkdir -p bin | |
| for dir in cmd/*/; do | |
| name=$(basename $dir) | |
| echo "Building $name..." | |
| go build -ldflags="-s -w -X main.Version=${{ github.sha }}" -o bin/$name ./$dir || exit 1 | |
| done | |
| - name: Go test | |
| working-directory: workers/go | |
| run: go test ./... -v -timeout 60s | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: go-binaries | |
| path: workers/go/bin/ | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Python orchestration services | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| python-ci: | |
| name: Python CI (Lint + Test) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| cache: pip | |
| - name: Install dependencies | |
| run: | | |
| pip install --upgrade pip | |
| pip install flake8 pytest pytest-asyncio | |
| find orchestration -name requirements.txt -exec pip install -r {} \; | |
| - name: Lint with flake8 | |
| run: flake8 orchestration/ workers/python/ --max-line-length=120 --ignore=E501,W503 | |
| continue-on-error: true | |
| - name: Run Python tests | |
| run: pytest orchestration/ -v --tb=short | |
| continue-on-error: true | |
| - name: Python security scan (bandit) | |
| run: | | |
| pip install bandit | |
| bandit -r workers/python/ orchestration/ -ll -ii --format json -o bandit-results.json || true | |
| - uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: python-security | |
| path: bandit-results.json | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Rust workers | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| rust-ci: | |
| name: Rust CI (Check + Test) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: workers/rust -> target | |
| - name: Rust format check | |
| working-directory: workers/rust | |
| run: cargo fmt --check | |
| continue-on-error: true | |
| - name: Rust clippy (deny warnings) | |
| working-directory: workers/rust | |
| run: cargo clippy -- -D warnings | |
| continue-on-error: true | |
| - name: Rust security audit | |
| run: | | |
| cargo install cargo-audit 2>/dev/null || true | |
| cd workers/rust && cargo audit || true | |
| - name: Rust build | |
| working-directory: workers/rust | |
| run: cargo build --release | |
| - name: Rust tests | |
| working-directory: workers/rust | |
| run: cargo test --release | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Security scanning | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| security: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: fs | |
| scan-ref: '.' | |
| format: sarif | |
| output: trivy-results.sarif | |
| severity: CRITICAL,HIGH | |
| continue-on-error: true | |
| - uses: github/codeql-action/upload-sarif@v3 | |
| if: always() | |
| with: | |
| sarif_file: trivy-results.sarif | |
| continue-on-error: true | |
| - uses: pnpm/action-setup@v3 | |
| with: | |
| version: 10 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: pnpm | |
| - name: Audit npm packages (fail on high/critical) | |
| run: pnpm audit --audit-level=high | |
| continue-on-error: true | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Integration tests (E2E with Playwright) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| integration: | |
| name: Integration Tests (E2E) | |
| runs-on: ubuntu-latest | |
| needs: [node-ci] | |
| if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging' | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: ndsep_user | |
| POSTGRES_PASSWORD: ndsep_test_pass | |
| POSTGRES_DB: ndsep_test | |
| ports: ['5432:5432'] | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| env: | |
| DATABASE_URL: postgresql://ndsep_user:ndsep_test_pass@localhost:5432/ndsep_test | |
| JWT_SECRET: test-jwt-secret-for-ci-only | |
| NODE_ENV: test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v3 | |
| with: { version: '10.4.1' } | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm exec playwright install --with-deps chromium | |
| - run: pnpm run build && pnpm start & | |
| name: Start server | |
| - run: sleep 5 && pnpm exec playwright test | |
| name: Run E2E tests | |
| continue-on-error: true | |
| - uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: playwright-report | |
| path: test-results/ | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Docker build + push (on main branch only) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| docker: | |
| name: Docker Build & Push | |
| runs-on: ubuntu-latest | |
| needs: [node-ci, go-ci, python-ci, security] | |
| if: github.ref == 'refs/heads/main' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| push: true | |
| tags: | | |
| ghcr.io/${{ github.repository }}:latest | |
| ghcr.io/${{ github.repository }}:${{ github.sha }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| build-args: | | |
| BUILD_SHA=${{ github.sha }} | |
| BUILD_DATE=${{ github.event.head_commit.timestamp }} |