fix: stop ignoring tracked storage paths (#1211)

This commit is contained in:
Brad Groux 2026-08-23 20:23:07 -05:00 committed by GitHub
parent 8ca792ad87
commit 2cbfd3b215
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 140 additions and 5 deletions

View file

@ -79,6 +79,7 @@ jobs:
scripts/check-actions-pinned.test.mjs
scripts/check-delivery-cadence.test.mjs
scripts/check-security-gates.test.mjs
scripts/check-tracked-ignore.test.mjs
scripts/select-ci-test-scope.test.mjs
scripts/verify-full-suite-job-evidence.test.mjs
@ -91,6 +92,9 @@ jobs:
- name: Guard continuous security gates
run: node scripts/check-security-gates.mjs
- name: Reject tracked files covered by ignore rules
run: node scripts/check-tracked-ignore.mjs
- name: Find reviewed full-suite evidence
id: reviewed_full
if: github.event_name == 'push'

8
.gitignore vendored
View file

@ -44,10 +44,8 @@ tasks/archive/*.md
tasks/backlog/*.md
tasks/attachments/
tasks/archive-attachments/
storage/
server/storage/
!server/src/storage/
!server/src/storage/**
/storage/
/server/storage/
.veritas-kanban/*
!.veritas-kanban/.gitkeep
.veritas-desktop-dev/
@ -112,7 +110,7 @@ tasks/
!tasks/
!tasks/examples/
!tasks/examples/*.md
.veritas-kanban/
/.veritas-kanban/
# Local security middleware (not shared)
server/src/middleware/external-api-key.ts

View file

@ -1,4 +1,5 @@
pnpm check:security-artifacts
pnpm check:actions-pinned
pnpm check:tracked-ignore
node scripts/check-delivery-cadence.mjs
npx lint-staged

View file

@ -73,6 +73,7 @@ pnpm lint:fix
# Smoke checks
pnpm check:actions-pinned # Rejects mutable external GitHub Action references
pnpm check:pnpm-settings # Validates package manager fields match this file
pnpm check:tracked-ignore # Rejects tracked files covered by ignore rules
pnpm check:coverage-policy # Validates coverage policy, configs, CI, and regression tests
pnpm check:delivery-cadence # Prevents verification and review policy drift
pnpm check:security-gates # Validates CodeQL/gitleaks workflow and exact suppressions

View file

@ -41,6 +41,7 @@
"check:vite-native-config": "pnpm --filter @veritas-kanban/web exec vite build --configLoader native && vitest run --configLoader native web/src/lib/__tests__/client-policy.test.ts",
"check:pnpm-settings": "node scripts/check-pnpm-settings.mjs",
"check:security-artifacts": "node scripts/check-security-artifacts.mjs",
"check:tracked-ignore": "node --test scripts/check-tracked-ignore.test.mjs && node scripts/check-tracked-ignore.mjs",
"check:service-filesystem-boundary": "node --test scripts/check-service-filesystem-boundary.test.mjs && node scripts/check-service-filesystem-boundary.mjs",
"typecheck": "pnpm --filter @veritas-kanban/shared build && pnpm -r typecheck",
"test": "pnpm test:unit",

View file

@ -0,0 +1,53 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
function runGit(args, options = {}) {
const result = spawnSync('git', args, {
cwd: options.cwd,
env: options.env,
input: options.input,
maxBuffer: 16 * 1024 * 1024,
});
if (result.error) throw result.error;
return result;
}
export function findIgnoredTrackedFiles(cwd = process.cwd(), env = process.env) {
const tracked = runGit(['ls-files', '-z'], { cwd, env });
if (tracked.status !== 0) {
throw new Error(`git ls-files failed: ${tracked.stderr.toString('utf8').trim()}`);
}
const ignored = runGit(['check-ignore', '--no-index', '-z', '--stdin'], {
cwd,
env,
input: tracked.stdout,
});
if (ignored.status === 1) return [];
if (ignored.status !== 0) {
throw new Error(`git check-ignore failed: ${ignored.stderr.toString('utf8').trim()}`);
}
return ignored.stdout.toString('utf8').split('\0').filter(Boolean).sort();
}
export function runTrackedIgnoreCheck(cwd = process.cwd()) {
const ignored = findIgnoredTrackedFiles(cwd);
if (ignored.length === 0) {
console.log('Tracked-file ignore check passed.');
return true;
}
console.error('Tracked-file ignore check failed. These tracked paths match ignore rules:');
for (const file of ignored) console.error(`- ${file}`);
process.exitCode = 1;
return false;
}
function isDirectExecution() {
return process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
}
if (isDirectExecution()) runTrackedIgnoreCheck();

View file

@ -0,0 +1,77 @@
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import { findIgnoredTrackedFiles } from './check-tracked-ignore.mjs';
const isolatedGitEnvironment = Object.fromEntries(
Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_'))
);
async function createRepository() {
const root = await mkdtemp(path.join(os.tmpdir(), 'veritas-tracked-ignore-'));
execFileSync('git', ['init', '--quiet'], { cwd: root, env: isolatedGitEnvironment });
return root;
}
async function write(root, relativePath, content = '') {
const destination = path.join(root, relativePath);
await mkdir(path.dirname(destination), { recursive: true });
await writeFile(destination, content, 'utf8');
}
test('reports a tracked file covered by a later ignore rule', async (t) => {
const root = await createRepository();
t.after(() => rm(root, { recursive: true, force: true }));
await write(root, 'server/src/__tests__/storage/example.test.ts', 'export {};\n');
execFileSync('git', ['add', 'server/src/__tests__/storage/example.test.ts'], {
cwd: root,
env: isolatedGitEnvironment,
});
await write(root, '.gitignore', 'storage/\n');
assert.deepEqual(findIgnoredTrackedFiles(root, isolatedGitEnvironment), [
'server/src/__tests__/storage/example.test.ts',
]);
});
test('allows tracked storage source while anchored runtime roots stay ignored', async (t) => {
const root = await createRepository();
t.after(() => rm(root, { recursive: true, force: true }));
await write(root, '.gitignore', '/storage/\n/server/storage/\n');
await write(root, 'server/src/storage/repository.ts', 'export {};\n');
await write(root, 'server/src/__tests__/storage/repository.test.ts', 'export {};\n');
execFileSync(
'git',
[
'add',
'.gitignore',
'server/src/storage/repository.ts',
'server/src/__tests__/storage/repository.test.ts',
],
{ cwd: root, env: isolatedGitEnvironment }
);
assert.deepEqual(findIgnoredTrackedFiles(root, isolatedGitEnvironment), []);
assert.equal(
execFileSync('git', ['check-ignore', 'storage/runtime.json'], {
cwd: root,
env: isolatedGitEnvironment,
encoding: 'utf8',
}),
'storage/runtime.json\n'
);
assert.equal(
execFileSync('git', ['check-ignore', 'server/storage/runtime.json'], {
cwd: root,
env: isolatedGitEnvironment,
encoding: 'utf8',
}),
'server/storage/runtime.json\n'
);
});