timonel 2.12.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 +6 -0
- package/README.md +2 -0
- package/dist/cli.js +64 -20
- package/dist/lib/templates/umbrella-chart.js +136 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
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
|
+
|
|
1
7
|
# [2.12.0](https://github.com/KenkoGeek/timonel/compare/v2.11.0...v2.12.0) (2025-09-28)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -137,6 +137,8 @@ export const umbrella = new UmbrellaChartTemplate(umbrellaConfig);
|
|
|
137
137
|
patterns and practices
|
|
138
138
|
- **[Contributing](https://github.com/KenkoGeek/timonel/wiki/Contributing)** - Development setup
|
|
139
139
|
and guidelines
|
|
140
|
+
- **[Timonel Examples Repository](https://github.com/KenkoGeek/timonel-examples)** - Curated
|
|
141
|
+
collection of ready-to-run Timonel sample projects
|
|
140
142
|
|
|
141
143
|
## 🔧 Troubleshooting
|
|
142
144
|
|
package/dist/cli.js
CHANGED
|
@@ -64,6 +64,7 @@ function usageAndExit(msg, silent = false) {
|
|
|
64
64
|
' --silent Suppress output (useful for CI)',
|
|
65
65
|
' --env <environment> Use environment-specific values',
|
|
66
66
|
' --set <key=value> Override values (can be used multiple times)',
|
|
67
|
+
' --mode <dependencies|inline> Umbrella synth mode (default: dependencies)',
|
|
67
68
|
' --help, -h Show this help message',
|
|
68
69
|
'',
|
|
69
70
|
'Examples:',
|
|
@@ -234,18 +235,22 @@ async function cmdTemplates(flags) {
|
|
|
234
235
|
});
|
|
235
236
|
}
|
|
236
237
|
}
|
|
238
|
+
const UMBRELLA_SYNTH_MODES = ['dependencies', 'inline'];
|
|
237
239
|
async function cmdUmbrella(subcommand, args, flags) {
|
|
238
240
|
if (!subcommand)
|
|
239
241
|
usageAndExit('Missing umbrella subcommand');
|
|
242
|
+
const workingArgs = [...(args ?? [])];
|
|
243
|
+
const subcommandFlags = parseFlags(workingArgs);
|
|
244
|
+
const mergedFlags = mergeCliFlags(flags, subcommandFlags);
|
|
240
245
|
switch (subcommand) {
|
|
241
246
|
case 'init':
|
|
242
|
-
await cmdUmbrellaInit(
|
|
247
|
+
await cmdUmbrellaInit(workingArgs[0], mergedFlags.silent);
|
|
243
248
|
break;
|
|
244
249
|
case 'add':
|
|
245
|
-
await cmdUmbrellaAdd(
|
|
250
|
+
await cmdUmbrellaAdd(workingArgs[0], mergedFlags.silent);
|
|
246
251
|
break;
|
|
247
252
|
case 'synth':
|
|
248
|
-
await cmdUmbrellaSynth(
|
|
253
|
+
await cmdUmbrellaSynth(workingArgs[0], mergedFlags);
|
|
249
254
|
break;
|
|
250
255
|
default:
|
|
251
256
|
usageAndExit(`Unknown umbrella subcommand: ${subcommand}`);
|
|
@@ -304,28 +309,54 @@ function addImportStatement(content, importStatement) {
|
|
|
304
309
|
return lines.join('\n');
|
|
305
310
|
}
|
|
306
311
|
function buildSubchartsContent(subchartsContent, subchartEntry) {
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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`;
|
|
315
326
|
}
|
|
316
327
|
function addSubchartToArray(content, chartName, camelCaseName) {
|
|
317
|
-
const subchartsRegex = /
|
|
328
|
+
const subchartsRegex = /(const SUBCHARTS[\s\S]*?=\s*\[)([\s\S]*?)(\];)/;
|
|
318
329
|
const match = content.match(subchartsRegex);
|
|
319
330
|
if (!match) {
|
|
320
331
|
return content;
|
|
321
332
|
}
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
333
|
+
const [, prefix, body, suffix] = match;
|
|
334
|
+
if (body?.includes(`name: '${chartName}'`)) {
|
|
324
335
|
return content;
|
|
325
336
|
}
|
|
326
|
-
const subchartEntry = `{ name: '${chartName}',
|
|
327
|
-
const
|
|
328
|
-
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;
|
|
329
360
|
}
|
|
330
361
|
function updateUmbrellaTs(subchartPath, chartName) {
|
|
331
362
|
const umbrellaFile = path.join(process.cwd(), UMBRELLA_FILE_NAME);
|
|
@@ -391,9 +422,13 @@ async function cmdUmbrellaSynth(outDir, flags) {
|
|
|
391
422
|
process.exit(1);
|
|
392
423
|
}
|
|
393
424
|
const resolvedOutDir = outDir ? path.resolve(outDir) : defaultOutDir;
|
|
394
|
-
|
|
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);
|
|
395
430
|
}
|
|
396
|
-
async function executeTypeScriptUmbrella(resolvedPath, outDir, flags) {
|
|
431
|
+
async function executeTypeScriptUmbrella(resolvedPath, outDir, mode, flags) {
|
|
397
432
|
const wrapperScript = `
|
|
398
433
|
import { pathToFileURL } from 'url';
|
|
399
434
|
|
|
@@ -405,9 +440,10 @@ if (typeof runner !== 'function') {
|
|
|
405
440
|
}
|
|
406
441
|
|
|
407
442
|
const output = '${outDir}';
|
|
443
|
+
const synthOptions = { mode: '${mode}' };
|
|
408
444
|
const fs = await import('fs');
|
|
409
445
|
fs.mkdirSync(output, { recursive: true });
|
|
410
|
-
await Promise.resolve(runner(output));
|
|
446
|
+
await Promise.resolve(runner(output, synthOptions));
|
|
411
447
|
console.log('Umbrella chart written to ' + output);
|
|
412
448
|
`;
|
|
413
449
|
const wrapperFile = path.join(process.cwd(), '.timonel-umbrella-wrapper.mjs');
|
|
@@ -455,6 +491,14 @@ function parseFlags(args) {
|
|
|
455
491
|
}
|
|
456
492
|
break;
|
|
457
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
|
+
}
|
|
458
502
|
case '--help':
|
|
459
503
|
case '-h':
|
|
460
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
|