diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..e3671af --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,66 @@ +name: PR checks + +# Deterministic-only CI gate for pull requests and pushes to main. +# Typecheck, unit tests and a secret scan — no API credits, no market data, +# no Electron E2E (those stay on the release tag pipeline). + +on: + pull_request: + push: + branches: [main] + +concurrency: + group: pr-checks-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + typecheck: + name: Typecheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Install dependencies + run: bun install --frozen-lockfile + - name: Typecheck + run: bun run typecheck + + unit-tests: + name: Unit tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Install dependencies + run: bun install --frozen-lockfile + - name: Run tests + run: bun test --isolate + + secret-scan: + name: Secret scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Scan for high-signal secret patterns + run: | + set -uo pipefail + # Fake values in test fixtures (e.g. packages/*/src/**/*.test.ts) and + # canonical fake examples quoted in docs prose are excluded on + # purpose; this scan targets accidental real secrets in source, + # scripts and configuration. + MATCHES=$(grep -rInE \ + --exclude-dir=node_modules \ + --exclude-dir=docs \ + --exclude='*.test.ts' --exclude='*.test.tsx' --exclude='bun.lock' \ + 'ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----' \ + . || true) + if [ -n "$MATCHES" ]; then + echo "::error::Possible secrets found:" + echo "$MATCHES" + exit 1 + fi + echo "No secret patterns found."