-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathshared.ts
More file actions
120 lines (107 loc) · 3.46 KB
/
Copy pathshared.ts
File metadata and controls
120 lines (107 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { constants } from 'node:fs';
import { access, stat } from 'node:fs/promises';
import path from 'node:path';
import fg from 'fast-glob';
export async function resolveEvalPaths(evalPaths: string[], cwd: string): Promise<string[]> {
const normalizedInputs = evalPaths.map((value) => value?.trim()).filter((value) => value);
if (normalizedInputs.length === 0) {
throw new Error('No eval paths provided.');
}
// Separate negation patterns (!glob) from include patterns.
// Negation patterns are passed to fast-glob as `ignore`.
const includePatterns: string[] = [];
const ignorePatterns: string[] = [];
for (const input of normalizedInputs) {
if (input.startsWith('!')) {
ignorePatterns.push(input.slice(1));
} else {
includePatterns.push(input);
}
}
if (includePatterns.length === 0) {
throw new Error('No eval paths provided (only negation patterns found).');
}
const unmatched: string[] = [];
const results = new Set<string>();
for (const pattern of includePatterns) {
// If the pattern points to an existing file or directory, short-circuit globbing
const candidatePath = path.isAbsolute(pattern)
? path.normalize(pattern)
: path.resolve(cwd, pattern);
try {
const stats = await stat(candidatePath);
if (stats.isFile() && /\.(ya?ml|jsonl|json)$/i.test(candidatePath)) {
results.add(candidatePath);
continue;
}
if (stats.isDirectory()) {
// Auto-expand directory to recursive eval file glob
const dirGlob = path.posix.join(candidatePath.replace(/\\/g, '/'), '**/*.eval.{yaml,yml}');
const dirMatches = await fg(dirGlob, {
absolute: true,
onlyFiles: true,
unique: true,
dot: true,
followSymbolicLinks: true,
ignore: ignorePatterns,
});
if (dirMatches.length === 0) {
unmatched.push(pattern);
} else {
for (const filePath of dirMatches) {
results.add(path.normalize(filePath));
}
}
continue;
}
} catch {
// fall through to glob matching
}
const globPattern = pattern.includes('\\') ? pattern.replace(/\\/g, '/') : pattern;
const matches = await fg(globPattern, {
cwd,
absolute: true,
onlyFiles: true,
unique: true,
dot: true,
followSymbolicLinks: true,
ignore: ignorePatterns,
});
const yamlMatches = matches.filter((filePath) => /\.(ya?ml|jsonl|json)$/i.test(filePath));
if (yamlMatches.length === 0) {
unmatched.push(pattern);
continue;
}
for (const filePath of yamlMatches) {
results.add(path.normalize(filePath));
}
}
if (unmatched.length > 0) {
throw new Error(
`No eval files matched: ${unmatched.join(
', ',
)}. Provide YAML, JSONL, or JSON paths or globs (e.g., "evals/**/*.yaml", "evals/**/*.jsonl", "evals.json").`,
);
}
const sorted = Array.from(results);
sorted.sort();
return sorted;
}
export async function findRepoRoot(start: string): Promise<string> {
const fallback = path.resolve(start);
let current: string | undefined = fallback;
while (current !== undefined) {
const candidate = path.join(current, '.git');
try {
await access(candidate, constants.F_OK);
return current;
} catch {
const parent = path.dirname(current);
if (parent === current) {
break;
}
current = parent;
}
}
return fallback;
}