Repository navigation
docs: update badge #79
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 | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| test: | |
| name: Test & Lint | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: write | |
| statuses: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@b789df37d6b7526a863bf51b65df64f3f56ffe4f # v7 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| cache-dependency-path: package-lock.json | |
| - name: Install dependencies | |
| run: npm ci --legacy-peer-deps | |
| - name: Run lint | |
| run: npm run lint | |
| - name: Run tests | |
| run: npm run test:ci | |
| - name: Run tests with coverage | |
| run: npm run test:coverage | |
| - name: Run build | |
| run: npm run build | |
| - name: Upload coverage reports to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| semgrep: | |
| name: Semrep (SAST) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: write | |
| statuses: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Get week number for cache key | |
| id: week | |
| run: echo "week=$(date +%V)" >> $GITHUB_OUTPUT | |
| - name: Cache Semgrep | |
| id: cache-semgrep | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v7 | |
| with: | |
| path: | | |
| ~/.cache/pip | |
| ~/.semgrep/ | |
| # Expires weekly — matches Dependabot cadence | |
| key: semgrep-${{ runner.os }}-${{ steps.week.outputs.week }} | |
| - name: Install Semgrep | |
| # Always runs — ~/.cache/pip only caches downloads, not the binary itself. | |
| # On cache hit, pip uses cached .whl files (~1 sec); on miss, downloads fresh. | |
| run: python3 -m pip install semgrep | |
| - name: Semgrep SAST | |
| # Skip .github/ — CI workflow YAML is not application code | |
| run: semgrep scan --config auto --error --exclude .github | |
| trivy: | |
| name: Trivy (SCA) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: write | |
| statuses: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@c07df6fec6fa692e6fd1200d50aaa1fdd66f03c8 # v0.36.0 | |
| with: | |
| scan-type: "fs" | |
| scan-ref: "." | |
| scanners: "vuln" | |
| format: "table" | |
| severity: "CRITICAL,HIGH" | |
| exit-code: 1 | |
| # dependency-review: | |
| #name: Dependency Review | |
| #if: github.event_name == 'pull_request' | |
| #runs-on: ubuntu-latest | |
| #permissions: | |
| #contents: read | |
| #pull-requests: write | |
| # | |
| #steps: | |
| #- name: Checkout | |
| #uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| # | |
| #- name: Dependency Review | |
| #uses: actions/dependency-review-action@1e966ba708e4b64bb0c9bfb7abb5a232840be16a # v5.0 | |
| #with: | |
| # Fail on critical & high — warn on everything else | |
| #fail-on-severity: high | |
| # Allow these licenses for Angular ecosystem | |
| #allow-licenses: >- | |
| #MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, 0BSD, | |
| #CC0-1.0, Unlicense, Python-2.0, Zlib | |
| gitleaks: | |
| name: Gitleaks (Secrets) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| statuses: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7.0.1 | |
| with: | |
| fetch-depth: 0 # Gitleaks needs full git history | |
| - name: Gitleaks secret scan | |
| uses: gitleaks/gitleaks-action@bcfb9cce635345aac9996cedc19b2de8e01b894f | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| sonarcloud: | |
| name: SonarCloud (Deep SAST) | |
| # The SonarCloud action handles missing tokens gracefully | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 # SonarCloud needs full git history | |
| - name: Setup Node.js | |
| uses: actions/setup-node@b789df37d6b7526a863bf51b65df64f3f56ffe4f # v7 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| cache-dependency-path: package-lock.json | |
| - name: Install dependencies | |
| run: npm ci --legacy-peer-deps | |
| - name: Generate coverage report | |
| run: npm run test:coverage | |
| - name: SonarCloud Scan | |
| uses: sonarsource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1 nosemgrep:generic.secrets.security.detected-sonarqube-docs-api-key | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| Vercel: | |
| name: Notify Vercel | |
| if: always() | |
| needs: [test, semgrep, trivy, sonarcloud, gitleaks] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: write | |
| statuses: write | |
| steps: | |
| - name: Notify Vercel | |
| uses: vercel/repository-dispatch/actions/status@30f760c6640485cd92f8c785ef361382555fb712 # v1 | |
| with: | |
| # Must match the check name configured in Vercel Deployment Checks | |
| name: "Vercel - stephulz-portfolio: coverage" | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| # Fail the job if any upstream dependency failed — this makes the | |
| # vercel/repository-dispatch post-action report failure to Vercel. | |
| - name: Propagate upstream failure | |
| if: >- | |
| contains(needs.*.result, 'failure') || | |
| contains(needs.*.result, 'cancelled') | |
| run: | | |
| echo "❌ Some checks failed — notifying Vercel of failure" | |
| echo "test: ${{ needs.test.result }}" | |
| echo "semgrep: ${{ needs.semgrep.result }}" | |
| echo "trivy: ${{ needs.trivy.result }}" | |
| echo "gitleaks: ${{ needs.gitleaks.result }}" | |
| exit 1 |