mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-08-28 02:44:59 +00:00
fix: stop ignoring tracked storage paths (#1211)
This commit is contained in:
parent
8ca792ad87
commit
2cbfd3b215
7 changed files with 140 additions and 5 deletions
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
|
@ -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
8
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
53
scripts/check-tracked-ignore.mjs
Normal file
53
scripts/check-tracked-ignore.mjs
Normal 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();
|
||||
77
scripts/check-tracked-ignore.test.mjs
Normal file
77
scripts/check-tracked-ignore.test.mjs
Normal 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'
|
||||
);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue