name: CI on: push: branches: [main, develop] pull_request: branches: [main, develop] env: NODE_VERSION: '22' DOCKER_PLATFORMS: linux/amd64,linux/arm64 jobs: lint: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Security audit run: npm audit --audit-level=critical - name: Run ESLint run: npm run lint # `nest build` uses tsconfig.build.json which excludes **/*spec.ts, and ts-jest/eslint don't # full-program type-check specs — so without this gate, type errors in spec files are invisible # to CI. tsconfig.json already includes both `src` and `test`, so this is the cheapest full-program # check covering specs (no separate tsconfig.spec.json needed). - name: Type-check full program (including specs) run: npx tsc --noEmit -p tsconfig.json - name: Check formatting run: npm run format -- --check - name: Check version consistency (docs track package.json) run: npm run check:versions # The committed openapi.json is the machine-readable API contract. Fail if a controller/DTO # change landed without regenerating the snapshot (npm run openapi:export). The generator # bootstraps the app hermetically (in-memory/temp SQLite, no listen), so this is a static check. - name: Check OpenAPI snapshot is up to date run: npm run openapi:check test: name: Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Run unit tests run: npm test -- --coverage - name: Run e2e smoke tests run: npm run test:e2e - name: Upload coverage uses: codecov/codecov-action@v7 if: always() with: files: ./coverage/lcov.info fail_ci_if_error: false test-postgres: name: Test (PostgreSQL migrations) runs-on: ubuntu-latest services: postgres: image: postgres:16-alpine env: POSTGRES_USER: openwa POSTGRES_PASSWORD: openwa POSTGRES_DB: openwa ports: - 5432:5432 options: >- --health-cmd "pg_isready -U openwa" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v7 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Build (compiles the data migrations to dist/) run: npm run build # Applies the full data-migration chain to a real Postgres and asserts every generated-uuid PK # has a DB DEFAULT — the dialect gap SQLite-only tests can't see. - name: Migrate + uuid-default smoke against PostgreSQL run: npm run test:pg-smoke env: DATABASE_TYPE: postgres DATABASE_HOST: localhost DATABASE_PORT: '5432' DATABASE_USERNAME: openwa DATABASE_PASSWORD: openwa DATABASE_NAME: openwa # Runtime-proves BuiltInFtsProvider on Postgres (websearch_to_tsquery + ts_headline against the # STORED body_ts tsvector). The spec self-skips unless DATABASE_TYPE=postgres, so it is a no-op # in the default test job and only executes here against the postgres:16 service. - name: Postgres FTS provider spec run: npx jest src/database/migrations/__tests__/1782400000000-AddMessagesFts.pg.spec.ts env: DATABASE_TYPE: postgres DATABASE_HOST: localhost DATABASE_PORT: '5432' DATABASE_USERNAME: openwa DATABASE_PASSWORD: openwa DATABASE_NAME: openwa dashboard: name: Dashboard runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' cache-dependency-path: dashboard/package-lock.json - name: Install dependencies run: cd dashboard && npm ci - name: Run ESLint run: cd dashboard && npm run lint - name: Check i18n parity run: cd dashboard && npm run i18n:check - name: Build dashboard run: cd dashboard && npm run build - name: Run dashboard unit tests run: cd dashboard && npm run test:unit build: name: Build runs-on: ubuntu-latest needs: [lint, test, dashboard] steps: - uses: actions/checkout@v7 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies run: npm ci - name: Build project run: npm run build - name: Upload build artifacts uses: actions/upload-artifact@v7 with: name: dist path: dist/ retention-days: 7 docker: name: Docker Build runs-on: ubuntu-latest needs: [build, test-postgres] permissions: contents: read packages: write steps: - uses: actions/checkout@v7 - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Login to GitHub Container Registry uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v6 with: images: ghcr.io/${{ github.repository }} tags: | type=ref,event=branch type=sha,prefix= type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} - name: Build and push uses: docker/build-push-action@v7 with: context: . push: true platforms: ${{ env.DOCKER_PLATFORMS }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} # provenance is generated by default (build-push-action v7); pin it explicitly and opt into # an SBOM attestation so each published image carries an in-toto SLSA provenance + SBOM pair, # verifiable via `docker buildx imagetools inspect`. provenance: true sbom: true cache-from: type=gha cache-to: type=gha,mode=max