diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16dd260..153ad27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,16 +6,48 @@ on: pull_request: branches: [main] +# Cancela runs anteriores do mesmo PR ou branch para economizar minutos de CI. +# Em push para main, cada run é independente (group usa o SHA para não cancelar). +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + permissions: {} env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: + # ─── Build ──────────────────────────────────────────────────────────────────── + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Instalar dependências + run: pnpm install --frozen-lockfile + + - name: Compilar projeto + run: pnpm build + # ─── Qualidade estática ──────────────────────────────────────────────────── qualidade: name: Lint e Typecheck runs-on: ubuntu-latest + needs: build + timeout-minutes: 10 permissions: contents: read @@ -43,6 +75,7 @@ jobs: name: Testes Unitários runs-on: ubuntu-latest needs: qualidade + timeout-minutes: 10 permissions: contents: read @@ -67,6 +100,7 @@ jobs: name: Testes E2E runs-on: ubuntu-latest needs: testes-unitarios + timeout-minutes: 10 permissions: contents: read @@ -120,11 +154,12 @@ jobs: - name: Testes E2E run: pnpm exec vitest run src/testes/e2e - # ─── Cobertura (suite completa: unitários + E2E) ─────────────────────────── + # ─── Cobertura + Quality Gate (suite completa: unitários + E2E) ─────────── cobertura: - name: Cobertura de Testes + name: Cobertura e Quality Gate runs-on: ubuntu-latest needs: testes-e2e + timeout-minutes: 10 permissions: contents: read @@ -175,11 +210,21 @@ jobs: - name: Instalar dependências run: pnpm install --frozen-lockfile - - name: Gerar relatório de cobertura + # O próprio vitest --coverage falha o processo se os thresholds configurados + # em vitest.config.ts não forem atingidos — este é o quality gate explícito. + - name: Gerar relatório de cobertura (quality gate) run: pnpm test:coverage - - name: Publicar cobertura + - name: Publicar cobertura no Codecov uses: codecov/codecov-action@v5 with: files: ./coverage/lcov.info fail_ci_if_error: false + + - name: Upload do relatório de cobertura como artifact + uses: actions/upload-artifact@v4 + if: always() + with: + name: relatorio-cobertura-${{ github.run_number }} + path: coverage/ + retention-days: 7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ecaaac4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,291 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +permissions: {} + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + # ─── Build ──────────────────────────────────────────────────────────────────── + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Instalar dependências + run: pnpm install --frozen-lockfile + + - name: Compilar projeto + run: pnpm build + + # ─── Qualidade estática ──────────────────────────────────────────────────── + qualidade: + name: Lint e Typecheck + runs-on: ubuntu-latest + needs: build + timeout-minutes: 10 + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Instalar dependências + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm lint + + - name: Typecheck + run: pnpm typecheck + + # ─── Testes Unitários ───────────────────────────────────────────────────── + testes-unitarios: + name: Testes Unitários + runs-on: ubuntu-latest + needs: qualidade + timeout-minutes: 10 + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Instalar dependências + run: pnpm install --frozen-lockfile + + - name: Testes unitários + run: pnpm exec vitest run src/testes/unitarios + + # ─── Testes E2E (MongoDB + Redis) ───────────────────────────────────────── + testes-e2e: + name: Testes E2E + runs-on: ubuntu-latest + needs: testes-unitarios + timeout-minutes: 10 + permissions: + contents: read + + services: + mongodb: + image: mongo:8.0 + env: + MONGO_INITDB_ROOT_USERNAME: administrador + MONGO_INITDB_ROOT_PASSWORD: 1qaz2sx12 + ports: + - 27017:27017 + options: >- + --health-cmd "mongosh --eval 'db.runCommand({ ping: 1 })'" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + redis: + image: redis:8 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 + + env: + MONGO_URI: mongodb://administrador:1qaz2sx12@localhost:27017/bem-vindo-testes?authSource=admin + REDIS_URL: redis://localhost:6379 + NODE_ENV: test + JWT_SECRETO: jwt-secreto-de-teste-com-pelo-menos-32-chars + JWT_EXPIRACAO_ACCESS: 1h + JWT_EXPIRACAO_REFRESH: 7d + PORTA: 3000 + TZ: America/Sao_Paulo + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Instalar dependências + run: pnpm install --frozen-lockfile + + - name: Testes E2E + run: pnpm exec vitest run src/testes/e2e + + # ─── Cobertura + Quality Gate ───────────────────────────────────────────── + cobertura: + name: Cobertura e Quality Gate + runs-on: ubuntu-latest + needs: testes-e2e + timeout-minutes: 10 + permissions: + contents: read + + services: + mongodb: + image: mongo:8.0 + env: + MONGO_INITDB_ROOT_USERNAME: administrador + MONGO_INITDB_ROOT_PASSWORD: 1qaz2sx12 + ports: + - 27017:27017 + options: >- + --health-cmd "mongosh --eval 'db.runCommand({ ping: 1 })'" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + redis: + image: redis:8 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 + + env: + MONGO_URI: mongodb://administrador:1qaz2sx12@localhost:27017/bem-vindo-testes?authSource=admin + REDIS_URL: redis://localhost:6379 + NODE_ENV: test + JWT_SECRETO: jwt-secreto-de-teste-com-pelo-menos-32-chars + JWT_EXPIRACAO_ACCESS: 1h + JWT_EXPIRACAO_REFRESH: 7d + PORTA: 3000 + TZ: America/Sao_Paulo + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Instalar dependências + run: pnpm install --frozen-lockfile + + - name: Gerar relatório de cobertura (quality gate) + run: pnpm test:coverage + + - name: Upload do relatório de cobertura como artifact + uses: actions/upload-artifact@v4 + if: always() + with: + name: relatorio-cobertura-${{ github.ref_name }} + path: coverage/ + retention-days: 30 + + # ─── Scan de segurança ──────────────────────────────────────────────────── + security-scan: + name: Scan de Segurança + runs-on: ubuntu-latest + needs: cobertura + timeout-minutes: 10 + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Instalar dependências + run: pnpm install --frozen-lockfile + + # Falha o pipeline em vulnerabilidades de severidade high ou critical. + - name: Auditoria de dependências + run: pnpm audit --audit-level=high + + # ─── Artifact de produção ───────────────────────────────────────────────── + artifact-build: + name: Artifact de Produção + runs-on: ubuntu-latest + needs: security-scan + timeout-minutes: 10 + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Instalar dependências (somente produção) + run: pnpm install --frozen-lockfile --prod + + - name: Compilar projeto + run: pnpm build + + - name: Upload do artifact de produção + uses: actions/upload-artifact@v4 + with: + name: dist-${{ github.ref_name }} + path: dist/ + retention-days: 30 + + # ─── Aprovação manual antes de produção ─────────────────────────────────── + aprovacao-producao: + name: Aprovacao para Producao + runs-on: ubuntu-latest + needs: artifact-build + timeout-minutes: 10 + environment: producao + permissions: + contents: read + + steps: + - name: Release ${{ github.ref_name }} aprovado para producao + run: | + echo "Tag: ${{ github.ref_name }}" + echo "Commit: ${{ github.sha }}" + echo "Aprovado por: ${{ github.actor }}" + echo "Release liberado para producao." diff --git a/vitest.config.ts b/vitest.config.ts index 780febc..9d16845 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -17,6 +17,7 @@ export default defineConfig({ lines: 80, functions: 80, branches: 80, + statements: 80, }, }, },