@lumenflow/cli 2.5.1 โ 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/gates-config.test.js +304 -0
- package/dist/gates.js +64 -15
- package/package.json +6 -6
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file gates-config.test.ts
|
|
3
|
+
* WU-1356: Tests for package manager and script name configuration.
|
|
4
|
+
*
|
|
5
|
+
* Tests configurable package_manager, gates.commands, test_runner, and build_command
|
|
6
|
+
* in .lumenflow.config.yaml for framework agnosticism.
|
|
7
|
+
*/
|
|
8
|
+
/* eslint-disable sonarjs/no-duplicate-string -- Test files intentionally repeat string literals for readability */
|
|
9
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
10
|
+
import * as fs from 'node:fs';
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import * as os from 'node:os';
|
|
13
|
+
import * as yaml from 'yaml';
|
|
14
|
+
// Import the schema and functions we're testing
|
|
15
|
+
import { PackageManagerSchema, TestRunnerSchema, GatesCommandsConfigSchema, parseConfig, } from '@lumenflow/core/dist/lumenflow-config-schema.js';
|
|
16
|
+
import { resolvePackageManager, resolveBuildCommand, resolveGatesCommands, resolveTestRunner, getIgnorePatterns, } from '@lumenflow/core/dist/gates-config.js';
|
|
17
|
+
describe('WU-1356: Package manager and script configuration', () => {
|
|
18
|
+
let tempDir;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lumenflow-test-'));
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
describe('PackageManagerSchema', () => {
|
|
26
|
+
it('accepts pnpm as default', () => {
|
|
27
|
+
const result = PackageManagerSchema.parse(undefined);
|
|
28
|
+
expect(result).toBe('pnpm');
|
|
29
|
+
});
|
|
30
|
+
it('accepts npm', () => {
|
|
31
|
+
const result = PackageManagerSchema.parse('npm');
|
|
32
|
+
expect(result).toBe('npm');
|
|
33
|
+
});
|
|
34
|
+
it('accepts yarn', () => {
|
|
35
|
+
const result = PackageManagerSchema.parse('yarn');
|
|
36
|
+
expect(result).toBe('yarn');
|
|
37
|
+
});
|
|
38
|
+
it('accepts bun', () => {
|
|
39
|
+
const result = PackageManagerSchema.parse('bun');
|
|
40
|
+
expect(result).toBe('bun');
|
|
41
|
+
});
|
|
42
|
+
it('rejects invalid package manager', () => {
|
|
43
|
+
expect(() => PackageManagerSchema.parse('invalid')).toThrow();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('TestRunnerSchema', () => {
|
|
47
|
+
it('accepts vitest as default', () => {
|
|
48
|
+
const result = TestRunnerSchema.parse(undefined);
|
|
49
|
+
expect(result).toBe('vitest');
|
|
50
|
+
});
|
|
51
|
+
it('accepts jest', () => {
|
|
52
|
+
const result = TestRunnerSchema.parse('jest');
|
|
53
|
+
expect(result).toBe('jest');
|
|
54
|
+
});
|
|
55
|
+
it('accepts mocha', () => {
|
|
56
|
+
const result = TestRunnerSchema.parse('mocha');
|
|
57
|
+
expect(result).toBe('mocha');
|
|
58
|
+
});
|
|
59
|
+
it('rejects invalid test runner', () => {
|
|
60
|
+
expect(() => TestRunnerSchema.parse('invalid')).toThrow();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('GatesCommandsConfigSchema', () => {
|
|
64
|
+
it('has sensible defaults for test commands', () => {
|
|
65
|
+
const result = GatesCommandsConfigSchema.parse({});
|
|
66
|
+
expect(result.test_full).toBeDefined();
|
|
67
|
+
expect(result.test_docs_only).toBeDefined();
|
|
68
|
+
expect(result.test_incremental).toBeDefined();
|
|
69
|
+
});
|
|
70
|
+
it('allows custom test commands', () => {
|
|
71
|
+
const config = {
|
|
72
|
+
test_full: 'npm test',
|
|
73
|
+
test_docs_only: 'npm test -- --grep docs',
|
|
74
|
+
test_incremental: 'npm test -- --changed',
|
|
75
|
+
};
|
|
76
|
+
const result = GatesCommandsConfigSchema.parse(config);
|
|
77
|
+
expect(result.test_full).toBe('npm test');
|
|
78
|
+
expect(result.test_docs_only).toBe('npm test -- --grep docs');
|
|
79
|
+
expect(result.test_incremental).toBe('npm test -- --changed');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('LumenFlowConfigSchema - package_manager field', () => {
|
|
83
|
+
it('includes package_manager with default pnpm', () => {
|
|
84
|
+
const config = parseConfig({});
|
|
85
|
+
expect(config.package_manager).toBe('pnpm');
|
|
86
|
+
});
|
|
87
|
+
it('accepts npm as package_manager', () => {
|
|
88
|
+
const config = parseConfig({ package_manager: 'npm' });
|
|
89
|
+
expect(config.package_manager).toBe('npm');
|
|
90
|
+
});
|
|
91
|
+
it('accepts yarn as package_manager', () => {
|
|
92
|
+
const config = parseConfig({ package_manager: 'yarn' });
|
|
93
|
+
expect(config.package_manager).toBe('yarn');
|
|
94
|
+
});
|
|
95
|
+
it('accepts bun as package_manager', () => {
|
|
96
|
+
const config = parseConfig({ package_manager: 'bun' });
|
|
97
|
+
expect(config.package_manager).toBe('bun');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('LumenFlowConfigSchema - test_runner field', () => {
|
|
101
|
+
it('includes test_runner with default vitest', () => {
|
|
102
|
+
const config = parseConfig({});
|
|
103
|
+
expect(config.test_runner).toBe('vitest');
|
|
104
|
+
});
|
|
105
|
+
it('accepts jest as test_runner', () => {
|
|
106
|
+
const config = parseConfig({ test_runner: 'jest' });
|
|
107
|
+
expect(config.test_runner).toBe('jest');
|
|
108
|
+
});
|
|
109
|
+
it('accepts mocha as test_runner', () => {
|
|
110
|
+
const config = parseConfig({ test_runner: 'mocha' });
|
|
111
|
+
expect(config.test_runner).toBe('mocha');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe('LumenFlowConfigSchema - gates.commands section', () => {
|
|
115
|
+
it('includes gates.commands with defaults', () => {
|
|
116
|
+
const config = parseConfig({});
|
|
117
|
+
expect(config.gates.commands).toBeDefined();
|
|
118
|
+
expect(config.gates.commands.test_full).toBeDefined();
|
|
119
|
+
expect(config.gates.commands.test_docs_only).toBeDefined();
|
|
120
|
+
expect(config.gates.commands.test_incremental).toBeDefined();
|
|
121
|
+
});
|
|
122
|
+
it('allows custom gates commands configuration', () => {
|
|
123
|
+
const config = parseConfig({
|
|
124
|
+
gates: {
|
|
125
|
+
commands: {
|
|
126
|
+
test_full: 'npm test',
|
|
127
|
+
test_docs_only: 'npm test -- --docs',
|
|
128
|
+
test_incremental: 'npm test -- --changed',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
expect(config.gates.commands.test_full).toBe('npm test');
|
|
133
|
+
expect(config.gates.commands.test_docs_only).toBe('npm test -- --docs');
|
|
134
|
+
expect(config.gates.commands.test_incremental).toBe('npm test -- --changed');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('LumenFlowConfigSchema - build_command field', () => {
|
|
138
|
+
it('includes build_command with default for pnpm', () => {
|
|
139
|
+
const config = parseConfig({});
|
|
140
|
+
expect(config.build_command).toBe('pnpm --filter @lumenflow/cli build');
|
|
141
|
+
});
|
|
142
|
+
it('allows custom build_command', () => {
|
|
143
|
+
const config = parseConfig({ build_command: 'npm run build' });
|
|
144
|
+
expect(config.build_command).toBe('npm run build');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe('resolvePackageManager', () => {
|
|
148
|
+
it('returns pnpm when no config file exists', () => {
|
|
149
|
+
const result = resolvePackageManager(tempDir);
|
|
150
|
+
expect(result).toBe('pnpm');
|
|
151
|
+
});
|
|
152
|
+
it('returns configured package manager from config file', () => {
|
|
153
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
154
|
+
fs.writeFileSync(configPath, yaml.stringify({ package_manager: 'npm' }));
|
|
155
|
+
const result = resolvePackageManager(tempDir);
|
|
156
|
+
expect(result).toBe('npm');
|
|
157
|
+
});
|
|
158
|
+
it('returns yarn when configured', () => {
|
|
159
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
160
|
+
fs.writeFileSync(configPath, yaml.stringify({ package_manager: 'yarn' }));
|
|
161
|
+
const result = resolvePackageManager(tempDir);
|
|
162
|
+
expect(result).toBe('yarn');
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('resolveBuildCommand', () => {
|
|
166
|
+
it('returns default build command when no config file exists', () => {
|
|
167
|
+
const result = resolveBuildCommand(tempDir);
|
|
168
|
+
expect(result).toBe('pnpm --filter @lumenflow/cli build');
|
|
169
|
+
});
|
|
170
|
+
it('returns configured build_command from config file', () => {
|
|
171
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
172
|
+
fs.writeFileSync(configPath, yaml.stringify({ build_command: 'npm run build' }));
|
|
173
|
+
const result = resolveBuildCommand(tempDir);
|
|
174
|
+
expect(result).toBe('npm run build');
|
|
175
|
+
});
|
|
176
|
+
it('adapts default build command for different package managers', () => {
|
|
177
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
178
|
+
fs.writeFileSync(configPath, yaml.stringify({ package_manager: 'npm' }));
|
|
179
|
+
const result = resolveBuildCommand(tempDir);
|
|
180
|
+
expect(result).toContain('npm');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('resolveGatesCommands', () => {
|
|
184
|
+
it('returns default commands when no config file exists', () => {
|
|
185
|
+
const commands = resolveGatesCommands(tempDir);
|
|
186
|
+
expect(commands.test_full).toBeDefined();
|
|
187
|
+
expect(commands.test_docs_only).toBeDefined();
|
|
188
|
+
expect(commands.test_incremental).toBeDefined();
|
|
189
|
+
});
|
|
190
|
+
it('returns configured commands from config file', () => {
|
|
191
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
192
|
+
fs.writeFileSync(configPath, yaml.stringify({
|
|
193
|
+
gates: {
|
|
194
|
+
commands: {
|
|
195
|
+
test_full: 'npm test',
|
|
196
|
+
test_docs_only: 'npm test -- --docs',
|
|
197
|
+
test_incremental: 'npm test -- --changed',
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
}));
|
|
201
|
+
const commands = resolveGatesCommands(tempDir);
|
|
202
|
+
expect(commands.test_full).toBe('npm test');
|
|
203
|
+
expect(commands.test_docs_only).toBe('npm test -- --docs');
|
|
204
|
+
expect(commands.test_incremental).toBe('npm test -- --changed');
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('resolveTestRunner', () => {
|
|
208
|
+
it('returns vitest when no config file exists', () => {
|
|
209
|
+
const result = resolveTestRunner(tempDir);
|
|
210
|
+
expect(result).toBe('vitest');
|
|
211
|
+
});
|
|
212
|
+
it('returns jest when configured', () => {
|
|
213
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
214
|
+
fs.writeFileSync(configPath, yaml.stringify({ test_runner: 'jest' }));
|
|
215
|
+
const result = resolveTestRunner(tempDir);
|
|
216
|
+
expect(result).toBe('jest');
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
describe('getIgnorePatterns', () => {
|
|
220
|
+
it('returns .turbo pattern for vitest', () => {
|
|
221
|
+
const patterns = getIgnorePatterns('vitest');
|
|
222
|
+
expect(patterns).toContain('.turbo');
|
|
223
|
+
});
|
|
224
|
+
it('returns different pattern for jest', () => {
|
|
225
|
+
const patterns = getIgnorePatterns('jest');
|
|
226
|
+
expect(patterns).not.toContain('.turbo');
|
|
227
|
+
});
|
|
228
|
+
it('returns custom pattern from config', () => {
|
|
229
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
230
|
+
fs.writeFileSync(configPath, yaml.stringify({
|
|
231
|
+
gates: {
|
|
232
|
+
ignore_patterns: ['.nx', 'dist'],
|
|
233
|
+
},
|
|
234
|
+
}));
|
|
235
|
+
// This would be a config-aware version
|
|
236
|
+
const patterns = getIgnorePatterns('jest');
|
|
237
|
+
expect(Array.isArray(patterns)).toBe(true);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
describe('WU-1356: npm+jest configuration', () => {
|
|
242
|
+
let tempDir;
|
|
243
|
+
beforeEach(() => {
|
|
244
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lumenflow-npm-jest-'));
|
|
245
|
+
});
|
|
246
|
+
afterEach(() => {
|
|
247
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
248
|
+
});
|
|
249
|
+
it('supports npm+jest configuration', () => {
|
|
250
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
251
|
+
fs.writeFileSync(configPath, yaml.stringify({
|
|
252
|
+
package_manager: 'npm',
|
|
253
|
+
test_runner: 'jest',
|
|
254
|
+
gates: {
|
|
255
|
+
commands: {
|
|
256
|
+
test_full: 'npm test',
|
|
257
|
+
test_docs_only: 'npm test -- --testPathPattern=docs',
|
|
258
|
+
test_incremental: 'npm test -- --onlyChanged',
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
build_command: 'npm run build',
|
|
262
|
+
}));
|
|
263
|
+
const pkgManager = resolvePackageManager(tempDir);
|
|
264
|
+
const testRunner = resolveTestRunner(tempDir);
|
|
265
|
+
const commands = resolveGatesCommands(tempDir);
|
|
266
|
+
const buildCmd = resolveBuildCommand(tempDir);
|
|
267
|
+
expect(pkgManager).toBe('npm');
|
|
268
|
+
expect(testRunner).toBe('jest');
|
|
269
|
+
expect(commands.test_full).toBe('npm test');
|
|
270
|
+
expect(commands.test_incremental).toBe('npm test -- --onlyChanged');
|
|
271
|
+
expect(buildCmd).toBe('npm run build');
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
describe('WU-1356: yarn+nx configuration', () => {
|
|
275
|
+
let tempDir;
|
|
276
|
+
beforeEach(() => {
|
|
277
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lumenflow-yarn-nx-'));
|
|
278
|
+
});
|
|
279
|
+
afterEach(() => {
|
|
280
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
281
|
+
});
|
|
282
|
+
it('supports yarn+nx configuration', () => {
|
|
283
|
+
const configPath = path.join(tempDir, '.lumenflow.config.yaml');
|
|
284
|
+
fs.writeFileSync(configPath, yaml.stringify({
|
|
285
|
+
package_manager: 'yarn',
|
|
286
|
+
test_runner: 'jest',
|
|
287
|
+
gates: {
|
|
288
|
+
commands: {
|
|
289
|
+
test_full: 'yarn nx run-many --target=test --all',
|
|
290
|
+
test_docs_only: 'yarn nx test docs',
|
|
291
|
+
test_incremental: 'yarn nx affected --target=test',
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
build_command: 'yarn nx build @lumenflow/cli',
|
|
295
|
+
}));
|
|
296
|
+
const pkgManager = resolvePackageManager(tempDir);
|
|
297
|
+
const commands = resolveGatesCommands(tempDir);
|
|
298
|
+
const buildCmd = resolveBuildCommand(tempDir);
|
|
299
|
+
expect(pkgManager).toBe('yarn');
|
|
300
|
+
expect(commands.test_full).toBe('yarn nx run-many --target=test --all');
|
|
301
|
+
expect(commands.test_incremental).toBe('yarn nx affected --target=test');
|
|
302
|
+
expect(buildCmd).toBe('yarn nx build @lumenflow/cli');
|
|
303
|
+
});
|
|
304
|
+
});
|
package/dist/gates.js
CHANGED
|
@@ -65,7 +65,8 @@ import { runSystemMapValidation } from '@lumenflow/core/dist/system-map-validato
|
|
|
65
65
|
// WU-1191: Lane health gate configuration
|
|
66
66
|
// WU-1262: Coverage config from methodology policy
|
|
67
67
|
// WU-1280: Test policy for tests_required (warn vs block on test failures)
|
|
68
|
-
|
|
68
|
+
// WU-1356: Configurable package manager and test commands
|
|
69
|
+
import { loadLaneHealthConfig, resolveTestPolicy, resolveGatesCommands, resolveTestRunner, } from '@lumenflow/core/dist/gates-config.js';
|
|
69
70
|
// WU-1191: Lane health check
|
|
70
71
|
import { runLaneHealthCheck } from './lane-health.js';
|
|
71
72
|
// WU-1315: Onboarding smoke test
|
|
@@ -184,8 +185,24 @@ const PRETTIER_CONFIG_FILES = new Set([
|
|
|
184
185
|
'prettier.config.mjs',
|
|
185
186
|
'.prettierignore',
|
|
186
187
|
]);
|
|
187
|
-
|
|
188
|
-
const
|
|
188
|
+
// WU-1356: Extended to support multiple build tools and test runners
|
|
189
|
+
const TEST_CONFIG_BASENAMES = new Set([
|
|
190
|
+
'turbo.json', // Turborepo
|
|
191
|
+
'nx.json', // Nx
|
|
192
|
+
'lerna.json', // Lerna
|
|
193
|
+
'pnpm-lock.yaml',
|
|
194
|
+
'package-lock.json',
|
|
195
|
+
'yarn.lock',
|
|
196
|
+
'bun.lockb',
|
|
197
|
+
'package.json',
|
|
198
|
+
]);
|
|
199
|
+
// WU-1356: Extended to support vitest, jest, and mocha config patterns
|
|
200
|
+
const TEST_CONFIG_PATTERNS = [
|
|
201
|
+
/^vitest\.config\.(ts|mts|js|mjs|cjs)$/i,
|
|
202
|
+
/^jest\.config\.(ts|js|mjs|cjs|json)$/i,
|
|
203
|
+
/^\.mocharc\.(js|json|yaml|yml)$/i,
|
|
204
|
+
/^tsconfig(\..+)?\.json$/i,
|
|
205
|
+
];
|
|
189
206
|
function normalizePath(filePath) {
|
|
190
207
|
return filePath.replace(/\\/g, '/');
|
|
191
208
|
}
|
|
@@ -380,9 +397,11 @@ export function loadCurrentWUCodePaths(options = {}) {
|
|
|
380
397
|
}
|
|
381
398
|
/**
|
|
382
399
|
* WU-1299: Run filtered tests for docs-only mode
|
|
400
|
+
* WU-1356: Updated to use configured test command
|
|
383
401
|
*
|
|
384
402
|
* When --docs-only is passed and code_paths contains packages, this runs tests
|
|
385
|
-
* only for those specific packages
|
|
403
|
+
* only for those specific packages. The filter syntax adapts to the configured
|
|
404
|
+
* build tool (turbo, nx, or plain package manager).
|
|
386
405
|
*
|
|
387
406
|
* @param options - Options including packages to test and agent log context
|
|
388
407
|
* @returns Result object with ok status and duration
|
|
@@ -395,10 +414,20 @@ async function runDocsOnlyFilteredTests({ packages, agentLog, }) {
|
|
|
395
414
|
return { ok: true, duration: Date.now() - start };
|
|
396
415
|
}
|
|
397
416
|
logLine(`\n> Tests (docs-only filtered: ${packages.join(', ')})\n`);
|
|
398
|
-
//
|
|
399
|
-
|
|
417
|
+
// WU-1356: Use configured test command with filter
|
|
418
|
+
const gatesCommands = resolveGatesCommands(process.cwd());
|
|
419
|
+
// If there's a configured test_docs_only command, use it
|
|
420
|
+
if (gatesCommands.test_docs_only) {
|
|
421
|
+
const result = run(gatesCommands.test_docs_only, { agentLog });
|
|
422
|
+
return { ok: result.ok, duration: Date.now() - start };
|
|
423
|
+
}
|
|
424
|
+
// Otherwise, use the full test command with filter args
|
|
425
|
+
// Build filter args for each package (works with turbo, nx, and pnpm/yarn workspaces)
|
|
400
426
|
const filterArgs = packages.map((pkg) => `--filter=${pkg}`);
|
|
401
|
-
const
|
|
427
|
+
const baseCmd = gatesCommands.test_full;
|
|
428
|
+
// Append filter args to the base command
|
|
429
|
+
const filteredCmd = `${baseCmd} ${filterArgs.join(' ')}`;
|
|
430
|
+
const result = run(filteredCmd, { agentLog });
|
|
402
431
|
return { ok: result.ok, duration: Date.now() - start };
|
|
403
432
|
}
|
|
404
433
|
export function parsePrettierListOutput(output) {
|
|
@@ -782,7 +811,8 @@ async function runIncrementalLint({ agentLog, } = {}) {
|
|
|
782
811
|
}
|
|
783
812
|
}
|
|
784
813
|
/**
|
|
785
|
-
* Run changed tests using
|
|
814
|
+
* Run changed tests using configured test runner's incremental mode.
|
|
815
|
+
* WU-1356: Updated to use configured commands from gates-config.
|
|
786
816
|
* Falls back to full test suite if on main branch or if the run fails.
|
|
787
817
|
*
|
|
788
818
|
* @returns {{ ok: boolean, duration: number, isIncremental: boolean }}
|
|
@@ -797,13 +827,16 @@ async function runChangedTests({ agentLog, } = {}) {
|
|
|
797
827
|
}
|
|
798
828
|
writeSync(agentLog.logFd, `${line}\n`);
|
|
799
829
|
};
|
|
830
|
+
// WU-1356: Get configured commands
|
|
831
|
+
const gatesCommands = resolveGatesCommands(process.cwd());
|
|
832
|
+
const testRunner = resolveTestRunner(process.cwd());
|
|
800
833
|
try {
|
|
801
834
|
const git = getGitForCwd();
|
|
802
835
|
const currentBranch = await git.getCurrentBranch();
|
|
803
836
|
const isMainBranch = currentBranch === BRANCHES.MAIN || currentBranch === BRANCHES.MASTER;
|
|
804
837
|
if (isMainBranch) {
|
|
805
838
|
logLine('๐ On main branch - running full test suite');
|
|
806
|
-
const result = run(
|
|
839
|
+
const result = run(gatesCommands.test_full, { agentLog });
|
|
807
840
|
return { ...result, isIncremental: false };
|
|
808
841
|
}
|
|
809
842
|
let changedFiles = [];
|
|
@@ -841,16 +874,29 @@ async function runChangedTests({ agentLog, } = {}) {
|
|
|
841
874
|
logLine('โ ๏ธ Changed file list unavailable - running full test suite');
|
|
842
875
|
}
|
|
843
876
|
logLine('๐ Running full test suite to avoid missing coverage');
|
|
844
|
-
const result = run(
|
|
877
|
+
const result = run(gatesCommands.test_full, { agentLog });
|
|
845
878
|
return { ...result, duration: Date.now() - start, isIncremental: false };
|
|
846
879
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
880
|
+
// WU-1356: Use configured incremental test command
|
|
881
|
+
logLine(`\n> Running tests (${testRunner} --changed)\n`);
|
|
882
|
+
// If test_incremental is configured, use it directly
|
|
883
|
+
if (gatesCommands.test_incremental) {
|
|
884
|
+
const result = run(gatesCommands.test_incremental, { agentLog });
|
|
885
|
+
return { ...result, duration: Date.now() - start, isIncremental: true };
|
|
886
|
+
}
|
|
887
|
+
// Fallback: For vitest, use the built-in changed args helper
|
|
888
|
+
if (testRunner === 'vitest') {
|
|
889
|
+
const result = run(pnpmCmd('vitest', 'run', ...buildVitestChangedArgs({ baseBranch: 'origin/main' })), { agentLog });
|
|
890
|
+
return { ...result, duration: Date.now() - start, isIncremental: true };
|
|
891
|
+
}
|
|
892
|
+
// For other runners without configured incremental, fall back to full
|
|
893
|
+
logLine('โ ๏ธ No incremental test command configured, running full suite');
|
|
894
|
+
const result = run(gatesCommands.test_full, { agentLog });
|
|
895
|
+
return { ...result, duration: Date.now() - start, isIncremental: false };
|
|
850
896
|
}
|
|
851
897
|
catch (error) {
|
|
852
898
|
console.error('โ ๏ธ Changed tests failed, falling back to full suite:', error.message);
|
|
853
|
-
const result = run(
|
|
899
|
+
const result = run(gatesCommands.test_full, { agentLog });
|
|
854
900
|
return { ...result, isIncremental: false };
|
|
855
901
|
}
|
|
856
902
|
}
|
|
@@ -1055,6 +1101,8 @@ async function executeGates(opts) {
|
|
|
1055
1101
|
const testsRequired = resolvedTestPolicy.tests_required;
|
|
1056
1102
|
// WU-1191: Lane health gate mode (warn, error, or off)
|
|
1057
1103
|
const laneHealthMode = loadLaneHealthConfig(process.cwd());
|
|
1104
|
+
// WU-1356: Resolve configured gates commands for test execution
|
|
1105
|
+
const configuredGatesCommands = resolveGatesCommands(process.cwd());
|
|
1058
1106
|
if (useAgentMode) {
|
|
1059
1107
|
console.log(`๐งพ gates (agent mode): output -> ${agentLog.logPath} (use --verbose for streaming)\n`);
|
|
1060
1108
|
}
|
|
@@ -1185,10 +1233,11 @@ async function executeGates(opts) {
|
|
|
1185
1233
|
// WU-1920: Use changed tests by default, full suite with --full-tests
|
|
1186
1234
|
// WU-2244: --full-coverage implies --full-tests for accurate coverage
|
|
1187
1235
|
// WU-1280: When tests_required=false (methodology.testing: none), failures only warn
|
|
1236
|
+
// WU-1356: Use configured test command instead of hard-coded turbo
|
|
1188
1237
|
{
|
|
1189
1238
|
name: GATE_NAMES.TEST,
|
|
1190
1239
|
cmd: isFullTests || isFullCoverage
|
|
1191
|
-
?
|
|
1240
|
+
? configuredGatesCommands.test_full
|
|
1192
1241
|
: GATE_COMMANDS.INCREMENTAL_TEST,
|
|
1193
1242
|
warnOnly: !testsRequired,
|
|
1194
1243
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "Command-line interface for LumenFlow workflow framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lumenflow",
|
|
@@ -148,11 +148,11 @@
|
|
|
148
148
|
"pretty-ms": "^9.2.0",
|
|
149
149
|
"simple-git": "^3.30.0",
|
|
150
150
|
"yaml": "^2.8.2",
|
|
151
|
-
"@lumenflow/
|
|
152
|
-
"@lumenflow/
|
|
153
|
-
"@lumenflow/
|
|
154
|
-
"@lumenflow/agent": "2.
|
|
155
|
-
"@lumenflow/memory": "2.
|
|
151
|
+
"@lumenflow/core": "2.6.0",
|
|
152
|
+
"@lumenflow/initiatives": "2.6.0",
|
|
153
|
+
"@lumenflow/metrics": "2.6.0",
|
|
154
|
+
"@lumenflow/agent": "2.6.0",
|
|
155
|
+
"@lumenflow/memory": "2.6.0"
|
|
156
156
|
},
|
|
157
157
|
"devDependencies": {
|
|
158
158
|
"@vitest/coverage-v8": "^4.0.17",
|