@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kabran-tecnologia/kabran-config",
3
- "version": "1.10.0",
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();