fix: resolve security, routing, and accessibility issues #199
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: CI / Neon Preview | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| types: | |
| - opened | |
| - reopened | |
| - synchronize | |
| - closed | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| setup: | |
| name: Setup | |
| outputs: | |
| branch: ${{ steps.branch_name.outputs.current_branch }} | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Get branch name | |
| id: branch_name | |
| uses: tj-actions/branch-names@dde14ac574a8b9b1cedc59a1cf312788af43d8d8 # v8.2.1 | |
| changes: | |
| name: Detect Changes | |
| runs-on: [self-hosted, macOS] | |
| outputs: | |
| db: ${{ steps.filter.outputs.db }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 | |
| id: filter | |
| with: | |
| filters: | | |
| db: | |
| - 'server/db/**' | |
| - 'shared/schemas/**' | |
| - 'drizzle/**' | |
| - 'server/scripts/migrate.ts' | |
| verify_port: | |
| name: Verify Port (Strict 5002) | |
| if: github.event.action != 'closed' | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - run: npm ci | |
| - name: Verify Port 5002 Compliance | |
| run: npm run verify-port | |
| build_shared: | |
| name: Build Shared Package | |
| if: github.event.action != 'closed' | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - run: npm ci | |
| - name: Build Shared | |
| run: npm run build --prefix shared | |
| # Parallel quality checks for faster feedback | |
| lint: | |
| name: Lint (Biome) | |
| needs: build_shared | |
| if: github.event.action != 'closed' | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - run: npm ci | |
| - name: Build Shared Declarations | |
| run: npm run build --prefix shared | |
| - name: Build Server Declarations | |
| run: npx tsc -b server/tsconfig.json | |
| - name: Run Biome Check | |
| run: npm run check | |
| typecheck: | |
| name: Type Check (TSC) | |
| needs: build_shared | |
| if: github.event.action != 'closed' | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - run: npm ci | |
| - name: Build Shared Declarations | |
| run: npm run build --prefix shared | |
| - name: Build Server Declarations | |
| run: npx tsc -b server/tsconfig.json | |
| - run: npm run typecheck | |
| security_audit: | |
| name: Security Audit | |
| if: github.event.action != 'closed' | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - run: npm ci | |
| - name: Run npm audit | |
| run: npm audit --audit-level=high | |
| docs_check: | |
| name: Docs Check | |
| if: github.event.action != 'closed' | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - run: npm ci | |
| - name: Check Documentation Links | |
| run: npm run check:docs | |
| test: | |
| name: Test & Verify | |
| needs: [setup, build_shared] | |
| if: | | |
| github.event_name == 'pull_request' && ( | |
| github.event.action == 'synchronize' | |
| || github.event.action == 'opened' | |
| || github.event.action == 'reopened') | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Get branch expiration date | |
| id: get_expiration_date | |
| run: echo "EXPIRES_AT=$(date -u -v+24h +'%Y-%m-%dT%H:%M:%SZ')" >> "$GITHUB_ENV" | |
| - name: Create Neon Branch | |
| id: create_neon_branch | |
| if: needs.changes.outputs.db == 'true' | |
| continue-on-error: true | |
| uses: neondatabase/create-branch-action@fb620d43d4c565abaf088b848a4e28e5c4ea4d9c # 6.3.1 | |
| with: | |
| project_id: ${{ secrets.NEON_PROJECT_ID }} | |
| branch_name: preview/pr-${{ github.event.number }}-${{ needs.setup.outputs.branch }} | |
| api_key: ${{ secrets.NEON_API_KEY }} | |
| expires_at: ${{ env.EXPIRES_AT }} | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - run: npm ci | |
| - name: Build Shared Locally | |
| run: npm run build --prefix shared | |
| - name: Push Database Schema | |
| continue-on-error: ${{ steps.create_neon_branch.outcome == 'failure' }} | |
| run: npm run db:push | |
| env: | |
| DATABASE_URL: ${{ needs.changes.outputs.db == 'true' && steps.create_neon_branch.outputs.db_url_with_pooler || secrets.DATABASE_URL_PREVIEW }} | |
| CLOUD_TASKS_AUDIENCE: "placeholder" | |
| CLOUD_TASKS_SERVICE_ACCOUNT_EMAIL: "placeholder@example.com" | |
| - name: Run Tests with Coverage | |
| run: npx vitest run --coverage | |
| env: | |
| DATABASE_URL: ${{ needs.changes.outputs.db == 'true' && steps.create_neon_branch.outputs.db_url_with_pooler || secrets.DATABASE_URL_PREVIEW }} | |
| CLOUD_TASKS_AUDIENCE: "placeholder" | |
| CLOUD_TASKS_SERVICE_ACCOUNT_EMAIL: "placeholder@example.com" | |
| - name: Check Coverage Thresholds | |
| run: | | |
| COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct') | |
| if (( $(echo "$COVERAGE < 40" | bc -l) )); then | |
| echo "Coverage $COVERAGE% is below 40% threshold" | |
| exit 1 | |
| fi | |
| - name: Upload Coverage Report | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| if: always() | |
| with: | |
| name: coverage-report | |
| path: coverage/ | |
| retention-days: 7 | |
| - name: Upload to Codecov | |
| uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 | |
| if: always() | |
| with: | |
| files: ./coverage/lcov.info | |
| fail_ci_if_error: false | |
| verbose: true | |
| env: | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| build: | |
| name: Build | |
| needs: [lint, typecheck, test] | |
| if: | | |
| github.event_name == 'pull_request' && ( | |
| github.event.action == 'synchronize' | |
| || github.event.action == 'opened' | |
| || github.event.action == 'reopened') | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: "24" | |
| cache: "npm" | |
| - run: npm ci | |
| - name: Cache Turbo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: node_modules/.cache/turbo | |
| key: turbo-${{ runner.os }}-${{ github.sha }} | |
| restore-keys: | | |
| turbo-${{ runner.os }}- | |
| - name: Build | |
| run: npm run build | |
| env: | |
| DATABASE_URL: postgresql://placeholder:placeholder@localhost:5432/placeholder | |
| CLOUD_TASKS_AUDIENCE: "placeholder" | |
| CLOUD_TASKS_SERVICE_ACCOUNT_EMAIL: "placeholder@example.com" | |
| - name: Check Bundle Size | |
| run: npm run check:bundle | |
| - name: Upload Source Maps to Sentry | |
| if: env.SENTRY_AUTH_TOKEN != '' | |
| run: | | |
| npx @sentry/cli sourcemaps inject ./client/build | |
| npx @sentry/cli sourcemaps upload ./client/build --org ${{ secrets.SENTRY_ORG }} --project ${{ secrets.SENTRY_PROJECT }} | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| delete_neon_branch: | |
| name: Delete Neon Branch | |
| needs: [setup, changes] | |
| if: github.event_name == 'pull_request' && github.event.action == 'closed' && needs.changes.outputs.db == 'true' | |
| runs-on: [self-hosted, macOS] | |
| steps: | |
| - name: Delete Neon Branch | |
| uses: neondatabase/delete-branch-action@4468d825d5a88ef4012f1705a82f02ec3072f776 # v3.2.1 | |
| with: | |
| project_id: ${{ secrets.NEON_PROJECT_ID }} | |
| branch: preview/pr-${{ github.event.number }}-${{ needs.setup.outputs.branch }} | |
| api_key: ${{ secrets.NEON_API_KEY }} |