Enhance Windows dev support (#13)

Makes local dev work on Windows: cross-env for the server dev port, pnpm --parallel to run server+web together (replacing the POSIX-only '&'), PowerShell setup docs, engines widened to Node >=20, and a Node 20 + 24 CI matrix.

CI keeps a stable required 'Build & test' status via an aggregate gate job so the matrix rename doesn't drop the context the main ruleset requires.

Co-authored-by: BadAtCaptchas <2359196+BadAtCaptchas@users.noreply.github.com>
Co-authored-by: Jannis Braun <151788261+TheZwiss@users.noreply.github.com>
This commit is contained in:
BadAtCaptchas
2026-07-12 13:10:43 +02:00
committed by GitHub
co-authored by Jannis Braun
parent 43cab41e60
commit 1e6c7c6042
5 changed files with 63 additions and 15 deletions
+27 -7
View File
@@ -22,9 +22,14 @@ concurrency:
jobs:
build-and-test:
name: Build & test
name: Build & test (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [20, 24]
steps:
- name: Checkout
uses: actions/checkout@v5
@@ -33,15 +38,10 @@ jobs:
uses: pnpm/action-setup@v5
with:
version: 10.34.3
# Node 20 is the project's supported runtime (package.json engines pins
# >=20 <21, .nvmrc says 20). Running the matrix on 20 also means the fresh
# install below pulls better-sqlite3's prebuilt binary for the correct ABI,
# which is what makes the server suite runnable in CI.
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: 20
node-version: ${{ matrix.node-version }}
cache: pnpm
# The desktop postinstall tries to rebuild the native uiohook-napi module.
@@ -65,3 +65,23 @@ jobs:
# Runs every package's `test` script (server, web, desktop) via vitest.
- name: Test
run: pnpm -r test
# Aggregate gate reporting a single, matrix-independent "Build & test" status.
# Branch protection on main requires the "Build & test" context, but the matrix
# job above reports per-version contexts ("Build & test (Node 20/24)"). This job
# keeps the stable required context alive and fails unless every matrix leg
# succeeded (if: always() so a matrix failure still reports a definitive result
# instead of leaving the required check pending forever).
build-and-test-required:
name: Build & test
if: always()
needs: build-and-test
runs-on: ubuntu-latest
steps:
- name: Verify matrix result
run: |
if [ "${{ needs.build-and-test.result }}" != "success" ]; then
echo "Matrix build-and-test did not succeed: ${{ needs.build-and-test.result }}"
exit 1
fi
echo "All matrix legs passed."
+20 -5
View File
@@ -465,10 +465,10 @@ briefly restarts the `backspace` container (clients reconnect automatically).
## Development
Requirements: **Node.js 20 (LTS)** and **pnpm 10**, both pinned (`.nvmrc` plus
the `packageManager` field), so `nvm use` and Corepack select the right versions
automatically. Newer Node majors are untested; the Docker image always builds on
Node 20 regardless of your host.
Requirements: **Node.js 20 or newer** and **pnpm 10**. The `.nvmrc` file keeps
Node 20 as the default development and production baseline; CI additionally
exercises Node 24, and newer majors generally work but are not part of the test
matrix. The Docker image continues to build on Node 20 regardless of your host.
```bash
pnpm install
@@ -476,6 +476,21 @@ cp .env.example .env # set JWT_SECRET (openssl rand -hex 32)
pnpm dev # API server on :3005, Vite dev server on :5173
```
On Windows PowerShell, confirm Node 20 or newer and use the native copy command:
```powershell
node --version
pnpm install
Copy-Item .env.example .env
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```
Paste the generated value after `JWT_SECRET=` in `.env`, then start both
development servers with `pnpm dev`. Use `node --version` to confirm the active
version if pnpm reports an engine warning. This covers the server and web dev
servers; building the Electron desktop app still expects a POSIX shell (macOS or
Linux).
> **Server/web only?** `pnpm install` also builds the desktop app's native
> keyboard-hook module (`uiohook-napi`), which needs a C++ toolchain
> (`make`, `g++`, `python3`). If those are missing it now **warns and continues**,
@@ -604,7 +619,7 @@ packages/
| Layer | Technology |
|--------------|------------|
| Server | Node.js 20 (LTS), Fastify 4, TypeScript (strict) |
| Server | Node.js 20+, Fastify 4, TypeScript (strict) |
| Database | SQLite (better-sqlite3) + Drizzle ORM |
| Auth | JWT + bcrypt |
| Frontend | React 18, Vite 6, Tailwind CSS 3, Zustand 5 |
+2 -2
View File
@@ -13,7 +13,7 @@
"scripts": {
"dev:server": "pnpm --filter @backspace/server dev",
"dev:web": "pnpm --filter @backspace/web dev",
"dev": "pnpm --filter @backspace/server dev & pnpm --filter @backspace/web dev",
"dev": "pnpm --parallel --filter @backspace/server --filter @backspace/web dev",
"build:shared": "pnpm --filter @backspace/shared build",
"build:server": "pnpm --filter @backspace/server build",
"build:web": "pnpm --filter @backspace/web build",
@@ -42,7 +42,7 @@
},
"packageManager": "pnpm@10.34.3",
"engines": {
"node": ">=20.0.0 <21.0.0",
"node": ">=20.0.0",
"pnpm": ">=10.0.0"
}
}
+2 -1
View File
@@ -6,7 +6,7 @@
"author": "Jannis Braun",
"type": "module",
"scripts": {
"dev": "PORT=3005 tsx watch src/index.ts",
"dev": "cross-env PORT=3005 tsx watch src/index.ts",
"start": "node --import tsx/esm src/index.ts",
"build": "tsc",
"typecheck": "tsc --noEmit",
@@ -41,6 +41,7 @@
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^20.14.0",
"@types/ws": "^8.18.1",
"cross-env": "^7.0.3",
"drizzle-kit": "^0.24.0",
"typescript": "^5.4.0",
"vitest": "^4.0.18",
+12
View File
@@ -120,6 +120,9 @@ importers:
'@types/ws':
specifier: ^8.18.1
version: 8.18.1
cross-env:
specifier: ^7.0.3
version: 7.0.3
drizzle-kit:
specifier: ^0.24.0
version: 0.24.2
@@ -2656,6 +2659,11 @@ packages:
crc@3.8.0:
resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==}
cross-env@7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
hasBin: true
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -7993,6 +8001,10 @@ snapshots:
buffer: 5.7.1
optional: true
cross-env@7.0.3:
dependencies:
cross-spawn: 7.0.6
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1