Skip to content
Merged
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
14 changes: 10 additions & 4 deletions .github/workflows/audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,24 @@ jobs:
steps:
- uses: actions/checkout@v7

- uses: pnpm/action-setup@v6

# setup-pnpm's default store nests inside its own bin dir, which breaks
# any prepare-building dep (portable-type TS2742) — pin it outside.
- run: pnpm config set store-dir ~/.pnpm-store --global

- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
cache: pnpm

- run: npm ci
- run: pnpm install --frozen-lockfile

# --audit-level=high → exit non-zero only when a high or critical
# vuln is present. Moderate transitive vulns flap too often (esbuild
# via drizzle-kit, postcss-via-next) to be a useful daily signal.
- name: npm audit (fail on high+)
run: npm audit --audit-level=high
- name: pnpm audit (fail on high+)
run: pnpm audit --audit-level=high

# Always print the full report so the run page shows context even
# when the audit step itself passed.
Expand Down
14 changes: 10 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,18 @@ jobs:
with:
fetch-depth: 0

- uses: pnpm/action-setup@v6

# setup-pnpm's default store nests inside its own bin dir, which breaks
# any prepare-building dep (portable-type TS2742) — pin it outside.
- run: pnpm config set store-dir ~/.pnpm-store --global

- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
cache: pnpm

- run: npm ci
- run: pnpm install --frozen-lockfile

# check:design scans with ripgrep, which is NOT preinstalled on the
# runner. Without this the script exited 127 on every pattern, read that
Expand All @@ -70,7 +76,7 @@ jobs:
- name: Install ripgrep (required by check:design)
run: sudo apt-get update -qq && sudo apt-get install -y -qq ripgrep

# ONE canonical bundle — the exact `npm run verify` you run locally
# ONE canonical bundle — the exact `pnpm run verify` you run locally
# (tsc + lint + check:design + check:desktop + test:unit + test:home +
# test:ops). CI calls it VERBATIM, so a green local verify can't diverge
# from a green CI. The unit suite is glob-discovered
Expand All @@ -96,7 +102,7 @@ jobs:
npx drizzle-kit push --force

- name: Verify
run: npm run verify
run: pnpm run verify

# `next build` is intentionally NOT here — the App-Router pages do
# DB queries during static pre-render, so a real Postgres would
Expand Down
23 changes: 15 additions & 8 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,26 @@ jobs:
# out the default branch and would ship something nobody verified.
ref: ${{ github.event.workflow_run.head_sha || github.sha }}

- uses: pnpm/action-setup@v6

# setup-pnpm's default store nests inside its own bin dir, which breaks
# any prepare-building dep (portable-type TS2742) — pin it outside.
- run: pnpm config set store-dir ~/.pnpm-store --global

- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
cache: pnpm

# Root deps AND the bridge subpackage — bridge/ is its own npm package with
# its own lockfile (pg, @types/pg). The build step below runs `tsc` in it,
# so without this the bridge build fails with "Cannot find module 'pg'".
- run: npm ci
- run: npm --prefix bridge ci
# One workspace install covers root AND bridge/ (both in
# pnpm-workspace.yaml) — the separate `npm --prefix bridge ci` is gone.
- run: pnpm install --frozen-lockfile

- name: Push schema to CI Postgres (build needs it)
run: npx drizzle-kit push
run: pnpm exec drizzle-kit push

# Next's incremental compiler cache. Every deploy compiled the whole app
# from cold because the runner is ephemeral — the one part of the pipeline
Expand All @@ -105,15 +112,15 @@ jobs:
uses: actions/cache@v6
with:
path: .next/cache
key: ${{ runner.os }}-next-${{ hashFiles('package-lock.json') }}-${{ hashFiles('src/**/*.ts', 'src/**/*.tsx') }}
key: ${{ runner.os }}-next-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.ts', 'src/**/*.tsx') }}
restore-keys: |
${{ runner.os }}-next-${{ hashFiles('package-lock.json') }}-
${{ runner.os }}-next-${{ hashFiles('pnpm-lock.yaml') }}-
${{ runner.os }}-next-

- name: Build (app + bridge)
run: |
npm run build
npm --prefix bridge run build
pnpm run build
pnpm -C bridge run build

- name: SSH setup
run: |
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/desktop-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@ jobs:
# Top-level install — the web app's package.json is needed so
# @home/* + @/* imports in the desktop main process resolve (those
# aliases reach into ../src and ../home).
- uses: pnpm/action-setup@v6

- name: Install root deps
run: npm ci
run: pnpm install --frozen-lockfile

- name: Install desktop deps
working-directory: desktop
Expand Down
4 changes: 2 additions & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# 2026-08-25: 206 finished files sat uncommitted across the fleet for ten days.
#
# The split that makes sense: a COMMIT is local and reversible, so gate it
# cheaply; a PUSH is shared, so gate it completely. `npm run verify` still runs
# cheaply; a PUSH is shared, so gate it completely. `pnpm run verify` still runs
# in full on pre-push and again in CI, so nothing reaches origin unverified —
# only the local loop got its three minutes back.
#
Expand Down Expand Up @@ -54,4 +54,4 @@ fi

echo "→ eslint (staged files only — full tree runs on push and in CI)"
# shellcheck disable=SC2086
git diff --cached -z --name-only $FILTER -- $GLOBS | xargs -0 -r npx eslint
git diff --cached -z --name-only $FILTER -- $GLOBS | xargs -0 -r pnpm exec eslint
22 changes: 11 additions & 11 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env sh
# Before code leaves this machine: run the SAME `npm run verify` bundle CI runs
# Before code leaves this machine: run the SAME `pnpm run verify` bundle CI runs
# (tsc + lint + check:design + test:unit + test:home) — one definition of
# "verified", so a `git commit --no-verify` can't slip a red past the local gate.
# Then the deploy-safety extras: schema-drift guard, dev-server smoke (if up),
Expand Down Expand Up @@ -31,16 +31,16 @@ fi
# push went red, and the conclusion drawn was "the hook is broken". Fail loudly
# on the real cause instead, before running anything that would mis-attribute it.
# ---------------------------------------------------------------------------
if [ package-lock.json -nt node_modules/.package-lock.json ] 2>/dev/null; then
echo "✗ node_modules is older than package-lock.json."
if [ pnpm-lock.yaml -nt node_modules/.modules.yaml ] 2>/dev/null; then
echo "✗ node_modules is older than pnpm-lock.yaml."
echo " This is an INSTALL problem, not a problem with your change — and left"
echo " alone it surfaces as bogus 'Cannot find module' type errors."
echo " Run: npm ci"
echo " Run: pnpm install"
exit 1
fi

echo "→ verify (tsc + lint + check:design + test:unit + test:home) — same bundle as CI"
npm run verify
pnpm run verify

# ---------------------------------------------------------------------------
# AMBIENT CHECK — scoped to the diff.
Expand Down Expand Up @@ -71,10 +71,10 @@ fi

if [ -n "$_touches_schema" ]; then
echo "→ check:schema (this branch changes schema — a mismatch is yours)"
npm run check:schema
pnpm run check:schema
else
echo "→ check:schema (branch touches no schema — mismatches are ambient, not blocking)"
npm run check:schema || echo " ⚠ schema mismatch predates this branch. Fix with: npm run db:push"
pnpm run check:schema || echo " ⚠ schema mismatch predates this branch. Fix with: pnpm run db:push"
fi

BASE="${COCKPIT_URL:-http://localhost:3000}"
Expand All @@ -90,16 +90,16 @@ BASE="${COCKPIT_URL:-http://localhost:3000}"
# exactly like a missing one.
_health=$(curl -sf --max-time 2 "$BASE/api/health" 2>/dev/null || true)
if [ -z "$_health" ]; then
echo "→ smoke: server not running at $BASE — skipping (start with npm run dev or install-app)"
echo "→ smoke: server not running at $BASE — skipping (start with pnpm run dev or install-app)"
elif ! printf '%s' "$_health" | grep -q '"commit"'; then
echo "→ smoke: server at $BASE is NOT FleetCrown (health payload lacks \"commit\") — skipping"
else
echo "→ smoke: FleetCrown server detected at $BASE"
npm run smoke
pnpm run smoke
fi

echo "→ test:pre-push-prod-dogfood"
npm run test:pre-push-prod-dogfood
pnpm run test:pre-push-prod-dogfood

# >>> fleetcrown push-deploy >>>
# DISABLED 2026-07-28 — the Hetzner deploy now fires from GitHub Actions
Expand Down Expand Up @@ -132,7 +132,7 @@ npm run test:pre-push-prod-dogfood
# echo "[push-deploy] fleetcrown: BLOCKED by CI gate for ${_sha} — fix CI, then re-push or deploy manually"
# fi
# ) >> "$_log" 2>&1 & disown 2>/dev/null || true
# echo "[push-deploy] fleetcrown: CI-gated deploy started → ${_log} (or: npm run deploy:status)"
# echo "[push-deploy] fleetcrown: CI-gated deploy started → ${_log} (or: pnpm run deploy:status)"
# fi
echo "→ deploy: handled by GitHub Actions (deploy.yml, DEPLOY_VIA_CI=true) after CI passes on main"
# <<< fleetcrown push-deploy <<<
Loading
Loading