ci(app): add missing tsx devDependency for the test script #5
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 | |
| # Gates every PR + push to main on: | |
| # - TypeScript: lint, typecheck, tests, production bundle build | |
| # - Rust: cargo check + clippy + unit tests against the program | |
| # Both job families run in parallel; failures fail the workflow. | |
| # | |
| # Deliberately NOT here: | |
| # - cargo build-sbf (requires Solana toolchain, slow ~5m, deferred to | |
| # a future job that runs on a schedule or workflow_dispatch only). | |
| # - pnpm --filter @pushflip/dealer test (LiteSVM-based; runs locally, | |
| # adds CI minutes for marginal value — re-enable once dealer | |
| # productionization stabilizes). | |
| # - Container build smoke (covered end-to-end by deploy-tucker.sh). | |
| on: | |
| pull_request: | |
| paths-ignore: | |
| - 'wiki/**' | |
| - 'docs/wiki/**' | |
| - 'scripts/wiki-health-check.sh' | |
| - '.github/workflows/wiki-build.yml' | |
| - '.github/workflows/telegram-notify.yml' | |
| - 'notes.md' | |
| push: | |
| branches: [main] | |
| paths-ignore: | |
| - 'wiki/**' | |
| - 'docs/wiki/**' | |
| - 'scripts/wiki-health-check.sh' | |
| - '.github/workflows/wiki-build.yml' | |
| - '.github/workflows/telegram-notify.yml' | |
| - 'notes.md' | |
| workflow_dispatch: | |
| # Least-privilege token. The workflow only needs to read repo contents | |
| # (checkout) — no writes, no PR comments, no package operations. Setting | |
| # this explicitly closes the org/repo default which is typically | |
| # `contents: write` and would let a compromised dep push to the repo | |
| # via the workflow's GITHUB_TOKEN. Defense-in-depth. | |
| permissions: | |
| contents: read | |
| # Cancel an in-flight CI run when a new push lands on the same ref. | |
| # Saves CI minutes and gives faster feedback on the head commit. | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| typescript: | |
| name: TypeScript (lint + typecheck + test + build) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: pnpm | |
| - name: Install workspace deps | |
| run: pnpm install --frozen-lockfile | |
| # The repo-level `.npmrc` already sets `ignore-scripts=true`, | |
| # which skips biome/ultracite postinstalls AND better-sqlite3's | |
| # `prebuild-install || node-gyp rebuild` install script. The | |
| # better-sqlite3 binding is restored by the dedicated step | |
| # below. The `--ignore-scripts` CLI flag was previously passed | |
| # here too — it was redundant with the .npmrc setting, so | |
| # removed for clarity. | |
| - name: Build better-sqlite3 native binding | |
| # The repo's `.npmrc ignore-scripts=true` blocks every form of | |
| # `pnpm install/rebuild` from running install scripts, even | |
| # with explicit allow-lists like `pnpm.onlyBuiltDependencies` | |
| # — verified empirically (commit 7f8e646's `pnpm rebuild | |
| # better-sqlite3` step ran in 700ms with zero output and | |
| # produced no binding). The reliable bypass is to invoke the | |
| # package's own install script directly via `npm run install` | |
| # from its directory: that runs `prebuild-install`, which | |
| # downloads the precompiled binding from upstream's GitHub | |
| # release for the current (node-20, linux-x64) tuple. The | |
| # `bash -c` form lets the glob expand the pnpm-store path | |
| # without committing to a specific better-sqlite3 version. | |
| run: | | |
| cd node_modules/.pnpm/better-sqlite3@*/node_modules/better-sqlite3 | |
| npm run install | |
| - name: Verify better-sqlite3 native binding loads | |
| # Load-bearing guard. `require('better-sqlite3')` alone does | |
| # NOT trigger the binding load — better-sqlite3's lib/database.js | |
| # only calls `require('bindings')('better_sqlite3.node')` lazily | |
| # inside the Database constructor (line 48). So this step | |
| # instantiates an in-memory Database AND executes a query, | |
| # which forces the .node file to load. Without this, a missing | |
| # binding would slip past the smoke and only surface inside | |
| # `pnpm test`, several steps later, with a less actionable | |
| # failure context. | |
| run: pnpm --filter @pushflip/faucet exec node -e "const D=require('better-sqlite3'); const db=new D(':memory:'); db.exec('SELECT 1'); db.close()" | |
| - name: Lint (app) | |
| run: pnpm --filter @pushflip/app lint | |
| - name: Lint (faucet) | |
| run: pnpm --filter @pushflip/faucet lint | |
| - name: Typecheck (client) | |
| run: pnpm --filter @pushflip/client exec tsc --noEmit | |
| - name: Typecheck (app) | |
| run: pnpm --filter @pushflip/app typecheck | |
| - name: Typecheck (faucet) | |
| run: pnpm --filter @pushflip/faucet typecheck | |
| - name: Typecheck (scripts) | |
| run: pnpm --filter @pushflip/scripts exec tsc --noEmit | |
| - name: Test (client) | |
| run: pnpm --filter @pushflip/client test | |
| - name: Test (faucet) | |
| run: pnpm --filter @pushflip/faucet test | |
| - name: Test (app) | |
| run: pnpm --filter @pushflip/app test | |
| - name: Build (app — proves Vite production bundle compiles) | |
| env: | |
| # Match the deploy-script defaults so the build doesn't fail | |
| # on the production-only env-var-required guards in | |
| # use-faucet.ts / use-display-name.ts. | |
| VITE_FAUCET_URL: /api/faucet | |
| VITE_NICKNAME_URL: /api/nickname | |
| VITE_RPC_ENDPOINT: https://api.devnet.solana.com | |
| VITE_RPC_WS_ENDPOINT: wss://api.devnet.solana.com | |
| run: pnpm --filter @pushflip/app build | |
| - name: Summary | |
| if: always() | |
| run: | | |
| { | |
| echo "## TypeScript CI summary" | |
| echo | |
| echo "- pnpm: $(pnpm --version)" | |
| echo "- node: $(node --version)" | |
| echo "- workspaces: $(jq -r '.workspaces[]' package.json | wc -l)" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| rust: | |
| name: Rust (check + clippy + test) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy | |
| - name: Cache cargo registry + build | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| # Scope cache to the workspace; the test harness shares the | |
| # same target dir as the program so cache reuse is high. | |
| shared-key: pushflip-rust | |
| - name: Cargo check (all targets) | |
| # Exclude the integration-test crate: its build.rs invokes | |
| # `cargo build-sbf`, which requires the Solana platform-tools | |
| # that this runner intentionally doesn't install (see the | |
| # workflow header — sbf builds are deferred to a slower job). | |
| # `cargo check --all-targets` would still run that build.rs | |
| # and panic. The test crate's source IS still type-checked, | |
| # via the dedicated `Cargo check (integration test crate)` | |
| # step below using `PUSHFLIP_SKIP_SBF=1`. | |
| run: cargo check --workspace --exclude pushflip-tests --all-targets --locked | |
| - name: Cargo clippy (all targets, report-only) | |
| # `-D warnings` would block every PR on the ~24 deferred | |
| # dead-code warnings tracked under Task 3.B.End. Running | |
| # without `-D warnings` so the report still appears in the | |
| # job log + summary, but doesn't block. Future tightening | |
| # (once the deferred warnings are cleaned up) should add | |
| # `-- -D warnings` here. | |
| # `--exclude pushflip-tests`: same reason as Cargo check above. | |
| run: cargo clippy --workspace --exclude pushflip-tests --all-targets --locked 2>&1 | tee clippy.log | |
| continue-on-error: false | |
| - name: Cargo clippy summary | |
| # Note: NOT `if: always()`. If the prior clippy step failed | |
| # (which would be a `cargo clippy` invocation error, not a | |
| # warning count), `clippy.log` may be missing or partial and | |
| # this summary would lie. Letting the summary skip in that | |
| # case keeps the GitHub run page honest. | |
| run: | | |
| if [ ! -s clippy.log ]; then | |
| echo "clippy.log missing or empty — clippy step failed before producing output" >&2 | |
| exit 1 | |
| fi | |
| # `^warning:` (with the colon) matches individual diagnostic | |
| # headers and skips the per-crate roll-up like | |
| # `warning: pushflip (lib) generated 8 warnings`. | |
| # `^error:` is similarly precise. | |
| warning_count=$(grep -c '^warning:' clippy.log || true) | |
| error_count=$(grep -c '^error:' clippy.log || true) | |
| { | |
| echo "### Clippy" | |
| echo | |
| echo "- Warnings: $warning_count" | |
| echo "- Errors: $error_count" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Fail the step if clippy produced ANY error (not warning). | |
| # Errors here would mean a real lint regression, not a | |
| # deferred dead-code warning. | |
| [ "$error_count" -eq 0 ] | |
| - name: Cargo test (program unit tests) | |
| # `-p pushflip` skips the integration-test crate which requires | |
| # `cargo build-sbf` to have run first. That's a separate slower | |
| # job (deferred — see workflow comment header). | |
| run: cargo test -p pushflip --locked | |
| - name: Cargo check (integration test crate) | |
| # Type-checks tests/src/*.rs (~1.5k LOC of LiteSVM-based | |
| # integration tests) WITHOUT invoking `cargo build-sbf`. | |
| # `PUSHFLIP_SKIP_SBF=1` makes tests/build.rs short-circuit and | |
| # write a stub `pushflip-skip-sbf-stub.so` so the | |
| # `include_bytes!(env!("PUSHFLIP_TEST_SBF_PATH"))` in the test | |
| # source still resolves at compile time. This catches accidental | |
| # compile breakage in the integration test crate that the | |
| # `--exclude pushflip-tests` step above would otherwise let | |
| # through. ACTUALLY RUNNING the tests is the deferred | |
| # SBF-toolchain job's responsibility; this step is a typecheck | |
| # gate only. | |
| env: | |
| PUSHFLIP_SKIP_SBF: "1" | |
| run: cargo check -p pushflip-tests --tests --locked | |
| - name: Summary | |
| if: always() | |
| run: | | |
| { | |
| echo "## Rust CI summary" | |
| echo | |
| echo "- rustc: $(rustc --version)" | |
| echo "- cargo: $(cargo --version)" | |
| } >> "$GITHUB_STEP_SUMMARY" |