Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: CI

on:
push:
branches: [dev, main]
pull_request:
branches: [dev, main]

permissions:
contents: read

# Cancel superseded runs for the same ref so rapid PR updates don't pile up,
# but keep per-commit CI on the protected branches (dev, main) so each commit
# there still gets a completed run.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}

jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 15

strategy:
fail-fast: false
matrix:
node-version: ['24.13.0', '24']

steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
persist-credentials: false

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ matrix.node-version }}
cache: npm

# `npm ci` runs the `prepare` script, which builds the package (tsup),
# so the compiled output in dist/ is available to the test step below.
- name: Install dependencies and build
run: npm ci

# `node --test` exits 0 when its glob matches no files, so a renamed or
# moved test directory would otherwise produce a silent green run. Fail
# loudly if no test files are present.
- name: Verify tests are discoverable
run: |
# `|| true` so a missing test/ directory does not abort at the
# assignment (set -e + pipefail); the explicit check below reports it.
count=$(find test -name '*.test.mjs' 2>/dev/null | wc -l | tr -d ' ' || true)
echo "Discovered $count test file(s)"
if [ "$count" -eq 0 ]; then
echo "::error::No test files matched test/**/*.test.mjs" >&2
exit 1
fi

- name: Typecheck
run: npm run typecheck

# CI runs typecheck and the tests as separate steps (rather than the
# aggregate `npm test`) for clearer per-step attribution and to avoid a
# second build — `npm test` remains a developer convenience.
- name: Test
run: npm run test:unit
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,18 @@ Typecheck:
npm run typecheck
```

Run the test suite (typecheck, build, then `node --test`):

```sh
npm test
```

Run only the tests against an existing build in `dist/`:

```sh
npm run test:unit
```

Run local smoke tests:

```sh
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"dev": "tsx watch main.js",
"typecheck": "tsc --noEmit",
"prepare": "npm run build",
"test": "npm run typecheck",
"test": "npm run typecheck && npm run build && node --test \"test/**/*.test.mjs\"",
"test:unit": "node --test \"test/**/*.test.mjs\"",
"test:local": "npm run build && node scripts/test-local.mjs",
"warning:stub": "npm run build && node scripts/create-stub-warning.mjs"
},
Expand Down
Loading