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
23 changes: 6 additions & 17 deletions .github/actions/setup-node-cached/action.yml
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
name: Setup Node with cached node_modules
name: Setup Node with npm cache
description: >-
Set up Node from .nvmrc, restore node_modules from an exact-key cache, and run
`npm ci` only on a cache miss. postinstall/preinstall are inert in CI (git-hook
install + engine assert), so skipping the install on a hit is safe. Native deps
are platform prebuilds keyed by runner.os, so there is no ABI mismatch risk.
Set up Node from .nvmrc, restore npm's download cache, and run the immutable
clean install on every fresh runner. node_modules is never restored, so each
job proves package.json and package-lock.json can reproduce the toolchain.
runs:
using: composite
steps:
Expand All@@ -14,16 +13,6 @@ runs:
cache: npm
cache-dependency-path: package-lock.json

- name: Restore node_modules
id: node-modules-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: node_modules
# Include every install-contract input. In particular, an .npmrc-only PR
# must not restore old modules and skip the npm ci it is meant to validate.
key: node-modules-${{ runner.os }}-${{ hashFiles('.nvmrc', 'package.json', 'package-lock.json', '.npmrc') }}

- name: Install dependencies (cache miss only)
if: steps.node-modules-cache.outputs.cache-hit != 'true'
- name: Install dependencies
shell: bash
run: npm ci
run: npm ci --include=dev
2 changes: 1 addition & 1 deletion .github/workflows/dependency-report.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -39,7 +39,7 @@ jobs:
cache-dependency-path: package-lock.json

- name: Install dependencies
run: npm ci
run: npm ci --include=dev

- name: Render dependency report
id: report
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/eval-canary.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,7 +143,7 @@ jobs:

- name: Install dependencies
id: install
run: npm ci
run: npm ci --include=dev

- name: Guard Supabase project identity
id: project_guard
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ingestion-autopilot.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ jobs:
cache-dependency-path: package-lock.json

- name: Install dependencies
run: npm ci
run: npm ci --include=dev

- name: Guard Supabase project identity
run: npm run check:supabase-project
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/live-drift.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,7 @@ jobs:
cache-dependency-path: package-lock.json

- name: Install dependencies
run: npm ci
run: npm ci --include=dev

- name: Guard Supabase project identity
run: npm run check:supabase-project
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ops-digest.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,8 @@
# hit-rate, answer spend, degraded/truncation rates) into a one-screen summary
# and keeps it in a rolling GitHub issue, commenting only when something is off.
#
# The repo variable and matching deployment/GitHub secret are provisioned.
# The repo variable and matching deployment/GitHub secret must both be
# provisioned. The preflight below fails closed when either name is absent.
# Keep workflow_dispatch for operator verification and the daily schedule for
# the normal morning digest.
name: Ops Digest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/staging-tenancy.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,7 +31,7 @@ jobs:
cache-dependency-path: package-lock.json

- name: Install dependencies
run: npm ci
run: npm ci --include=dev

- name: Run cross-tenant staging checks
env:
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,9 +13,13 @@ questions with source citations that link back to the original PDF/document.
2. Install dependencies:

```bash
npm install
npm ci --include=dev
```

This is the clean-checkout and validation install contract. Use `npm install`
only when intentionally changing dependencies and regenerating
`package-lock.json`.

3. Copy the full `.env.example` to `.env.local` and fill in Supabase and OpenAI
values. Copy the worker and upload defaults too — they are conservative
local-first settings, not optional extras.
Expand Down
2 changes: 1 addition & 1 deletion scripts/check-installed-lock-parity.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

export const criticalInstalledPackages = ["next", "react", "react-dom", "playwright", "typescript"];
export const criticalInstalledPackages = ["next", "react", "react-dom", "eslint", "playwright", "typescript", "vitest"];

function readJson(filePath) {
return JSON.parse(readFileSync(filePath, "utf8"));
Expand Down
12 changes: 6 additions & 6 deletions tests/ci-cache-safety.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,12 @@ const uiSetup = readFileSync(new URL("../.github/actions/setup-ui-e2e/action.yml
const workflow = readFileSync(new URL("../.github/workflows/ci.yml", import.meta.url), "utf8");

describe("CI cache safety", () => {
it("invalidates cached node_modules for every install-contract input", () => {
const cacheKey = nodeSetup.match(/^\s*key:\s*(node-modules-.*)$/m)?.[1] ?? "";
expect(cacheKey).toContain(".nvmrc");
expect(cacheKey).toContain("package.json");
expect(cacheKey).toContain("package-lock.json");
expect(cacheKey).toContain(".npmrc");
it("uses npm's download cache but recreates node_modules on every job", () => {
expect(nodeSetup).toContain("cache: npm");
expect(nodeSetup).toContain("cache-dependency-path: package-lock.json");
expect(nodeSetup).toContain("run: npm ci --include=dev");
expect(nodeSetup).not.toContain("path: node_modules");
expect(nodeSetup).not.toContain("cache-hit");
});

it("keeps quarantined and mockup UI specs in one advisory lane", () => {
Expand Down
6 changes: 5 additions & 1 deletion tests/installed-lock-parity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,7 @@ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "nod
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { installedLockParity } from "../scripts/check-installed-lock-parity.mjs";
import { criticalInstalledPackages, installedLockParity } from "../scripts/check-installed-lock-parity.mjs";

const temporaryRoots: string[] = [];

Expand All@@ -28,6 +28,10 @@ afterEach(() => {
});

describe("installedLockParity", () => {
it("covers the local validation toolchain as well as runtime packages", () => {
expect(criticalInstalledPackages).toEqual(expect.arrayContaining(["eslint", "playwright", "typescript", "vitest"]));
});

it("accepts an installed package that exactly matches the lockfile", () => {
expect(installedLockParity(fixture("16.2.11", "16.2.11"), ["next"])).toEqual([
expect.objectContaining({ packageName: "next", lockedVersion: "16.2.11", installedVersion: "16.2.11", ok: true }),
Expand Down
Loading