fix(build): tolerate trailing commas when reading wrangler.jsonc #10
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: Main CI/CD Pipeline | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| env: | |
| NODE_VERSION: '20.9.0' | |
| PNPM_VERSION: '10.33.2' | |
| jobs: | |
| # Stage 1: Setup & Lint | |
| lint: | |
| name: Lint & Format | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'pnpm' | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v2 | |
| with: | |
| version: ${{ env.PNPM_VERSION }} | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Check formatting | |
| run: pnpm run prettier | |
| - name: Run ESLint | |
| run: pnpm run lint:check | |
| - name: Type checking | |
| run: pnpm run type-check | |
| # Stage 2: Unit & Integration Tests | |
| test: | |
| name: Test | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| needs: lint | |
| services: | |
| postgres: | |
| image: postgres:15-alpine | |
| env: | |
| POSTGRES_DB: test_db | |
| POSTGRES_USER: test_user | |
| POSTGRES_PASSWORD: test_password | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| redis: | |
| image: redis:7-alpine | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 6379:6379 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'pnpm' | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v2 | |
| with: | |
| version: ${{ env.PNPM_VERSION }} | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup test database | |
| env: | |
| DATABASE_URL: postgres://test_user:test_password@localhost:5432/test_db | |
| run: pnpm run migrate | |
| - name: Run unit tests | |
| env: | |
| DATABASE_URL: postgres://test_user:test_password@localhost:5432/test_db | |
| REDIS_URL: redis://localhost:6379 | |
| run: pnpm test -- --coverage | |
| - name: Upload coverage reports | |
| uses: codecov/codecov-action@v3 | |
| with: | |
| files: ./coverage/coverage-final.json | |
| flags: unittests | |
| fail_ci_if_error: true | |
| - name: Check coverage thresholds | |
| run: | | |
| # Fail if coverage below thresholds | |
| pnpm test:coverage -- --collectCoverageFrom="src/lib/**/*.ts" --coverageThreshold='{"src/lib/":{"branches":80,"functions":80,"lines":80,"statements":80}}' | |
| # Stage 3: Build | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: test | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'pnpm' | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v2 | |
| with: | |
| version: ${{ env.PNPM_VERSION }} | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build Next.js | |
| run: pnpm run build | |
| env: | |
| NODE_ENV: production | |
| - name: Analyze bundle size | |
| run: pnpm run analyze | |
| continue-on-error: true | |
| - name: Check bundle size | |
| run: | | |
| # Warn if bundle exceeds 500KB | |
| SIZE=$(stat -f%z .next/static/chunks/*.js 2>/dev/null | awk '{sum+=$1} END {print sum}') | |
| if [ $SIZE -gt 524288 ]; then | |
| echo "⚠️ Warning: Bundle size is $(( SIZE / 1024 ))KB (target: <500KB)" | |
| fi | |
| continue-on-error: true | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v3 | |
| with: | |
| name: next-build | |
| path: | | |
| .next | |
| public | |
| retention-days: 1 | |
| # Stage 4: Security Scanning | |
| security: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: lint | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'pnpm' | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v2 | |
| with: | |
| version: ${{ env.PNPM_VERSION }} | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run Snyk security scan | |
| uses: snyk/actions/node@master | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| continue-on-error: true | |
| - name: Upload Snyk results | |
| uses: github/codeql-action/upload-sarif@v2 | |
| with: | |
| sarif_file: snyk.sarif | |
| continue-on-error: true | |
| - name: Check npm audit | |
| run: npm audit --audit-level=moderate | |
| continue-on-error: true | |
| # Stage 5: Integration Tests | |
| integration: | |
| name: Integration Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| needs: build | |
| services: | |
| postgres: | |
| image: postgres:15-alpine | |
| env: | |
| POSTGRES_DB: test_db | |
| POSTGRES_USER: test_user | |
| POSTGRES_PASSWORD: test_password | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'pnpm' | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v2 | |
| with: | |
| version: ${{ env.PNPM_VERSION }} | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup test database | |
| env: | |
| DATABASE_URL: postgres://test_user:test_password@localhost:5432/test_db | |
| run: pnpm run migrate | |
| - name: Run integration tests | |
| env: | |
| DATABASE_URL: postgres://test_user:test_password@localhost:5432/test_db | |
| run: pnpm test -- --testPathPattern="integration" | |
| # Stage 6: Deploy to Staging | |
| deploy-staging: | |
| name: Deploy to Staging | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [test, build, security, integration] | |
| if: github.ref == 'refs/heads/develop' && github.event_name == 'push' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v3 | |
| with: | |
| name: next-build | |
| - name: Deploy to Vercel Staging | |
| uses: amondnet/vercel-action@v25 | |
| with: | |
| vercel-token: ${{ secrets.VERCEL_TOKEN }} | |
| vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} | |
| vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} | |
| scope: ${{ secrets.VERCEL_ORG_ID }} | |
| production: false | |
| environment-variables: NODE_ENV=staging | |
| - name: Comment on PR with staging URL | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const deployment = context.payload.deployment; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: `🚀 Staging deployment: ${deployment.environment_url}` | |
| }); | |
| # Stage 7: E2E Tests on Staging | |
| e2e: | |
| name: E2E Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| needs: deploy-staging | |
| if: github.ref == 'refs/heads/develop' && github.event_name == 'push' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Install Playwright | |
| run: pnpm install -D @playwright/test | |
| - name: Run E2E tests | |
| env: | |
| BASE_URL: ${{ secrets.STAGING_URL }} | |
| run: pnpm exec playwright test | |
| continue-on-error: true | |
| - name: Upload Playwright report | |
| uses: actions/upload-artifact@v3 | |
| if: always() | |
| with: | |
| name: playwright-report | |
| path: playwright-report/ | |
| # Stage 8: Performance Tests | |
| performance: | |
| name: Performance Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| needs: deploy-staging | |
| if: github.ref == 'refs/heads/develop' && github.event_name == 'push' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Run Lighthouse CI | |
| uses: treosh/lighthouse-ci-action@v9 | |
| with: | |
| configPath: ./lighthouserc.json | |
| uploadArtifacts: true | |
| temporaryPublicStorage: true | |
| env: | |
| LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }} | |
| # Stage 9: Deploy to Production | |
| deploy-production: | |
| name: Deploy to Production | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [test, build, security, integration, e2e, performance] | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v3 | |
| with: | |
| name: next-build | |
| - name: Deploy to Vercel Production | |
| uses: amondnet/vercel-action@v25 | |
| with: | |
| vercel-token: ${{ secrets.VERCEL_TOKEN }} | |
| vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} | |
| vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} | |
| scope: ${{ secrets.VERCEL_ORG_ID }} | |
| production: true | |
| - name: Notify deployment success | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| github.rest.repos.createDeployment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: context.sha, | |
| environment: 'production', | |
| description: 'Deployed to production', | |
| auto_merge: false, | |
| required_contexts: [] | |
| }); | |
| - name: Health check | |
| run: | | |
| for i in {1..10}; do | |
| if curl -f https://example.com/api/health; then | |
| echo "✅ Health check passed" | |
| exit 0 | |
| fi | |
| echo "Attempt $i failed, retrying..." | |
| sleep 10 | |
| done | |
| exit 1 | |
| # Summary Status | |
| status: | |
| name: CI/CD Status | |
| runs-on: ubuntu-latest | |
| needs: [lint, test, build, security, integration] | |
| if: always() | |
| steps: | |
| - name: Check pipeline status | |
| run: | | |
| if [ "${{ needs.lint.result }}" != "success" ] || \ | |
| [ "${{ needs.test.result }}" != "success" ] || \ | |
| [ "${{ needs.build.result }}" != "success" ] || \ | |
| [ "${{ needs.security.result }}" != "success" ] || \ | |
| [ "${{ needs.integration.result }}" != "success" ]; then | |
| echo "❌ Pipeline failed" | |
| exit 1 | |
| else | |
| echo "✅ Pipeline passed" | |
| fi |