timonel 2.11.0 → 2.12.1
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/CHANGELOG.md +17 -0
- package/README.md +5 -45
- package/dist/cli.js +121 -52
- package/dist/lib/templates/umbrella-chart.js +136 -20
- package/dist/lib/utils/helmYamlSerializer.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## [2.12.1](https://github.com/KenkoGeek/timonel/compare/v2.12.0...v2.12.1) (2025-09-28)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- **cli:** new feature for cli ([a8c5326](https://github.com/KenkoGeek/timonel/commit/a8c532617616bfedada20d28bcc4fe94ea165321))
|
|
6
|
+
|
|
7
|
+
# [2.12.0](https://github.com/KenkoGeek/timonel/compare/v2.11.0...v2.12.0) (2025-09-28)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **cli:** harden flag handling and path validation ([#130](https://github.com/KenkoGeek/timonel/issues/130)) ([2186d88](https://github.com/KenkoGeek/timonel/commit/2186d886428752025e7bf531c4a6541dade683a6))
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
- **docs:** update references and CLI [skip ci] ([23704d3](https://github.com/KenkoGeek/timonel/commit/23704d3164cfbb92d183af40c70e27a32b566d06))
|
|
16
|
+
- **docs:** update references and CLI [skip ci] ([be4bcdc](https://github.com/KenkoGeek/timonel/commit/be4bcdca62cb3a9339e5ad70efc0ba31a6d9d5be))
|
|
17
|
+
|
|
1
18
|
# [2.11.0](https://github.com/KenkoGeek/timonel/compare/v2.10.2...v2.11.0) (2025-09-26)
|
|
2
19
|
|
|
3
20
|
### Features
|
package/README.md
CHANGED
|
@@ -38,9 +38,6 @@ npm install -g timonel
|
|
|
38
38
|
|
|
39
39
|
# Or use with pnpm
|
|
40
40
|
pnpm add -g timonel
|
|
41
|
-
|
|
42
|
-
# Verify installation
|
|
43
|
-
tl --version
|
|
44
41
|
```
|
|
45
42
|
|
|
46
43
|
### Basic Usage
|
|
@@ -140,6 +137,8 @@ export const umbrella = new UmbrellaChartTemplate(umbrellaConfig);
|
|
|
140
137
|
patterns and practices
|
|
141
138
|
- **[Contributing](https://github.com/KenkoGeek/timonel/wiki/Contributing)** - Development setup
|
|
142
139
|
and guidelines
|
|
140
|
+
- **[Timonel Examples Repository](https://github.com/KenkoGeek/timonel-examples)** - Curated
|
|
141
|
+
collection of ready-to-run Timonel sample projects
|
|
143
142
|
|
|
144
143
|
## 🔧 Troubleshooting
|
|
145
144
|
|
|
@@ -157,10 +156,10 @@ If you get `Error: Cannot find module 'cdk8s'` when running `tl umbrella synth`:
|
|
|
157
156
|
"version": "1.0.0",
|
|
158
157
|
"type": "module",
|
|
159
158
|
"dependencies": {
|
|
160
|
-
"cdk8s": "^2.70.
|
|
161
|
-
"cdk8s-plus-33": "^2.3.
|
|
159
|
+
"cdk8s": "^2.70.16",
|
|
160
|
+
"cdk8s-plus-33": "^2.3.8",
|
|
162
161
|
"constructs": "^10.4.2",
|
|
163
|
-
"timonel": "^2.
|
|
162
|
+
"timonel": "^2.11.0"
|
|
164
163
|
},
|
|
165
164
|
"devDependencies": {
|
|
166
165
|
"@types/node": "^24.5.2",
|
|
@@ -176,45 +175,6 @@ npm install
|
|
|
176
175
|
tl umbrella synth # Now it works!
|
|
177
176
|
```
|
|
178
177
|
|
|
179
|
-
### Version Display Issues
|
|
180
|
-
|
|
181
|
-
If the CLI shows an incorrect version:
|
|
182
|
-
|
|
183
|
-
**Problem**: The CLI might be using cached or hardcoded version information.
|
|
184
|
-
|
|
185
|
-
**Solution**: Rebuild the project:
|
|
186
|
-
|
|
187
|
-
```bash
|
|
188
|
-
# In the Timonel project directory
|
|
189
|
-
pnpm build
|
|
190
|
-
tl --version # Should show correct version
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### TypeScript Compilation Errors
|
|
194
|
-
|
|
195
|
-
If you encounter TypeScript errors when using umbrella charts:
|
|
196
|
-
|
|
197
|
-
**Problem**: Type mismatches in subchart configurations.
|
|
198
|
-
|
|
199
|
-
**Solution**: Ensure proper type casting for version and description fields:
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
// ✅ Correct
|
|
203
|
-
const subchart = {
|
|
204
|
-
name: 'my-service',
|
|
205
|
-
version: '1.0.0' as string,
|
|
206
|
-
description: 'My service' as string,
|
|
207
|
-
chart: myChartFunction,
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
// ❌ Incorrect
|
|
211
|
-
const subchart = {
|
|
212
|
-
name: 'my-service',
|
|
213
|
-
version: someUnknownValue, // This will cause TypeScript errors
|
|
214
|
-
chart: myChartFunction,
|
|
215
|
-
};
|
|
216
|
-
```
|
|
217
|
-
|
|
218
178
|
## 🤝 Contributing
|
|
219
179
|
|
|
220
180
|
See our [Contributing Guide](https://github.com/KenkoGeek/timonel/wiki/Contributing) for development
|
package/dist/cli.js
CHANGED
|
@@ -12,37 +12,20 @@ const UMBRELLA_FILE_NAME = 'umbrella.ts';
|
|
|
12
12
|
const PACKAGE_JSON_FILE = 'package.json';
|
|
13
13
|
function getVersion() {
|
|
14
14
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
for (const packagePath of possiblePaths) {
|
|
22
|
-
try {
|
|
23
|
-
const validatedPath = SecurityUtils.validatePath(packagePath, process.cwd());
|
|
24
|
-
if (fs.existsSync(validatedPath)) {
|
|
25
|
-
try {
|
|
26
|
-
const packageJson = JSON.parse(fs.readFileSync(validatedPath, 'utf8'));
|
|
27
|
-
return packageJson.version || 'unknown';
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
15
|
+
const packagePath = path.resolve(__dirname, '..', PACKAGE_JSON_FILE);
|
|
16
|
+
const allowedBase = path.resolve(__dirname, '..');
|
|
17
|
+
const validatedPath = SecurityUtils.validatePath(packagePath, allowedBase);
|
|
18
|
+
const packageJson = JSON.parse(fs.readFileSync(validatedPath, 'utf8'));
|
|
19
|
+
if (packageJson.version) {
|
|
20
|
+
return packageJson.version;
|
|
37
21
|
}
|
|
38
|
-
if (process.env.npm_package_version) {
|
|
39
|
-
return process.env.npm_package_version;
|
|
40
|
-
}
|
|
41
|
-
return 'unknown';
|
|
42
22
|
}
|
|
43
23
|
catch {
|
|
44
|
-
return 'unknown';
|
|
45
24
|
}
|
|
25
|
+
if (process.env.npm_package_version) {
|
|
26
|
+
return process.env.npm_package_version;
|
|
27
|
+
}
|
|
28
|
+
return 'unknown';
|
|
46
29
|
}
|
|
47
30
|
const cliLogger = createLogger('cli');
|
|
48
31
|
function log(msg, silent = false) {
|
|
@@ -81,6 +64,7 @@ function usageAndExit(msg, silent = false) {
|
|
|
81
64
|
' --silent Suppress output (useful for CI)',
|
|
82
65
|
' --env <environment> Use environment-specific values',
|
|
83
66
|
' --set <key=value> Override values (can be used multiple times)',
|
|
67
|
+
' --mode <dependencies|inline> Umbrella synth mode (default: dependencies)',
|
|
84
68
|
' --help, -h Show this help message',
|
|
85
69
|
'',
|
|
86
70
|
'Examples:',
|
|
@@ -162,7 +146,23 @@ await import(pathToFileURL('${tempChartFile}').href);
|
|
|
162
146
|
}
|
|
163
147
|
}
|
|
164
148
|
async function cmdValidate(flags) {
|
|
165
|
-
const
|
|
149
|
+
const lintArgs = ['lint', '.'];
|
|
150
|
+
if (flags?.env) {
|
|
151
|
+
try {
|
|
152
|
+
const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(flags.env);
|
|
153
|
+
lintArgs.push('-f', `values-${sanitizedEnv}.yaml`);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.error(SecurityUtils.sanitizeLogMessage(error.message));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (flags?.set) {
|
|
161
|
+
for (const setValue of flags.set) {
|
|
162
|
+
lintArgs.push('--set', setValue);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const result = spawnSync('helm', lintArgs, {
|
|
166
166
|
stdio: flags?.silent ? 'pipe' : 'inherit',
|
|
167
167
|
encoding: 'utf8',
|
|
168
168
|
});
|
|
@@ -180,6 +180,21 @@ async function cmdDeploy(release, namespace, flags) {
|
|
|
180
180
|
if (namespace) {
|
|
181
181
|
args.push('--namespace', namespace);
|
|
182
182
|
}
|
|
183
|
+
if (flags?.env) {
|
|
184
|
+
try {
|
|
185
|
+
const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(flags.env);
|
|
186
|
+
args.push('-f', `values-${sanitizedEnv}.yaml`);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
console.error(SecurityUtils.sanitizeLogMessage(error.message));
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (flags?.set) {
|
|
194
|
+
for (const setValue of flags.set) {
|
|
195
|
+
args.push('--set', setValue);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
183
198
|
const result = spawnSync('helm', args.filter((arg) => Boolean(arg)), {
|
|
184
199
|
stdio: flags?.silent ? 'pipe' : 'inherit',
|
|
185
200
|
encoding: 'utf8',
|
|
@@ -220,18 +235,22 @@ async function cmdTemplates(flags) {
|
|
|
220
235
|
});
|
|
221
236
|
}
|
|
222
237
|
}
|
|
238
|
+
const UMBRELLA_SYNTH_MODES = ['dependencies', 'inline'];
|
|
223
239
|
async function cmdUmbrella(subcommand, args, flags) {
|
|
224
240
|
if (!subcommand)
|
|
225
241
|
usageAndExit('Missing umbrella subcommand');
|
|
242
|
+
const workingArgs = [...(args ?? [])];
|
|
243
|
+
const subcommandFlags = parseFlags(workingArgs);
|
|
244
|
+
const mergedFlags = mergeCliFlags(flags, subcommandFlags);
|
|
226
245
|
switch (subcommand) {
|
|
227
246
|
case 'init':
|
|
228
|
-
await cmdUmbrellaInit(
|
|
247
|
+
await cmdUmbrellaInit(workingArgs[0], mergedFlags.silent);
|
|
229
248
|
break;
|
|
230
249
|
case 'add':
|
|
231
|
-
await cmdUmbrellaAdd(
|
|
250
|
+
await cmdUmbrellaAdd(workingArgs[0], mergedFlags.silent);
|
|
232
251
|
break;
|
|
233
252
|
case 'synth':
|
|
234
|
-
await cmdUmbrellaSynth(
|
|
253
|
+
await cmdUmbrellaSynth(workingArgs[0], mergedFlags);
|
|
235
254
|
break;
|
|
236
255
|
default:
|
|
237
256
|
usageAndExit(`Unknown umbrella subcommand: ${subcommand}`);
|
|
@@ -290,28 +309,54 @@ function addImportStatement(content, importStatement) {
|
|
|
290
309
|
return lines.join('\n');
|
|
291
310
|
}
|
|
292
311
|
function buildSubchartsContent(subchartsContent, subchartEntry) {
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
312
|
+
const existingEntries = (subchartsContent || '')
|
|
313
|
+
.split('\n')
|
|
314
|
+
.map((line) => line.trim())
|
|
315
|
+
.filter((line) => line && !line.startsWith('//'))
|
|
316
|
+
.map((line) => line.replace(/,$/, ''));
|
|
317
|
+
if (existingEntries.includes(subchartEntry)) {
|
|
318
|
+
return subchartsContent ?? '';
|
|
319
|
+
}
|
|
320
|
+
const entries = [...existingEntries, subchartEntry];
|
|
321
|
+
const lines = [' // Add your subcharts here:'];
|
|
322
|
+
for (const entry of entries) {
|
|
323
|
+
lines.push(` ${entry},`);
|
|
324
|
+
}
|
|
325
|
+
return `\n${lines.join('\n')}\n`;
|
|
301
326
|
}
|
|
302
327
|
function addSubchartToArray(content, chartName, camelCaseName) {
|
|
303
|
-
const subchartsRegex = /
|
|
328
|
+
const subchartsRegex = /(const SUBCHARTS[\s\S]*?=\s*\[)([\s\S]*?)(\];)/;
|
|
304
329
|
const match = content.match(subchartsRegex);
|
|
305
330
|
if (!match) {
|
|
306
331
|
return content;
|
|
307
332
|
}
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
333
|
+
const [, prefix, body, suffix] = match;
|
|
334
|
+
if (body?.includes(`name: '${chartName}'`)) {
|
|
310
335
|
return content;
|
|
311
336
|
}
|
|
312
|
-
const subchartEntry = `{ name: '${chartName}',
|
|
313
|
-
const
|
|
314
|
-
return content.replace(subchartsRegex,
|
|
337
|
+
const subchartEntry = `{ name: '${chartName}', factory: ${camelCaseName} }`;
|
|
338
|
+
const newBody = buildSubchartsContent(body, subchartEntry);
|
|
339
|
+
return content.replace(subchartsRegex, `${prefix}${newBody}${suffix}`);
|
|
340
|
+
}
|
|
341
|
+
function mergeCliFlags(base, override) {
|
|
342
|
+
const merged = { ...(base || {}) };
|
|
343
|
+
if (!override) {
|
|
344
|
+
return merged;
|
|
345
|
+
}
|
|
346
|
+
if (override.dryRun !== undefined)
|
|
347
|
+
merged.dryRun = override.dryRun;
|
|
348
|
+
if (override.silent !== undefined)
|
|
349
|
+
merged.silent = override.silent;
|
|
350
|
+
if (override.env !== undefined)
|
|
351
|
+
merged.env = override.env;
|
|
352
|
+
if (override.mode !== undefined)
|
|
353
|
+
merged.mode = override.mode;
|
|
354
|
+
const baseSet = base?.set ?? [];
|
|
355
|
+
const overrideSet = override.set ?? [];
|
|
356
|
+
if (baseSet.length || overrideSet.length) {
|
|
357
|
+
merged.set = [...baseSet, ...overrideSet];
|
|
358
|
+
}
|
|
359
|
+
return merged;
|
|
315
360
|
}
|
|
316
361
|
function updateUmbrellaTs(subchartPath, chartName) {
|
|
317
362
|
const umbrellaFile = path.join(process.cwd(), UMBRELLA_FILE_NAME);
|
|
@@ -343,8 +388,19 @@ async function cmdUmbrellaAdd(subchartPath, silent = false) {
|
|
|
343
388
|
}
|
|
344
389
|
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
345
390
|
const subchartName = path.basename(validSubchartPath);
|
|
346
|
-
const
|
|
391
|
+
const chartsRoot = path.join(process.cwd(), 'charts');
|
|
392
|
+
const targetSubchartPath = path.join(chartsRoot, validSubchartPath);
|
|
393
|
+
let subchartDir;
|
|
394
|
+
try {
|
|
395
|
+
subchartDir = SecurityUtils.validatePath(targetSubchartPath, chartsRoot);
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
console.error(SecurityUtils.sanitizeLogMessage(error.message));
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
347
401
|
fs.mkdirSync(subchartDir, { recursive: true });
|
|
402
|
+
const relativeSubchartPath = path.relative(chartsRoot, subchartDir) || subchartName;
|
|
403
|
+
const normalizedSubchartPath = relativeSubchartPath.split(path.sep).join('/');
|
|
348
404
|
const chartFile = path.join(subchartDir, 'chart.ts');
|
|
349
405
|
const { generateFlexibleSubchartTemplate } = await import('./lib/templates/flexible-subchart.js');
|
|
350
406
|
const subchartContent = generateFlexibleSubchartTemplate(subchartName);
|
|
@@ -352,11 +408,11 @@ async function cmdUmbrellaAdd(subchartPath, silent = false) {
|
|
|
352
408
|
config.subcharts.push({
|
|
353
409
|
name: subchartName,
|
|
354
410
|
version: getVersion(),
|
|
355
|
-
path: `./charts/${
|
|
411
|
+
path: `./charts/${normalizedSubchartPath}/chart.ts`,
|
|
356
412
|
});
|
|
357
413
|
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
358
|
-
updateUmbrellaTs(
|
|
359
|
-
log(`Subchart ${subchartName} added to umbrella at path '${
|
|
414
|
+
updateUmbrellaTs(normalizedSubchartPath, subchartName);
|
|
415
|
+
log(`Subchart ${subchartName} added to umbrella at path '${normalizedSubchartPath}'`, silent);
|
|
360
416
|
}
|
|
361
417
|
async function cmdUmbrellaSynth(outDir, flags) {
|
|
362
418
|
const umbrellaFile = path.join(process.cwd(), UMBRELLA_FILE_NAME);
|
|
@@ -366,9 +422,13 @@ async function cmdUmbrellaSynth(outDir, flags) {
|
|
|
366
422
|
process.exit(1);
|
|
367
423
|
}
|
|
368
424
|
const resolvedOutDir = outDir ? path.resolve(outDir) : defaultOutDir;
|
|
369
|
-
|
|
425
|
+
const synthMode = flags?.mode ?? 'dependencies';
|
|
426
|
+
if (!UMBRELLA_SYNTH_MODES.includes(synthMode)) {
|
|
427
|
+
usageAndExit('Invalid mode. Use "dependencies" or "inline".', flags?.silent);
|
|
428
|
+
}
|
|
429
|
+
await executeTypeScriptUmbrella(umbrellaFile, resolvedOutDir, synthMode, flags);
|
|
370
430
|
}
|
|
371
|
-
async function executeTypeScriptUmbrella(resolvedPath, outDir, flags) {
|
|
431
|
+
async function executeTypeScriptUmbrella(resolvedPath, outDir, mode, flags) {
|
|
372
432
|
const wrapperScript = `
|
|
373
433
|
import { pathToFileURL } from 'url';
|
|
374
434
|
|
|
@@ -380,9 +440,10 @@ if (typeof runner !== 'function') {
|
|
|
380
440
|
}
|
|
381
441
|
|
|
382
442
|
const output = '${outDir}';
|
|
443
|
+
const synthOptions = { mode: '${mode}' };
|
|
383
444
|
const fs = await import('fs');
|
|
384
445
|
fs.mkdirSync(output, { recursive: true });
|
|
385
|
-
await Promise.resolve(runner(output));
|
|
446
|
+
await Promise.resolve(runner(output, synthOptions));
|
|
386
447
|
console.log('Umbrella chart written to ' + output);
|
|
387
448
|
`;
|
|
388
449
|
const wrapperFile = path.join(process.cwd(), '.timonel-umbrella-wrapper.mjs');
|
|
@@ -430,6 +491,14 @@ function parseFlags(args) {
|
|
|
430
491
|
}
|
|
431
492
|
break;
|
|
432
493
|
}
|
|
494
|
+
case '--mode': {
|
|
495
|
+
const modeValue = args.shift();
|
|
496
|
+
if (!modeValue || !UMBRELLA_SYNTH_MODES.includes(modeValue)) {
|
|
497
|
+
usageAndExit('Invalid mode. Use "dependencies" or "inline".', flags.silent);
|
|
498
|
+
}
|
|
499
|
+
flags.mode = modeValue;
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
433
502
|
case '--help':
|
|
434
503
|
case '-h':
|
|
435
504
|
usageAndExit(undefined, flags.silent);
|
|
@@ -7,36 +7,152 @@ import { generateHelpersTemplate } from '../utils/helmHelpers.js';
|
|
|
7
7
|
import { createFlexibleSubchart } from './flexible-subchart.js';
|
|
8
8
|
export function generateUmbrellaChart(name) {
|
|
9
9
|
return `import { App } from 'cdk8s';
|
|
10
|
-
import {
|
|
10
|
+
import { Rutter } from 'timonel';
|
|
11
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import * as jsYaml from 'js-yaml';
|
|
11
14
|
// Import subcharts - add your subchart imports here
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
type SynthMode = 'dependencies' | 'inline';
|
|
17
|
+
|
|
18
|
+
interface SynthOptions {
|
|
19
|
+
mode?: SynthMode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const DEFAULT_MODE: SynthMode = 'dependencies';
|
|
23
|
+
|
|
24
|
+
const SUBCHARTS: Array<{ name: string; factory: () => Rutter }> = [
|
|
25
|
+
// Add your subcharts here:
|
|
26
|
+
// { name: 'my-subchart', factory: mySubchart },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
function resolveMode(options?: SynthOptions): SynthMode {
|
|
30
|
+
const explicit = options?.mode;
|
|
31
|
+
if (explicit === 'dependencies' || explicit === 'inline') {
|
|
32
|
+
return explicit;
|
|
33
|
+
}
|
|
34
|
+
const envMode = process.env.TIMONEL_UMBRELLA_MODE;
|
|
35
|
+
if (envMode === 'dependencies' || envMode === 'inline') {
|
|
36
|
+
return envMode;
|
|
37
|
+
}
|
|
38
|
+
return DEFAULT_MODE;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readYamlFile(filePath: string): Record<string, unknown> {
|
|
42
|
+
if (!existsSync(filePath)) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
const content = jsYaml.load(readFileSync(filePath, 'utf8'));
|
|
46
|
+
return content && typeof content === 'object' ? (content as Record<string, unknown>) : {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function synth(outDir: string, options?: SynthOptions) {
|
|
50
|
+
const mode = resolveMode(options);
|
|
19
51
|
const app = new App({
|
|
20
52
|
outdir: outDir,
|
|
21
53
|
outputFileExtension: '.yaml',
|
|
22
|
-
yamlOutputType: 'FILE_PER_RESOURCE'
|
|
54
|
+
yamlOutputType: 'FILE_PER_RESOURCE',
|
|
23
55
|
});
|
|
24
56
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
57
|
+
const umbrella = new Rutter({
|
|
58
|
+
meta: {
|
|
59
|
+
name: '${name}',
|
|
60
|
+
version: '0.1.0',
|
|
61
|
+
description: '${name} umbrella chart',
|
|
62
|
+
appVersion: '1.0.0',
|
|
63
|
+
type: 'application',
|
|
64
|
+
},
|
|
65
|
+
scope: app,
|
|
66
|
+
defaultValues: {
|
|
67
|
+
namespace: 'default',
|
|
68
|
+
createNamespace: false,
|
|
69
|
+
},
|
|
33
70
|
});
|
|
34
71
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
72
|
+
umbrella.addConditionalManifest(
|
|
73
|
+
{
|
|
74
|
+
apiVersion: 'v1',
|
|
75
|
+
kind: 'Namespace',
|
|
76
|
+
metadata: {
|
|
77
|
+
name: '{{ .Values.namespace | default .Release.Namespace }}',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
'createNamespace',
|
|
81
|
+
'namespace',
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
umbrella.write(outDir);
|
|
85
|
+
|
|
86
|
+
const chartPath = join(outDir, 'Chart.yaml');
|
|
87
|
+
const valuesPath = join(outDir, 'values.yaml');
|
|
88
|
+
const templatesDir = join(outDir, 'templates');
|
|
89
|
+
mkdirSync(templatesDir, { recursive: true });
|
|
90
|
+
|
|
91
|
+
const chartDoc = readYamlFile(chartPath);
|
|
92
|
+
const valuesDoc = readYamlFile(valuesPath);
|
|
93
|
+
|
|
94
|
+
if (mode === 'dependencies') {
|
|
95
|
+
const chartsDir = join(outDir, 'charts');
|
|
96
|
+
mkdirSync(chartsDir, { recursive: true });
|
|
97
|
+
|
|
98
|
+
const dependencies: Array<{ name: string; version: string; repository: string }> = [];
|
|
99
|
+
|
|
100
|
+
SUBCHARTS.forEach((subchart) => {
|
|
101
|
+
const instance = subchart.factory();
|
|
102
|
+
const targetDir = join(chartsDir, subchart.name);
|
|
103
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
104
|
+
instance.write(targetDir);
|
|
105
|
+
const meta = instance.getMeta();
|
|
106
|
+
const version = meta.version ?? '0.1.0';
|
|
107
|
+
dependencies.push({
|
|
108
|
+
name: subchart.name,
|
|
109
|
+
version,
|
|
110
|
+
repository: 'file://./charts/' + subchart.name,
|
|
111
|
+
});
|
|
112
|
+
const subchartValues = readYamlFile(join(targetDir, 'values.yaml'));
|
|
113
|
+
if (Object.keys(subchartValues).length > 0) {
|
|
114
|
+
valuesDoc[subchart.name] = subchartValues;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
chartDoc.dependencies = dependencies;
|
|
119
|
+
} else {
|
|
120
|
+
const chartsDir = join(outDir, 'charts');
|
|
121
|
+
if (existsSync(chartsDir)) {
|
|
122
|
+
rmSync(chartsDir, { recursive: true, force: true });
|
|
123
|
+
}
|
|
124
|
+
delete chartDoc.dependencies;
|
|
125
|
+
|
|
126
|
+
SUBCHARTS.forEach((subchart) => {
|
|
127
|
+
const instance = subchart.factory();
|
|
128
|
+
const tempDir = join(outDir, '.timonel-inline-' + subchart.name);
|
|
129
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
130
|
+
instance.write(tempDir);
|
|
131
|
+
|
|
132
|
+
const subTemplatesDir = join(tempDir, 'templates');
|
|
133
|
+
if (existsSync(subTemplatesDir)) {
|
|
134
|
+
const targetTemplatesDir = join(templatesDir, subchart.name);
|
|
135
|
+
mkdirSync(targetTemplatesDir, { recursive: true });
|
|
136
|
+
for (const file of readdirSync(subTemplatesDir)) {
|
|
137
|
+
copyFileSync(join(subTemplatesDir, file), join(targetTemplatesDir, file));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const subchartValues = readYamlFile(join(tempDir, 'values.yaml'));
|
|
142
|
+
if (Object.keys(subchartValues).length > 0) {
|
|
143
|
+
valuesDoc[subchart.name] = subchartValues;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
writeFileSync(chartPath, jsYaml.dump(chartDoc));
|
|
151
|
+
writeFileSync(valuesPath, jsYaml.dump(valuesDoc));
|
|
152
|
+
|
|
39
153
|
app.synth();
|
|
154
|
+
|
|
155
|
+
console.log('✅ Umbrella chart generated in ' + mode + ' mode!');
|
|
40
156
|
}
|
|
41
157
|
|
|
42
158
|
// Auto-execute when run directly
|
|
@@ -217,7 +217,7 @@ function isQuotesBalanced(str) {
|
|
|
217
217
|
function validateFunctionCalls(_content) {
|
|
218
218
|
}
|
|
219
219
|
function checkCommonIssues(yaml, warnings) {
|
|
220
|
-
const deprecatedFunctions = ['template'
|
|
220
|
+
const deprecatedFunctions = ['template'];
|
|
221
221
|
for (const func of deprecatedFunctions) {
|
|
222
222
|
const pattern = new RegExp(`\\{\\{[^}]*\\b${func}\\b[^}]*\\}\\}`, 'g');
|
|
223
223
|
let match;
|
|
@@ -232,7 +232,7 @@ function checkCommonIssues(yaml, warnings) {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
function checkQuotedExpressions(yaml, warnings) {
|
|
235
|
-
const quotedPatterns = [/'\
|
|
235
|
+
const quotedPatterns = [/'(\{\{[^}]+\}\})'/g, /"(\{\{[^}]+\}\})"/g];
|
|
236
236
|
for (const pattern of quotedPatterns) {
|
|
237
237
|
let match;
|
|
238
238
|
pattern.lastIndex = 0;
|