chore: add pre-push hook for lint + tests (closes #29) (#38)

* chore: add pre-push hook for lint + tests (closes #29)

- scripts/install-hooks.mjs: zero-dep hook installer, runs on postinstall
- Pre-push runs: biome lint + pulse tests + pulse-hermes tests
- CI: add pulse-hermes type check + tests, use bun run lint
- CONTRIBUTING.md: document hook setup

Uses pure Node.js APIs (no Bun dependency) so postinstall works with any runtime.
Users can delete .git/hooks/pre-push to opt out.

* fix: TS2322 in findEffectiveEpoch — cast meta.to to string

meta is Record<string, unknown>, so meta.to is unknown.
Explicit cast to (string | undefined) satisfies codeRev: string param.

---------

Co-authored-by: 鹿鸣 <luming@shazhou.work>
This commit is contained in:
小橘 🍊
2026-04-14 23:38:34 +08:00
committed by GitHub
parent 84a5e58c52
commit 21a8c38040
5 changed files with 82 additions and 6 deletions
+14 -2
View File
@@ -16,11 +16,11 @@ jobs:
with: with:
bun-version: latest bun-version: latest
- name: Install Biome - name: Install dependencies
run: bun install run: bun install
- name: Lint (Biome) - name: Lint (Biome)
run: bunx biome check packages/ run: bun run lint
- name: Install dependencies (pulse) - name: Install dependencies (pulse)
working-directory: packages/pulse working-directory: packages/pulse
@@ -34,6 +34,10 @@ jobs:
working-directory: packages/upulse working-directory: packages/upulse
run: bun install run: bun install
- name: Install dependencies (pulse-hermes)
working-directory: packages/pulse-hermes
run: bun install
- name: Type check (pulse) - name: Type check (pulse)
working-directory: packages/pulse working-directory: packages/pulse
run: bunx tsc --noEmit run: bunx tsc --noEmit
@@ -42,10 +46,18 @@ jobs:
working-directory: packages/upulse working-directory: packages/upulse
run: bunx tsc --noEmit run: bunx tsc --noEmit
- name: Type check (pulse-hermes)
working-directory: packages/pulse-hermes
run: bunx tsc --noEmit
- name: Unit tests (pulse) - name: Unit tests (pulse)
working-directory: packages/pulse working-directory: packages/pulse
run: bun test run: bun test
- name: Unit tests (pulse-hermes)
working-directory: packages/pulse-hermes
run: bun test
- name: Unit tests (upulse) - name: Unit tests (upulse)
working-directory: packages/upulse working-directory: packages/upulse
run: bun test src/config.test.ts run: bun test src/config.test.ts
+8 -1
View File
@@ -11,9 +11,16 @@
```bash ```bash
git clone https://github.com/oc-xiaoju/pulse.git git clone https://github.com/oc-xiaoju/pulse.git
cd pulse cd pulse
bun install bun install # also installs pre-push hook
``` ```
### Git Hooks
`bun install` 自动安装 pre-push hook(via `scripts/install-hooks.mjs`),push 前自动跑 lint + tests。
手动跑检查:`bun run precheck`
删除 `.git/hooks/pre-push` 可禁用。
## 代码结构 ## 代码结构
``` ```
+5 -2
View File
@@ -2,8 +2,11 @@
"name": "pulse-monorepo", "name": "pulse-monorepo",
"private": true, "private": true,
"scripts": { "scripts": {
"lint": "biome check packages/", "lint": "bun x @biomejs/biome check packages/",
"lint:fix": "biome check --write packages/" "lint:fix": "bun x @biomejs/biome check --fix packages/",
"test": "cd packages/pulse && bun test && cd ../pulse-hermes && bun test",
"precheck": "bun run lint && bun run test",
"postinstall": "node scripts/install-hooks.mjs || true"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.4.11" "@biomejs/biome": "^2.4.11"
+1 -1
View File
@@ -107,7 +107,7 @@ export function findEffectiveEpoch(store: PulseStore): EventRecord | null {
// Corrupted meta — skip this rollback event, fall through to latest promote // Corrupted meta — skip this rollback event, fall through to latest promote
return store.getLatest('promote'); return store.getLatest('promote');
} }
const targetRev = meta.to || rollback.codeRev; const targetRev = (meta.to as string | undefined) || rollback.codeRev;
if (targetRev) { if (targetRev) {
return store.getLatestWhere({ kind: 'promote', codeRev: targetRev }); return store.getLatestWhere({ kind: 'promote', codeRev: targetRev });
} }
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env node
/**
* Install git pre-push hook.
* Zero dependencies — just writes a shell script to .git/hooks/pre-push.
* Runs on `bun install` via postinstall.
*/
import { writeFileSync, readFileSync, chmodSync, existsSync } from 'node:fs';
import { join } from 'node:path';
const hookPath = join(process.cwd(), '.git', 'hooks', 'pre-push');
// Don't overwrite if user has a custom hook
if (existsSync(hookPath)) {
let content = '';
try { content = readFileSync(hookPath, 'utf-8'); } catch {}
if (content && !content.includes('pulse-auto-hook')) {
console.log('[hooks] pre-push hook already exists (custom), skipping');
process.exit(0);
}
}
const hook = `#!/bin/sh
# pulse-auto-hook — installed by scripts/install-hooks.mjs
# Runs lint + tests before pushing. Delete this file to disable.
echo "[pre-push] Running lint..."
bun x @biomejs/biome check packages/ || {
echo "\\n❌ Lint failed. Fix with: bun run lint:fix"
exit 1
}
echo "[pre-push] Running tests (pulse)..."
cd packages/pulse && bun test || {
echo "\\n❌ Tests failed (pulse)"
exit 1
}
echo "[pre-push] Running tests (pulse-hermes)..."
cd ../pulse-hermes && bun test || {
echo "\\n❌ Tests failed (pulse-hermes)"
exit 1
}
echo "✅ All checks passed"
`;
try {
writeFileSync(hookPath, hook);
chmodSync(hookPath, 0o755);
console.log('[hooks] pre-push hook installed');
} catch (err) {
// Non-fatal — CI and shallow clones may not have .git/hooks
console.log('[hooks] Could not install pre-push hook (non-fatal):', err.message);
}