@kabran-tecnologia/kabran-config 1.10.0 → 2.0.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/package.json +7 -1
- package/src/cli/commands/build.mjs +54 -0
- package/src/cli/commands/check.mjs +107 -0
- package/src/cli/commands/ci.mjs +109 -0
- package/src/cli/commands/test.mjs +133 -0
- package/src/cli/kabran.mjs +144 -0
- package/src/core/config-loader.mjs +305 -0
- package/src/schemas/ci-result.v2.schema.json +1 -1
- package/src/scripts/ci/ci-core.sh +1 -1
- package/src/scripts/ci-result-history.mjs +2 -2
- package/src/scripts/ci-result-utils.mjs +1 -1
- package/src/scripts/deploy/deploy-core.sh +1 -1
- package/src/scripts/env-validator.mjs +12 -11
- package/src/scripts/generate-ci-result.mjs +3 -3
- package/src/scripts/quality-standard-validator.mjs +24 -13
- package/src/scripts/readme-validator.mjs +35 -19
- package/src/scripts/setup.mjs +1 -0
- package/src/scripts/traceability/coverage-report.sh +1 -1
- package/src/scripts/traceability/traceability-core.sh +1 -1
- package/src/scripts/traceability/validate-traceability.sh +1 -1
- package/src/telemetry/README.md +11 -11
- package/src/telemetry/config/defaults.mjs +5 -7
- package/src/telemetry/shared/types.d.ts +1 -1
- package/templates/config/.prettierignore +35 -0
- package/templates/config/kabran.config.mjs +53 -0
- package/CI-CD-MIGRATION.md +0 -388
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kabran-tecnologia/kabran-config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Shared quality configurations, enforcement scripts, and CI/CD tooling for Kabran projects",
|
|
5
5
|
"author": "Kabran",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"access": "public"
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
|
12
|
+
"kabran": "src/cli/kabran.mjs",
|
|
12
13
|
"kabran-setup": "src/scripts/setup.mjs",
|
|
13
14
|
"kabran-ci": "src/scripts/ci/ci-runner.sh",
|
|
14
15
|
"kabran-pr-comment": "src/scripts/pr-quality-comment.mjs",
|
|
@@ -16,6 +17,11 @@
|
|
|
16
17
|
"kabran-coverage": "src/scripts/traceability/coverage-report.sh"
|
|
17
18
|
},
|
|
18
19
|
"exports": {
|
|
20
|
+
"./core/config-loader": "./src/core/config-loader.mjs",
|
|
21
|
+
"./cli/commands/check": "./src/cli/commands/check.mjs",
|
|
22
|
+
"./cli/commands/test": "./src/cli/commands/test.mjs",
|
|
23
|
+
"./cli/commands/ci": "./src/cli/commands/ci.mjs",
|
|
24
|
+
"./cli/commands/build": "./src/cli/commands/build.mjs",
|
|
19
25
|
"./eslint": "./src/eslint.mjs",
|
|
20
26
|
"./eslint/node": "./src/eslint-node.mjs",
|
|
21
27
|
"./eslint/react": "./src/eslint-react.mjs",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Command
|
|
3
|
+
*
|
|
4
|
+
* Runs the project build process.
|
|
5
|
+
*
|
|
6
|
+
* @module cli/commands/build
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {spawn} from 'node:child_process';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run a shell command and return exit code
|
|
13
|
+
* @param {string} cmd - Command to run
|
|
14
|
+
* @returns {Promise<number>} Exit code
|
|
15
|
+
*/
|
|
16
|
+
function runCommand(cmd) {
|
|
17
|
+
return new Promise(resolve => {
|
|
18
|
+
const proc = spawn('sh', ['-c', cmd], {stdio: 'inherit'});
|
|
19
|
+
proc.on('close', code => resolve(code ?? 0));
|
|
20
|
+
proc.on('error', () => resolve(1));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Run the build process
|
|
26
|
+
* @param {object} config - Project configuration from kabran.config.mjs
|
|
27
|
+
* @param {string[]} _args - CLI arguments (unused for now)
|
|
28
|
+
* @returns {Promise<number>} Exit code (0 = success, 1 = failure)
|
|
29
|
+
*/
|
|
30
|
+
export async function runBuild(config, _args) {
|
|
31
|
+
const buildConfig = config.build;
|
|
32
|
+
|
|
33
|
+
if (!buildConfig?.command) {
|
|
34
|
+
console.log('\n[Build] Skipped (not configured)');
|
|
35
|
+
console.log(' Add build.command to kabran.config.mjs to enable');
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log('='.repeat(50));
|
|
40
|
+
console.log('Running build');
|
|
41
|
+
console.log('='.repeat(50));
|
|
42
|
+
|
|
43
|
+
console.log(`\n[Build] ${buildConfig.command}`);
|
|
44
|
+
const code = await runCommand(buildConfig.command);
|
|
45
|
+
|
|
46
|
+
console.log('\n' + '-'.repeat(50));
|
|
47
|
+
if (code === 0) {
|
|
48
|
+
console.log('Build: PASSED');
|
|
49
|
+
} else {
|
|
50
|
+
console.log('Build: FAILED');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return code;
|
|
54
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check Command (L1 - Base of Quality Pyramid)
|
|
3
|
+
*
|
|
4
|
+
* Runs static analysis checks:
|
|
5
|
+
* - Lint (ESLint)
|
|
6
|
+
* - Types (TypeScript)
|
|
7
|
+
* - Format (Prettier)
|
|
8
|
+
* - Validators (README, ENV, Quality Standard)
|
|
9
|
+
*
|
|
10
|
+
* @module cli/commands/check
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {spawn} from 'node:child_process';
|
|
14
|
+
import {validateReadme} from '../../scripts/readme-validator.mjs';
|
|
15
|
+
import {validateEnv} from '../../scripts/env-validator.mjs';
|
|
16
|
+
import {validate as validateQuality} from '../../scripts/quality-standard-validator.mjs';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Run a shell command and return exit code
|
|
20
|
+
* @param {string} name - Step name for display
|
|
21
|
+
* @param {string} cmd - Command to run
|
|
22
|
+
* @returns {Promise<number>} Exit code
|
|
23
|
+
*/
|
|
24
|
+
function runCommand(name, cmd) {
|
|
25
|
+
return new Promise(resolve => {
|
|
26
|
+
console.log(`\n[${name}] ${cmd}`);
|
|
27
|
+
const proc = spawn('sh', ['-c', cmd], {stdio: 'inherit'});
|
|
28
|
+
proc.on('close', code => resolve(code ?? 0));
|
|
29
|
+
proc.on('error', () => resolve(1));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Run L1 checks (static analysis)
|
|
35
|
+
* @param {object} config - Project configuration from kabran.config.mjs
|
|
36
|
+
* @param {string[]} args - CLI arguments
|
|
37
|
+
* @returns {Promise<number>} Exit code (0 = success, 1 = failure)
|
|
38
|
+
*/
|
|
39
|
+
export async function runCheck(config, args) {
|
|
40
|
+
const fix = args.includes('--fix');
|
|
41
|
+
let failed = 0;
|
|
42
|
+
|
|
43
|
+
console.log('='.repeat(50));
|
|
44
|
+
console.log('Running L1 checks (Static Analysis)');
|
|
45
|
+
console.log('='.repeat(50));
|
|
46
|
+
|
|
47
|
+
// 1. Lint
|
|
48
|
+
if (config.check?.lint) {
|
|
49
|
+
const cmd = fix ? `${config.check.lint} --fix` : config.check.lint;
|
|
50
|
+
const code = await runCommand('Lint', cmd);
|
|
51
|
+
if (code !== 0) failed++;
|
|
52
|
+
} else {
|
|
53
|
+
console.log('\n[Lint] Skipped (not configured)');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 2. Types
|
|
57
|
+
if (config.check?.types) {
|
|
58
|
+
const code = await runCommand('Types', config.check.types);
|
|
59
|
+
if (code !== 0) failed++;
|
|
60
|
+
} else {
|
|
61
|
+
console.log('\n[Types] Skipped (not configured)');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 3. Format
|
|
65
|
+
if (config.check?.format) {
|
|
66
|
+
const cmd = fix ? config.check.format.replace('--check', '--write') : config.check.format;
|
|
67
|
+
const code = await runCommand('Format', cmd);
|
|
68
|
+
if (code !== 0) failed++;
|
|
69
|
+
} else {
|
|
70
|
+
console.log('\n[Format] Skipped (not configured)');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 4. Validators
|
|
74
|
+
console.log('\n' + '='.repeat(50));
|
|
75
|
+
console.log('Running validators');
|
|
76
|
+
console.log('='.repeat(50));
|
|
77
|
+
|
|
78
|
+
// README validator
|
|
79
|
+
console.log('\n--- README Validator ---');
|
|
80
|
+
const readme = await validateReadme(process.cwd(), true);
|
|
81
|
+
console.log(`Result: ${readme.valid ? 'PASS' : 'FAIL'}`);
|
|
82
|
+
if (!readme.valid) failed++;
|
|
83
|
+
|
|
84
|
+
// ENV validator
|
|
85
|
+
console.log('\n--- ENV Validator ---');
|
|
86
|
+
const env = await validateEnv(process.cwd(), true);
|
|
87
|
+
console.log(`Result: ${env.valid ? 'PASS' : 'FAIL'}`);
|
|
88
|
+
if (!env.valid) failed++;
|
|
89
|
+
|
|
90
|
+
// Quality Standard validator
|
|
91
|
+
console.log('\n--- Quality Standard Validator ---');
|
|
92
|
+
const quality = await validateQuality(process.cwd(), true);
|
|
93
|
+
console.log(`Result: ${quality.valid ? 'PASS' : 'FAIL'}`);
|
|
94
|
+
if (!quality.valid) failed++;
|
|
95
|
+
|
|
96
|
+
// Summary
|
|
97
|
+
console.log('\n' + '='.repeat(50));
|
|
98
|
+
if (failed === 0) {
|
|
99
|
+
console.log('L1 checks: PASSED');
|
|
100
|
+
console.log('='.repeat(50));
|
|
101
|
+
return 0;
|
|
102
|
+
} else {
|
|
103
|
+
console.log(`L1 checks: FAILED (${failed} error${failed > 1 ? 's' : ''})`);
|
|
104
|
+
console.log('='.repeat(50));
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI Command - Full Pipeline Execution
|
|
3
|
+
*
|
|
4
|
+
* Runs the complete CI pipeline with configurable steps.
|
|
5
|
+
* Default steps: check → test:unit → test:integration → build
|
|
6
|
+
*
|
|
7
|
+
* @module cli/commands/ci
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {runCheck} from './check.mjs';
|
|
11
|
+
import {runTest} from './test.mjs';
|
|
12
|
+
import {runBuild} from './build.mjs';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Run the full CI pipeline
|
|
16
|
+
* @param {object} config - Project configuration from kabran.config.mjs
|
|
17
|
+
* @param {string[]} args - CLI arguments
|
|
18
|
+
* @returns {Promise<number>} Exit code (0 = success, 1 = failure)
|
|
19
|
+
*/
|
|
20
|
+
export async function runCI(config, args) {
|
|
21
|
+
// Get pipeline steps from config or use defaults
|
|
22
|
+
const steps = config.ci?.steps || ['check', 'test:unit', 'build'];
|
|
23
|
+
|
|
24
|
+
console.log('╔' + '═'.repeat(50) + '╗');
|
|
25
|
+
console.log('║' + ' KABRAN CI PIPELINE'.padEnd(50) + '║');
|
|
26
|
+
console.log('╚' + '═'.repeat(50) + '╝');
|
|
27
|
+
console.log();
|
|
28
|
+
console.log(`Steps: ${steps.join(' → ')}`);
|
|
29
|
+
console.log();
|
|
30
|
+
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
const results = [];
|
|
33
|
+
|
|
34
|
+
for (let i = 0; i < steps.length; i++) {
|
|
35
|
+
const step = steps[i];
|
|
36
|
+
const stepNum = i + 1;
|
|
37
|
+
|
|
38
|
+
console.log();
|
|
39
|
+
console.log('┌' + '─'.repeat(50) + '┐');
|
|
40
|
+
console.log(`│ Step ${stepNum}/${steps.length}: ${step.padEnd(42)}│`);
|
|
41
|
+
console.log('└' + '─'.repeat(50) + '┘');
|
|
42
|
+
|
|
43
|
+
const stepStart = Date.now();
|
|
44
|
+
let code = 0;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
if (step === 'check') {
|
|
48
|
+
code = await runCheck(config, []);
|
|
49
|
+
} else if (step.startsWith('test:')) {
|
|
50
|
+
const level = step.replace('test:', '');
|
|
51
|
+
code = await runTest(level, config, []);
|
|
52
|
+
} else if (step === 'test') {
|
|
53
|
+
code = await runTest('all', config, []);
|
|
54
|
+
} else if (step === 'build') {
|
|
55
|
+
code = await runBuild(config, []);
|
|
56
|
+
} else {
|
|
57
|
+
console.error(`Unknown step: ${step}`);
|
|
58
|
+
code = 1;
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(`Step "${step}" threw error: ${error.message}`);
|
|
62
|
+
code = 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const stepDuration = ((Date.now() - stepStart) / 1000).toFixed(2);
|
|
66
|
+
results.push({step, code, duration: stepDuration});
|
|
67
|
+
|
|
68
|
+
if (code !== 0) {
|
|
69
|
+
console.log();
|
|
70
|
+
console.log('╔' + '═'.repeat(50) + '╗');
|
|
71
|
+
console.log('║' + ` PIPELINE FAILED at step: ${step}`.padEnd(50) + '║');
|
|
72
|
+
console.log('╚' + '═'.repeat(50) + '╝');
|
|
73
|
+
console.log();
|
|
74
|
+
printSummary(results, startTime);
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Success
|
|
80
|
+
console.log();
|
|
81
|
+
console.log('╔' + '═'.repeat(50) + '╗');
|
|
82
|
+
console.log('║' + ' PIPELINE COMPLETED SUCCESSFULLY'.padEnd(50) + '║');
|
|
83
|
+
console.log('╚' + '═'.repeat(50) + '╝');
|
|
84
|
+
console.log();
|
|
85
|
+
printSummary(results, startTime);
|
|
86
|
+
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Print pipeline execution summary
|
|
92
|
+
* @param {Array<{step: string, code: number, duration: string}>} results - Step results
|
|
93
|
+
* @param {number} startTime - Pipeline start timestamp
|
|
94
|
+
*/
|
|
95
|
+
function printSummary(results, startTime) {
|
|
96
|
+
const totalDuration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
97
|
+
|
|
98
|
+
console.log('Summary:');
|
|
99
|
+
console.log('─'.repeat(50));
|
|
100
|
+
|
|
101
|
+
for (const {step, code, duration} of results) {
|
|
102
|
+
const status = code === 0 ? '✓ PASS' : '✗ FAIL';
|
|
103
|
+
console.log(` ${status} ${step.padEnd(25)} ${duration}s`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log('─'.repeat(50));
|
|
107
|
+
console.log(` Total time: ${totalDuration}s`);
|
|
108
|
+
console.log();
|
|
109
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Command (L2-L4 of Quality Pyramid)
|
|
3
|
+
*
|
|
4
|
+
* Runs tests at different levels:
|
|
5
|
+
* - L2: Unit tests
|
|
6
|
+
* - L3: Integration tests
|
|
7
|
+
* - L4: E2E tests
|
|
8
|
+
*
|
|
9
|
+
* @module cli/commands/test
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {spawn} from 'node:child_process';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Run a shell command and return exit code
|
|
16
|
+
* @param {string} cmd - Command to run
|
|
17
|
+
* @returns {Promise<number>} Exit code
|
|
18
|
+
*/
|
|
19
|
+
function runCommand(cmd) {
|
|
20
|
+
return new Promise(resolve => {
|
|
21
|
+
const proc = spawn('sh', ['-c', cmd], {stdio: 'inherit'});
|
|
22
|
+
proc.on('close', code => resolve(code ?? 0));
|
|
23
|
+
proc.on('error', () => resolve(1));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get pyramid level number from test type
|
|
29
|
+
* @param {string} level - Test level (unit, integration, e2e)
|
|
30
|
+
* @returns {string} Level number
|
|
31
|
+
*/
|
|
32
|
+
function levelNumber(level) {
|
|
33
|
+
return {unit: '2', integration: '3', e2e: '4'}[level] || '?';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run tests at specified level
|
|
38
|
+
* @param {string} level - Test level (unit, integration, e2e, all)
|
|
39
|
+
* @param {object} config - Project configuration from kabran.config.mjs
|
|
40
|
+
* @param {string[]} args - CLI arguments
|
|
41
|
+
* @returns {Promise<number>} Exit code (0 = success, 1 = failure)
|
|
42
|
+
*/
|
|
43
|
+
export async function runTest(level, config, args) {
|
|
44
|
+
const watch = args.includes('--watch');
|
|
45
|
+
const coverage = args.includes('--coverage');
|
|
46
|
+
|
|
47
|
+
// Run all tests (unit + integration)
|
|
48
|
+
if (level === 'all') {
|
|
49
|
+
console.log('='.repeat(50));
|
|
50
|
+
console.log('Running all tests (L2 + L3)');
|
|
51
|
+
console.log('='.repeat(50));
|
|
52
|
+
|
|
53
|
+
let failed = 0;
|
|
54
|
+
|
|
55
|
+
// Run unit tests
|
|
56
|
+
const unitCode = await runTest('unit', config, args);
|
|
57
|
+
if (unitCode !== 0) failed++;
|
|
58
|
+
|
|
59
|
+
// Run integration tests
|
|
60
|
+
const integrationCode = await runTest('integration', config, args);
|
|
61
|
+
if (integrationCode !== 0) failed++;
|
|
62
|
+
|
|
63
|
+
console.log('\n' + '='.repeat(50));
|
|
64
|
+
if (failed === 0) {
|
|
65
|
+
console.log('All tests: PASSED');
|
|
66
|
+
} else {
|
|
67
|
+
console.log(`All tests: FAILED (${failed} level${failed > 1 ? 's' : ''} failed)`);
|
|
68
|
+
}
|
|
69
|
+
console.log('='.repeat(50));
|
|
70
|
+
|
|
71
|
+
return failed > 0 ? 1 : 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get test configuration for this level
|
|
75
|
+
const testConfig = config.test?.[level];
|
|
76
|
+
|
|
77
|
+
if (!testConfig) {
|
|
78
|
+
console.log(`\n[test:${level}] Skipped (not configured)`);
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log('\n' + '='.repeat(50));
|
|
83
|
+
console.log(`Running L${levelNumber(level)} tests: ${level}`);
|
|
84
|
+
if (testConfig.doppler) {
|
|
85
|
+
console.log('[Doppler] Secrets injection enabled');
|
|
86
|
+
}
|
|
87
|
+
console.log('='.repeat(50));
|
|
88
|
+
|
|
89
|
+
// Setup phase (if defined)
|
|
90
|
+
if (testConfig.setup) {
|
|
91
|
+
console.log(`\n[Setup] ${testConfig.setup}`);
|
|
92
|
+
const setupCode = await runCommand(testConfig.setup);
|
|
93
|
+
if (setupCode !== 0) {
|
|
94
|
+
console.error('Setup failed');
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Build test command with options
|
|
100
|
+
let cmd = testConfig.command;
|
|
101
|
+
|
|
102
|
+
if (watch && !cmd.includes('--watch')) {
|
|
103
|
+
cmd += ' --watch';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (coverage && !cmd.includes('--coverage')) {
|
|
107
|
+
cmd += ' --coverage';
|
|
108
|
+
|
|
109
|
+
// Add coverage threshold if configured
|
|
110
|
+
if (testConfig.coverage?.threshold) {
|
|
111
|
+
cmd += ` --coverage.thresholds.statements=${testConfig.coverage.threshold}`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(`\n[Test] ${cmd}`);
|
|
116
|
+
const testCode = await runCommand(cmd);
|
|
117
|
+
|
|
118
|
+
// Teardown phase (if defined, always run even if tests fail)
|
|
119
|
+
if (testConfig.teardown) {
|
|
120
|
+
console.log(`\n[Teardown] ${testConfig.teardown}`);
|
|
121
|
+
await runCommand(testConfig.teardown);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Result
|
|
125
|
+
console.log('\n' + '-'.repeat(50));
|
|
126
|
+
if (testCode === 0) {
|
|
127
|
+
console.log(`L${levelNumber(level)} tests (${level}): PASSED`);
|
|
128
|
+
} else {
|
|
129
|
+
console.log(`L${levelNumber(level)} tests (${level}): FAILED`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return testCode;
|
|
133
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Kabran CLI - Unified command runner organized by quality pyramid levels
|
|
5
|
+
*
|
|
6
|
+
* L1 (Base): Static analysis - lint, types, format, validators
|
|
7
|
+
* L2: Unit tests
|
|
8
|
+
* L3: Integration tests
|
|
9
|
+
* L4: E2E tests
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* kabran <command> [options]
|
|
13
|
+
*
|
|
14
|
+
* Commands:
|
|
15
|
+
* check Run L1 checks (lint, types, format, validators)
|
|
16
|
+
* test:unit Run L2 unit tests
|
|
17
|
+
* test:integration Run L3 integration tests
|
|
18
|
+
* test:e2e Run L4 E2E tests
|
|
19
|
+
* test Run L2 + L3 tests
|
|
20
|
+
* ci Run full CI pipeline
|
|
21
|
+
* build Run build
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {loadConfig} from '../core/config-loader.mjs';
|
|
25
|
+
import {runCheck} from './commands/check.mjs';
|
|
26
|
+
import {runTest} from './commands/test.mjs';
|
|
27
|
+
import {runCI} from './commands/ci.mjs';
|
|
28
|
+
import {runBuild} from './commands/build.mjs';
|
|
29
|
+
|
|
30
|
+
const VERSION = '1.0.0';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Show help message
|
|
34
|
+
*/
|
|
35
|
+
function showHelp() {
|
|
36
|
+
console.log(`
|
|
37
|
+
kabran - Unified quality CLI for Kabran projects
|
|
38
|
+
|
|
39
|
+
Usage: kabran <command> [options]
|
|
40
|
+
|
|
41
|
+
Commands:
|
|
42
|
+
check Run L1 checks (lint, types, format, validators)
|
|
43
|
+
test:unit Run L2 unit tests
|
|
44
|
+
test:integration Run L3 integration tests
|
|
45
|
+
test:e2e Run L4 E2E tests
|
|
46
|
+
test Run L2 + L3 tests
|
|
47
|
+
ci Run full CI pipeline
|
|
48
|
+
build Run build
|
|
49
|
+
|
|
50
|
+
Options:
|
|
51
|
+
--fix Auto-fix issues (check command)
|
|
52
|
+
--watch Watch mode (test commands)
|
|
53
|
+
--coverage Generate coverage report
|
|
54
|
+
--help, -h Show this help
|
|
55
|
+
--version, -v Show version
|
|
56
|
+
|
|
57
|
+
Quality Pyramid:
|
|
58
|
+
/\\
|
|
59
|
+
/ \\
|
|
60
|
+
/ L4 \\ kabran test:e2e
|
|
61
|
+
/ \\
|
|
62
|
+
/__________\\
|
|
63
|
+
/ \\
|
|
64
|
+
/ L3 \\ kabran test:integration
|
|
65
|
+
/ \\
|
|
66
|
+
/__________________\\
|
|
67
|
+
/ \\
|
|
68
|
+
/ L2 \\ kabran test:unit
|
|
69
|
+
/ \\
|
|
70
|
+
/__________________________\\
|
|
71
|
+
/ \\
|
|
72
|
+
/ L1 \\ kabran check
|
|
73
|
+
/ \\
|
|
74
|
+
/__________________________________\\
|
|
75
|
+
|
|
76
|
+
Examples:
|
|
77
|
+
kabran check # Run lint, types, format, validators
|
|
78
|
+
kabran check --fix # Run checks with auto-fix
|
|
79
|
+
kabran test:unit # Run unit tests
|
|
80
|
+
kabran test --coverage # Run all tests with coverage
|
|
81
|
+
kabran ci # Run full CI pipeline
|
|
82
|
+
`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Show version
|
|
87
|
+
*/
|
|
88
|
+
function showVersion() {
|
|
89
|
+
console.log(`kabran v${VERSION}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Main CLI entry point
|
|
94
|
+
*/
|
|
95
|
+
async function main() {
|
|
96
|
+
const [command, ...args] = process.argv.slice(2);
|
|
97
|
+
|
|
98
|
+
// Handle help and version flags
|
|
99
|
+
if (!command || command === '--help' || command === '-h') {
|
|
100
|
+
showHelp();
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (command === '--version' || command === '-v') {
|
|
105
|
+
showVersion();
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Load project configuration
|
|
110
|
+
let config;
|
|
111
|
+
try {
|
|
112
|
+
config = await loadConfig();
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error(`Failed to load config: ${error.message}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Command routing
|
|
119
|
+
const commands = {
|
|
120
|
+
check: () => runCheck(config, args),
|
|
121
|
+
'test:unit': () => runTest('unit', config, args),
|
|
122
|
+
'test:integration': () => runTest('integration', config, args),
|
|
123
|
+
'test:e2e': () => runTest('e2e', config, args),
|
|
124
|
+
test: () => runTest('all', config, args),
|
|
125
|
+
ci: () => runCI(config, args),
|
|
126
|
+
build: () => runBuild(config, args),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (commands[command]) {
|
|
130
|
+
try {
|
|
131
|
+
const exitCode = await commands[command]();
|
|
132
|
+
process.exit(exitCode);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error(`Command failed: ${error.message}`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
console.error(`Unknown command: ${command}`);
|
|
139
|
+
console.error('Run "kabran --help" for available commands.');
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
main();
|