feat: add isolated ingest patch helpers

This commit is contained in:
Andrey Avtomonov 2026-05-17 21:22:15 +02:00
parent 01b7f54253
commit 739d88420e
4 changed files with 268 additions and 1 deletions

View file

@ -0,0 +1,81 @@
import { describe, expect, it } from 'vitest';
import { assertPatchAllowedForWorkUnit, parsePatchTouchedPaths, textArtifactRoots } from './git-patch.js';
describe('isolated diff patch contract', () => {
it('parses touched paths from no-rename git patches', () => {
const patch = [
'diff --git a/wiki/global/a.md b/wiki/global/a.md',
'index 1111111..2222222 100644',
'--- a/wiki/global/a.md',
'+++ b/wiki/global/a.md',
'@@ -1 +1 @@',
'-old',
'+new',
'diff --git a/semantic-layer/c1/orders.yaml b/semantic-layer/c1/orders.yaml',
'new file mode 100644',
'--- /dev/null',
'+++ b/semantic-layer/c1/orders.yaml',
'@@ -0,0 +1 @@',
'+name: orders',
'',
].join('\n');
expect(parsePatchTouchedPaths(patch)).toEqual([
{
path: 'wiki/global/a.md',
oldPath: 'wiki/global/a.md',
newPath: 'wiki/global/a.md',
mode: '100644',
binary: false,
},
{
path: 'semantic-layer/c1/orders.yaml',
oldPath: 'semantic-layer/c1/orders.yaml',
newPath: 'semantic-layer/c1/orders.yaml',
mode: '100644',
binary: false,
},
]);
});
it('rejects semantic-layer paths for slDisallowed work units', () => {
const patch = 'diff --git a/semantic-layer/c1/orders.yaml b/semantic-layer/c1/orders.yaml\nindex 1..2 100644\n';
expect(() =>
assertPatchAllowedForWorkUnit({
unitKey: 'lookml-mismatch',
patch,
slDisallowed: true,
}),
).toThrow(/slDisallowed WorkUnit lookml-mismatch touched semantic-layer\/c1\/orders.yaml/);
});
it('rejects executable and binary changes under known text artifact roots', () => {
expect(textArtifactRoots).toEqual(['wiki/', 'semantic-layer/']);
const executablePatch =
'diff --git a/wiki/global/a.md b/wiki/global/a.md\nold mode 100644\nnew mode 100755\nindex 1..2\n';
expect(() =>
assertPatchAllowedForWorkUnit({
unitKey: 'wu-1',
patch: executablePatch,
slDisallowed: false,
}),
).toThrow(/unexpected executable mode under wiki\/global\/a.md/);
const binaryPatch = [
'diff --git a/semantic-layer/c1/orders.yaml b/semantic-layer/c1/orders.yaml',
'index 1111111..2222222 100644',
'GIT binary patch',
'literal 0',
'',
].join('\n');
expect(() =>
assertPatchAllowedForWorkUnit({
unitKey: 'wu-2',
patch: binaryPatch,
slDisallowed: false,
}),
).toThrow(/unexpected binary patch under semantic-layer\/c1\/orders.yaml/);
});
});

View file

@ -0,0 +1,92 @@
export const textArtifactRoots = ['wiki/', 'semantic-layer/'] as const;
export interface PatchTouchedPath {
path: string;
oldPath: string;
newPath: string;
mode: string | null;
binary: boolean;
}
export interface PatchPolicyInput {
unitKey: string;
patch: string;
slDisallowed: boolean;
}
function stripPrefix(path: string): string {
return path.replace(/^[ab]\//, '');
}
function isTextArtifactPath(path: string): boolean {
return textArtifactRoots.some((root) => path.startsWith(root));
}
export function parsePatchTouchedPaths(patch: string): PatchTouchedPath[] {
const lines = patch.split('\n');
const entries: PatchTouchedPath[] = [];
let current: PatchTouchedPath | null = null;
const pushCurrent = () => {
if (current) {
entries.push(current);
}
};
for (const line of lines) {
const diffMatch = /^diff --git (.+) (.+)$/.exec(line);
if (diffMatch) {
pushCurrent();
const oldPath = stripPrefix(diffMatch[1] ?? '');
const newPath = stripPrefix(diffMatch[2] ?? '');
current = {
path: newPath === '/dev/null' ? oldPath : newPath,
oldPath,
newPath,
mode: null,
binary: false,
};
continue;
}
if (!current) {
continue;
}
const indexMode = /^index [0-9a-f]+\.\.[0-9a-f]+(?: ([0-7]{6}))?$/.exec(line);
if (indexMode?.[1]) {
current.mode = indexMode[1];
}
const newMode = /^new mode ([0-7]{6})$/.exec(line);
if (newMode) {
current.mode = newMode[1] ?? current.mode;
}
const newFileMode = /^new file mode ([0-7]{6})$/.exec(line);
if (newFileMode) {
current.mode = newFileMode[1] ?? current.mode;
}
if (line === 'GIT binary patch' || line.startsWith('Binary files ')) {
current.binary = true;
}
}
pushCurrent();
return entries;
}
export function assertPatchAllowedForWorkUnit(input: PatchPolicyInput): PatchTouchedPath[] {
const touched = parsePatchTouchedPaths(input.patch);
for (const entry of touched) {
if (input.slDisallowed && entry.path.startsWith('semantic-layer/')) {
throw new Error(`slDisallowed WorkUnit ${input.unitKey} touched ${entry.path}`);
}
if (!isTextArtifactPath(entry.path)) {
continue;
}
if (entry.binary) {
throw new Error(`unexpected binary patch under ${entry.path}`);
}
if (entry.mode && entry.mode !== '100644') {
throw new Error(`unexpected executable mode under ${entry.path}: ${entry.mode}`);
}
}
return touched;
}