timonel 2.8.3 → 2.9.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 +13 -0
- package/README.md +35 -0
- package/dist/cli.js +255 -680
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/lib/helmChartWriter.js +26 -4
- package/dist/lib/rutter.d.ts +28 -16
- package/dist/lib/rutter.js +250 -48
- package/dist/lib/templates/basic-chart.d.ts +19 -0
- package/dist/lib/templates/basic-chart.js +189 -0
- package/dist/lib/templates/subchart.d.ts +27 -0
- package/dist/lib/templates/subchart.js +221 -0
- package/dist/lib/templates/umbrella-chart.d.ts +13 -0
- package/dist/lib/templates/umbrella-chart.js +222 -0
- package/dist/lib/types.d.ts +20 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/utils/helmHelpers.d.ts +18 -2
- package/dist/lib/utils/helmHelpers.js +246 -3
- package/package.json +4 -5
package/dist/cli.js
CHANGED
|
@@ -1,31 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawnSync
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { spawnSync } from 'child_process';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
6
|
import { SecurityUtils } from './lib/security.js';
|
|
9
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
-
const __dirname = dirname(__filename);
|
|
11
|
-
const
|
|
12
|
-
const
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const UMBRELLA_CONFIG_FILE = 'umbrella.config.json';
|
|
10
|
+
const UMBRELLA_FILE_NAME = 'umbrella.ts';
|
|
11
|
+
function log(msg, silent = false) {
|
|
12
|
+
if (!silent) {
|
|
13
|
+
console.log(msg);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
13
16
|
function usageAndExit(msg) {
|
|
14
|
-
if (msg)
|
|
15
|
-
console.error(
|
|
17
|
+
if (msg) {
|
|
18
|
+
console.error(`Error: ${msg}`);
|
|
19
|
+
}
|
|
16
20
|
console.log([
|
|
17
|
-
'
|
|
21
|
+
'Usage: tl <command> [options]',
|
|
18
22
|
'',
|
|
19
|
-
'
|
|
20
|
-
' tl init <chart-name>
|
|
21
|
-
' tl synth
|
|
22
|
-
' tl validate
|
|
23
|
-
' tl
|
|
24
|
-
' tl
|
|
25
|
-
' tl package <chartDir> [outDir] Run `helm package` into outDir (requires Helm)',
|
|
23
|
+
'Commands:',
|
|
24
|
+
' tl init <chart-name> Create new chart',
|
|
25
|
+
' tl synth [outDir] Generate Helm chart',
|
|
26
|
+
' tl validate Validate chart',
|
|
27
|
+
' tl deploy <release> [namespace] Deploy chart',
|
|
28
|
+
' tl templates List available templates',
|
|
26
29
|
'',
|
|
27
30
|
'Umbrella Charts:',
|
|
28
|
-
' tl umbrella init <
|
|
31
|
+
' tl umbrella init <n> Create umbrella chart structure',
|
|
29
32
|
' tl umbrella add <subchart> Add subchart to umbrella',
|
|
30
33
|
' tl umbrella synth [outDir] Generate umbrella chart',
|
|
31
34
|
'',
|
|
@@ -34,17 +37,16 @@ function usageAndExit(msg) {
|
|
|
34
37
|
' --silent Suppress output (useful for CI)',
|
|
35
38
|
' --env <environment> Use environment-specific values',
|
|
36
39
|
' --set <key=value> Override values (can be used multiple times)',
|
|
37
|
-
' --version, -v Show version information',
|
|
38
40
|
' --help, -h Show this help message',
|
|
39
41
|
'',
|
|
40
42
|
'Examples:',
|
|
41
43
|
' tl init my-app',
|
|
42
|
-
' tl synth
|
|
43
|
-
' tl validate
|
|
44
|
-
' tl deploy
|
|
45
|
-
' tl synth
|
|
46
|
-
' tl synth
|
|
47
|
-
' tl deploy
|
|
44
|
+
' tl synth my-app my-app/dist',
|
|
45
|
+
' tl validate my-app',
|
|
46
|
+
' tl deploy my-app my-release --env prod',
|
|
47
|
+
' tl synth my-app --dry-run --silent',
|
|
48
|
+
' tl synth my-app --set replicas=5 --set image.tag=v2.0.0',
|
|
49
|
+
' tl deploy my-app my-release --set service.port=8080',
|
|
48
50
|
].join('\n'));
|
|
49
51
|
process.exit(msg ? 1 : 0);
|
|
50
52
|
}
|
|
@@ -53,100 +55,50 @@ async function cmdInit(name, silent = false) {
|
|
|
53
55
|
usageAndExit('Missing <chart-name>');
|
|
54
56
|
const validName = name;
|
|
55
57
|
if (!SecurityUtils.isValidChartName(validName)) {
|
|
56
|
-
usageAndExit('Invalid chart name
|
|
58
|
+
usageAndExit('Invalid chart name. Must be lowercase, start with a letter, and contain only letters, numbers, and dashes.');
|
|
57
59
|
}
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
writeFileSync(file, exampleChartTs(validName));
|
|
67
|
-
log(SecurityUtils.sanitizeLogMessage(`Scaffold created at ${file}`), silent);
|
|
60
|
+
const base = path.join(process.cwd(), validName);
|
|
61
|
+
const chartFile = path.join(base, 'chart.ts');
|
|
62
|
+
fs.mkdirSync(base, { recursive: true });
|
|
63
|
+
const { generateBasicChart } = await import('./lib/templates/basic-chart.js');
|
|
64
|
+
fs.writeFileSync(chartFile, generateBasicChart(validName));
|
|
65
|
+
log(`Chart created at ${base}`, silent);
|
|
66
|
+
log(`Generated chart.ts file`, silent);
|
|
67
|
+
log(`Run 'tl synth ${validName}' to generate complete Helm chart`, silent);
|
|
68
68
|
}
|
|
69
|
-
async function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
async function cmdSynth(chartDirOrOutDir, flags) {
|
|
70
|
+
let chartDir = process.cwd();
|
|
71
|
+
let outDir;
|
|
72
|
+
if (chartDirOrOutDir && fs.existsSync(path.join(chartDirOrOutDir, 'chart.ts'))) {
|
|
73
|
+
chartDir = path.resolve(chartDirOrOutDir);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
outDir = chartDirOrOutDir;
|
|
77
|
+
}
|
|
78
|
+
const chartFile = path.join(chartDir, 'chart.ts');
|
|
79
|
+
const defaultOutDir = path.join(chartDir, 'dist');
|
|
80
|
+
if (!fs.existsSync(chartFile)) {
|
|
81
|
+
console.error('chart.ts not found. Run `tl init` first.');
|
|
79
82
|
process.exit(1);
|
|
80
83
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
console.error('✗ Helm not found. Install Helm to enable chart validation.');
|
|
87
|
-
console.error(` Install: ${HELM_INSTALL_URL}`);
|
|
88
|
-
console.error(` ${HELM_ENV_VAR_MSG}`);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
const tempDir = join(process.cwd(), '.timonel-validate');
|
|
92
|
-
try {
|
|
93
|
-
await cmdSynth(projectDir, tempDir, { dryRun: false, silent: true, set: {} });
|
|
94
|
-
const lintResult = spawnSync(helm, ['lint', tempDir], {
|
|
95
|
-
stdio: silent ? 'pipe' : 'inherit',
|
|
96
|
-
encoding: 'utf8',
|
|
97
|
-
});
|
|
98
|
-
if (lintResult.status !== 0) {
|
|
99
|
-
if (silent && lintResult.stderr) {
|
|
100
|
-
console.error(SecurityUtils.sanitizeLogMessage(lintResult.stderr));
|
|
101
|
-
}
|
|
102
|
-
console.error('✗ Chart validation failed: Helm lint errors found');
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
log('✓ Chart validation passed', silent);
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
109
|
-
console.error('✗ Chart validation failed:', SecurityUtils.sanitizeLogMessage(errorMsg));
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
finally {
|
|
113
|
-
if (existsSync(tempDir)) {
|
|
114
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
async function executeTypeScriptChart(resolvedPath, outDir, flags) {
|
|
84
|
+
const resolvedOutDir = outDir ? path.resolve(outDir) : defaultOutDir;
|
|
85
|
+
const originalContent = fs.readFileSync(chartFile, 'utf8');
|
|
86
|
+
const modifiedContent = originalContent.replace(/chart\.writeHelmChart\(['"][^'"]*['"]\)/, `chart.writeHelmChart('${resolvedOutDir}')`);
|
|
87
|
+
const tempChartFile = path.join(chartDir, '.timonel-temp-chart.ts');
|
|
88
|
+
fs.writeFileSync(tempChartFile, modifiedContent);
|
|
119
89
|
const wrapperScript = `
|
|
120
90
|
import { pathToFileURL } from 'url';
|
|
121
91
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (typeof runner !== 'function') {
|
|
125
|
-
console.error('chart.ts must export a default/run/synth function');
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
${flags?.set && Object.keys(flags.set).length > 0
|
|
130
|
-
? `
|
|
131
|
-
// Apply --set overrides if provided
|
|
132
|
-
if (mod.rutter && typeof mod.rutter.setValues === 'function') {
|
|
133
|
-
mod.rutter.setValues(${JSON.stringify(flags.set)});
|
|
134
|
-
}
|
|
135
|
-
`
|
|
136
|
-
: ''}
|
|
137
|
-
|
|
138
|
-
const outDir = '${outDir}';
|
|
139
|
-
const fs = await import('fs');
|
|
140
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
141
|
-
await Promise.resolve(runner(outDir));
|
|
142
|
-
console.log('Chart written to ' + outDir);
|
|
92
|
+
// Import the modified chart file which should execute the synthesis directly
|
|
93
|
+
await import(pathToFileURL('${tempChartFile}').href);
|
|
143
94
|
`;
|
|
144
|
-
const wrapperFile = join(
|
|
95
|
+
const wrapperFile = path.join(chartDir, '.timonel-wrapper.mjs');
|
|
145
96
|
try {
|
|
146
|
-
writeFileSync(wrapperFile, wrapperScript);
|
|
147
|
-
const result = spawnSync('npx', ['tsx', wrapperFile], {
|
|
97
|
+
fs.writeFileSync(wrapperFile, wrapperScript);
|
|
98
|
+
const result = spawnSync('npx', ['tsx', wrapperFile].filter(Boolean), {
|
|
148
99
|
stdio: flags?.silent ? 'pipe' : 'inherit',
|
|
149
100
|
encoding: 'utf8',
|
|
101
|
+
cwd: chartDir,
|
|
150
102
|
});
|
|
151
103
|
if (result.status !== 0) {
|
|
152
104
|
if (flags?.silent && result.stderr) {
|
|
@@ -156,118 +108,77 @@ console.log('Chart written to ' + outDir);
|
|
|
156
108
|
}
|
|
157
109
|
}
|
|
158
110
|
finally {
|
|
159
|
-
if (existsSync(wrapperFile)) {
|
|
160
|
-
unlinkSync(wrapperFile);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
async function cmdSynth(projectDir, out, flags) {
|
|
165
|
-
if (!projectDir)
|
|
166
|
-
usageAndExit('Missing <projectDir>');
|
|
167
|
-
const proj = projectDir;
|
|
168
|
-
const basePath = isAbsolute(proj) ? dirname(proj) : process.cwd();
|
|
169
|
-
const chartTs = isAbsolute(proj)
|
|
170
|
-
? SecurityUtils.validatePath(join(proj, 'chart.ts'), basePath)
|
|
171
|
-
: SecurityUtils.validatePath(join(process.cwd(), proj, 'chart.ts'), process.cwd());
|
|
172
|
-
if (!existsSync(chartTs)) {
|
|
173
|
-
console.error(SecurityUtils.sanitizeLogMessage(`chart.ts not found at ${chartTs}`));
|
|
174
|
-
process.exit(1);
|
|
175
|
-
}
|
|
176
|
-
if (!SecurityUtils.isValidTypeScriptFile(chartTs)) {
|
|
177
|
-
console.error('Security: Only TypeScript files (.ts) are allowed');
|
|
178
|
-
process.exit(1);
|
|
179
|
-
}
|
|
180
|
-
const resolvedPath = resolve(chartTs);
|
|
181
|
-
const outDir = out || join(dirname(chartTs), 'dist');
|
|
182
|
-
if (flags?.dryRun) {
|
|
183
|
-
log(`[DRY RUN] Would write chart to ${outDir}`, flags.silent);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
try {
|
|
187
|
-
await executeTypeScriptChart(resolvedPath, outDir, flags);
|
|
188
|
-
}
|
|
189
|
-
catch (error) {
|
|
190
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
191
|
-
console.error(SecurityUtils.sanitizeLogMessage(`Failed to execute chart: ${errorMsg}`));
|
|
192
|
-
process.exit(1);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
async function cmdDiff(projectDir, chartDir, silent = false) {
|
|
196
|
-
if (!projectDir || !chartDir)
|
|
197
|
-
usageAndExit('Missing <projectDir> or <chartDir>');
|
|
198
|
-
const validProjectDir = projectDir;
|
|
199
|
-
const validChartDir = chartDir;
|
|
200
|
-
const tempDir = join(process.cwd(), '.timonel-temp');
|
|
201
|
-
try {
|
|
202
|
-
await cmdSynth(validProjectDir, tempDir);
|
|
203
|
-
const diffCmd = process.platform === 'win32' ? 'fc' : 'diff';
|
|
204
|
-
const args = process.platform === 'win32'
|
|
205
|
-
? ['/N', validChartDir, tempDir]
|
|
206
|
-
: ['-r', validChartDir, tempDir];
|
|
207
|
-
log(`> ${diffCmd} ${args.join(' ')}`, silent);
|
|
208
|
-
const res = spawnSync(diffCmd, args, { stdio: 'inherit' });
|
|
209
|
-
if (res.status === 0) {
|
|
210
|
-
log('✓ No differences found', silent);
|
|
111
|
+
if (fs.existsSync(wrapperFile)) {
|
|
112
|
+
fs.unlinkSync(wrapperFile);
|
|
211
113
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
process.exit(1);
|
|
114
|
+
if (fs.existsSync(tempChartFile)) {
|
|
115
|
+
fs.unlinkSync(tempChartFile);
|
|
215
116
|
}
|
|
216
117
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
118
|
+
}
|
|
119
|
+
async function cmdValidate(flags) {
|
|
120
|
+
const result = spawnSync('helm', ['lint', '.'], {
|
|
121
|
+
stdio: flags?.silent ? 'pipe' : 'inherit',
|
|
122
|
+
encoding: 'utf8',
|
|
123
|
+
});
|
|
124
|
+
if (result.status !== 0) {
|
|
125
|
+
if (flags?.silent && result.stderr) {
|
|
126
|
+
console.error(SecurityUtils.sanitizeLogMessage(result.stderr));
|
|
220
127
|
}
|
|
128
|
+
process.exit(result.status ?? 1);
|
|
221
129
|
}
|
|
222
130
|
}
|
|
223
|
-
async function cmdDeploy(
|
|
224
|
-
if (!
|
|
225
|
-
usageAndExit('Missing <
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
const tempDir = join(process.cwd(), '.timonel-deploy');
|
|
239
|
-
try {
|
|
240
|
-
await cmdSynth(validProjectDir, tempDir, flags);
|
|
241
|
-
const checkCmd = `${helm} status ${releaseName}`;
|
|
242
|
-
const releaseExists = spawnSync('sh', ['-c', checkCmd], { stdio: 'ignore' }).status === 0;
|
|
243
|
-
const action = releaseExists ? 'upgrade' : 'install';
|
|
244
|
-
const args = [action, validReleaseName, tempDir];
|
|
245
|
-
if (flags?.env) {
|
|
246
|
-
const valuesFile = join(tempDir, `values-${flags.env}.yaml`);
|
|
247
|
-
if (existsSync(valuesFile)) {
|
|
248
|
-
args.push('-f', valuesFile);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (flags?.dryRun) {
|
|
252
|
-
args.push('--dry-run');
|
|
253
|
-
}
|
|
254
|
-
log(`> ${helm} ${args.join(' ')}`, flags?.silent);
|
|
255
|
-
const res = spawnSync(helm, args, { stdio: 'inherit' });
|
|
256
|
-
if (res.status !== 0) {
|
|
257
|
-
process.exit(res.status ?? 1);
|
|
131
|
+
async function cmdDeploy(release, namespace, flags) {
|
|
132
|
+
if (!release)
|
|
133
|
+
usageAndExit('Missing <release>');
|
|
134
|
+
const args = ['upgrade', '--install', release, '.'];
|
|
135
|
+
if (namespace) {
|
|
136
|
+
args.push('--namespace', namespace);
|
|
137
|
+
}
|
|
138
|
+
const result = spawnSync('helm', args.filter((arg) => Boolean(arg)), {
|
|
139
|
+
stdio: flags?.silent ? 'pipe' : 'inherit',
|
|
140
|
+
encoding: 'utf8',
|
|
141
|
+
});
|
|
142
|
+
if (result.status !== 0) {
|
|
143
|
+
if (flags?.silent && result.stderr) {
|
|
144
|
+
console.error(SecurityUtils.sanitizeLogMessage(result.stderr));
|
|
258
145
|
}
|
|
259
|
-
|
|
146
|
+
process.exit(result.status ?? 1);
|
|
260
147
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
148
|
+
}
|
|
149
|
+
async function cmdTemplates(flags) {
|
|
150
|
+
const templates = [
|
|
151
|
+
{
|
|
152
|
+
name: 'basic-chart',
|
|
153
|
+
description: 'Basic chart with deployment and service',
|
|
154
|
+
usage: 'tl init my-app',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'umbrella-chart',
|
|
158
|
+
description: 'Umbrella chart for managing multiple subcharts',
|
|
159
|
+
usage: 'tl umbrella init my-app',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'subchart',
|
|
163
|
+
description: 'Subchart for use with umbrella charts',
|
|
164
|
+
usage: 'tl umbrella add my-subchart',
|
|
165
|
+
},
|
|
166
|
+
];
|
|
167
|
+
if (flags?.silent) {
|
|
168
|
+
console.log(JSON.stringify(templates));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.log('Available templates:');
|
|
172
|
+
templates.forEach((t) => {
|
|
173
|
+
console.log(`\n${t.name}`);
|
|
174
|
+
console.log(` Description: ${t.description}`);
|
|
175
|
+
console.log(` Usage: ${t.usage}`);
|
|
176
|
+
});
|
|
265
177
|
}
|
|
266
178
|
}
|
|
267
179
|
async function cmdUmbrella(subcommand, args, flags) {
|
|
268
|
-
const UMBRELLA_USAGE_MSG = 'Missing umbrella subcommand (init|add|synth)';
|
|
269
180
|
if (!subcommand)
|
|
270
|
-
usageAndExit(
|
|
181
|
+
usageAndExit('Missing umbrella subcommand');
|
|
271
182
|
switch (subcommand) {
|
|
272
183
|
case 'init':
|
|
273
184
|
await cmdUmbrellaInit(args?.[0], flags?.silent);
|
|
@@ -282,8 +193,6 @@ async function cmdUmbrella(subcommand, args, flags) {
|
|
|
282
193
|
usageAndExit(`Unknown umbrella subcommand: ${subcommand}`);
|
|
283
194
|
}
|
|
284
195
|
}
|
|
285
|
-
const UMBRELLA_CONFIG_FILE = 'umbrella.config.json';
|
|
286
|
-
const UMBRELLA_FILE_NAME = 'umbrella.ts';
|
|
287
196
|
async function cmdUmbrellaInit(name, silent = false) {
|
|
288
197
|
const MISSING_NAME_MSG = 'Missing umbrella chart name';
|
|
289
198
|
if (!name)
|
|
@@ -293,64 +202,91 @@ async function cmdUmbrellaInit(name, silent = false) {
|
|
|
293
202
|
const umbrellaFile = path.join(base, UMBRELLA_FILE_NAME);
|
|
294
203
|
const configFile = path.join(base, UMBRELLA_CONFIG_FILE);
|
|
295
204
|
fs.mkdirSync(base, { recursive: true });
|
|
296
|
-
|
|
297
|
-
fs.writeFileSync(umbrellaFile,
|
|
205
|
+
const { generateUmbrellaChart } = await import('./lib/templates/umbrella-chart.js');
|
|
206
|
+
fs.writeFileSync(umbrellaFile, generateUmbrellaChart(validName));
|
|
298
207
|
const config = {
|
|
299
208
|
name: validName,
|
|
300
209
|
version: '0.1.0',
|
|
301
|
-
description: `${
|
|
210
|
+
description: `${validName} umbrella chart`,
|
|
302
211
|
subcharts: [],
|
|
303
212
|
};
|
|
304
213
|
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
214
|
+
const chartsDir = path.join(base, 'charts');
|
|
215
|
+
fs.mkdirSync(chartsDir, { recursive: true });
|
|
305
216
|
log(`Umbrella chart structure created at ${base}`, silent);
|
|
306
217
|
log(`Add subcharts with: tl umbrella add <subchart-name>`, silent);
|
|
307
218
|
}
|
|
308
|
-
function
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const camelCaseName = chartName.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
317
|
-
const importStatement = `import { rutter as ${camelCaseName} } from './charts/${subchartPath}/chart';`;
|
|
318
|
-
const importRegex = /import\s+.*?from\s+['"].*?['"];?/g;
|
|
319
|
-
const imports = content.match(importRegex) || [];
|
|
320
|
-
if (imports.length > 0) {
|
|
321
|
-
const lastImport = imports[imports.length - 1];
|
|
322
|
-
if (!lastImport) {
|
|
323
|
-
throw new Error('No valid import statements found');
|
|
219
|
+
function toCamelCase(str) {
|
|
220
|
+
return str.replace(/-([a-z])/g, (g) => g[1]?.toUpperCase() || '');
|
|
221
|
+
}
|
|
222
|
+
function findLastImportIndex(lines) {
|
|
223
|
+
let lastImportIndex = -1;
|
|
224
|
+
for (let i = 0; i < lines.length; i++) {
|
|
225
|
+
if (lines[i]?.trim().startsWith('import ')) {
|
|
226
|
+
lastImportIndex = i;
|
|
324
227
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if (!content.includes(importStatement)) {
|
|
328
|
-
content = content.slice(0, insertIndex) + '\n' + importStatement + content.slice(insertIndex);
|
|
228
|
+
else if (lines[i]?.trim() && !lines[i]?.trim().startsWith('import ')) {
|
|
229
|
+
break;
|
|
329
230
|
}
|
|
330
231
|
}
|
|
232
|
+
return lastImportIndex;
|
|
233
|
+
}
|
|
234
|
+
function addImportStatement(content, importStatement) {
|
|
235
|
+
if (content.includes(importStatement)) {
|
|
236
|
+
return content;
|
|
237
|
+
}
|
|
238
|
+
const lines = content.split('\n');
|
|
239
|
+
const lastImportIndex = findLastImportIndex(lines);
|
|
240
|
+
if (lastImportIndex >= 0) {
|
|
241
|
+
lines.splice(lastImportIndex + 1, 0, importStatement);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
lines.splice(0, 0, importStatement);
|
|
245
|
+
}
|
|
246
|
+
return lines.join('\n');
|
|
247
|
+
}
|
|
248
|
+
function buildSubchartsContent(subchartsContent, subchartEntry) {
|
|
249
|
+
const hasExistingEntries = /\{\s*name:\s*['"]/.test(subchartsContent || '');
|
|
250
|
+
if (!hasExistingEntries) {
|
|
251
|
+
return `\n // Add your subcharts here:\n ${subchartEntry},\n `;
|
|
252
|
+
}
|
|
253
|
+
const trimmedContent = subchartsContent?.trimEnd() ?? '';
|
|
254
|
+
const needsComma = !trimmedContent.endsWith(',');
|
|
255
|
+
const comma = needsComma ? ',' : '';
|
|
256
|
+
return trimmedContent + `${comma}\n ${subchartEntry}`;
|
|
257
|
+
}
|
|
258
|
+
function addSubchartToArray(content, chartName, camelCaseName) {
|
|
331
259
|
const subchartsRegex = /subcharts:\s*\[([\s\S]*?)\]/;
|
|
332
260
|
const match = content.match(subchartsRegex);
|
|
333
|
-
if (match) {
|
|
334
|
-
|
|
335
|
-
if (subchartsContent === undefined) {
|
|
336
|
-
throw new Error('Unable to parse subcharts content');
|
|
337
|
-
}
|
|
338
|
-
if (!subchartsContent.includes(`name: '${chartName}'`)) {
|
|
339
|
-
let newSubchartsContent;
|
|
340
|
-
const subchartEntry = `{ name: '${chartName}', rutter: ${camelCaseName} }`;
|
|
341
|
-
const hasExistingEntries = /\{\s*name:\s*['"]/.test(subchartsContent);
|
|
342
|
-
if (!hasExistingEntries) {
|
|
343
|
-
newSubchartsContent = `\n // Add your subcharts here:\n ${subchartEntry},\n `;
|
|
344
|
-
}
|
|
345
|
-
else {
|
|
346
|
-
const trimmedContent = subchartsContent.trimEnd();
|
|
347
|
-
newSubchartsContent = trimmedContent + `,\n ${subchartEntry}`;
|
|
348
|
-
}
|
|
349
|
-
content = content.replace(subchartsRegex, `subcharts: [${newSubchartsContent}\n ]`);
|
|
350
|
-
}
|
|
261
|
+
if (!match) {
|
|
262
|
+
return content;
|
|
351
263
|
}
|
|
264
|
+
const subchartsContent = match[1];
|
|
265
|
+
if (subchartsContent?.includes(`name: '${chartName}'`)) {
|
|
266
|
+
return content;
|
|
267
|
+
}
|
|
268
|
+
const subchartEntry = `{ name: '${chartName}', chart: ${camelCaseName} }`;
|
|
269
|
+
const newSubchartsContent = buildSubchartsContent(subchartsContent, subchartEntry);
|
|
270
|
+
return content.replace(subchartsRegex, `subcharts: [${newSubchartsContent}\n ]`);
|
|
271
|
+
}
|
|
272
|
+
function updateUmbrellaTs(subchartPath, chartName) {
|
|
273
|
+
const umbrellaFile = path.join(process.cwd(), UMBRELLA_FILE_NAME);
|
|
274
|
+
const content = processUmbrellaFile(umbrellaFile, subchartPath, chartName);
|
|
352
275
|
fs.writeFileSync(umbrellaFile, content);
|
|
353
276
|
}
|
|
277
|
+
function processUmbrellaFile(umbrellaFile, subchartPath, chartName) {
|
|
278
|
+
let content = fs.readFileSync(umbrellaFile, 'utf8');
|
|
279
|
+
const importData = createImportData(subchartPath, chartName);
|
|
280
|
+
content = addImportStatement(content, importData.importStatement);
|
|
281
|
+
content = addSubchartToArray(content, chartName, importData.camelCaseName);
|
|
282
|
+
return content;
|
|
283
|
+
}
|
|
284
|
+
function createImportData(subchartPath, chartName) {
|
|
285
|
+
const importPath = `./charts/${subchartPath}/chart`;
|
|
286
|
+
const camelCaseName = toCamelCase(chartName);
|
|
287
|
+
const importStatement = `import ${camelCaseName} from '${importPath}';`;
|
|
288
|
+
return { importPath, camelCaseName, importStatement };
|
|
289
|
+
}
|
|
354
290
|
async function cmdUmbrellaAdd(subchartPath, silent = false) {
|
|
355
291
|
const MISSING_SUBCHART_MSG = 'Missing subchart name or path';
|
|
356
292
|
if (!subchartPath)
|
|
@@ -366,7 +302,9 @@ async function cmdUmbrellaAdd(subchartPath, silent = false) {
|
|
|
366
302
|
const subchartDir = path.join(process.cwd(), 'charts', validSubchartPath);
|
|
367
303
|
fs.mkdirSync(subchartDir, { recursive: true });
|
|
368
304
|
const chartFile = path.join(subchartDir, 'chart.ts');
|
|
369
|
-
|
|
305
|
+
const { generateSubchartTemplate } = await import('./lib/templates/subchart.js');
|
|
306
|
+
const subchartContent = generateSubchartTemplate(subchartName);
|
|
307
|
+
fs.writeFileSync(chartFile, subchartContent);
|
|
370
308
|
config.subcharts.push({
|
|
371
309
|
name: subchartName,
|
|
372
310
|
version: '0.1.0',
|
|
@@ -376,6 +314,16 @@ async function cmdUmbrellaAdd(subchartPath, silent = false) {
|
|
|
376
314
|
updateUmbrellaTs(validSubchartPath, subchartName);
|
|
377
315
|
log(`Subchart ${subchartName} added to umbrella at path '${validSubchartPath}'`, silent);
|
|
378
316
|
}
|
|
317
|
+
async function cmdUmbrellaSynth(outDir, flags) {
|
|
318
|
+
const umbrellaFile = path.join(process.cwd(), UMBRELLA_FILE_NAME);
|
|
319
|
+
const defaultOutDir = path.join(process.cwd(), 'dist');
|
|
320
|
+
if (!fs.existsSync(umbrellaFile)) {
|
|
321
|
+
console.error('umbrella.ts not found. Run `tl umbrella init` first.');
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
const resolvedOutDir = outDir ? path.resolve(outDir) : defaultOutDir;
|
|
325
|
+
await executeTypeScriptUmbrella(umbrellaFile, resolvedOutDir, flags);
|
|
326
|
+
}
|
|
379
327
|
async function executeTypeScriptUmbrella(resolvedPath, outDir, flags) {
|
|
380
328
|
const wrapperScript = `
|
|
381
329
|
import { pathToFileURL } from 'url';
|
|
@@ -396,7 +344,7 @@ console.log('Umbrella chart written to ' + output);
|
|
|
396
344
|
const wrapperFile = path.join(process.cwd(), '.timonel-umbrella-wrapper.mjs');
|
|
397
345
|
try {
|
|
398
346
|
fs.writeFileSync(wrapperFile, wrapperScript);
|
|
399
|
-
const result = spawnSync('npx', ['tsx', wrapperFile], {
|
|
347
|
+
const result = spawnSync('npx', ['tsx', wrapperFile].filter(Boolean), {
|
|
400
348
|
stdio: flags?.silent ? 'pipe' : 'inherit',
|
|
401
349
|
encoding: 'utf8',
|
|
402
350
|
});
|
|
@@ -413,453 +361,80 @@ console.log('Umbrella chart written to ' + output);
|
|
|
413
361
|
}
|
|
414
362
|
}
|
|
415
363
|
}
|
|
416
|
-
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const resolvedPath = path.resolve(validatedPath);
|
|
433
|
-
const output = outDir || path.join(process.cwd(), 'dist');
|
|
434
|
-
if (flags?.dryRun) {
|
|
435
|
-
log(`[DRY RUN] Would write umbrella chart to ${output}`, flags.silent);
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
try {
|
|
439
|
-
await executeTypeScriptUmbrella(resolvedPath, output, flags);
|
|
440
|
-
}
|
|
441
|
-
catch (error) {
|
|
442
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
443
|
-
console.error(SecurityUtils.sanitizeLogMessage(`Failed to execute umbrella: ${errorMsg}`));
|
|
444
|
-
process.exit(1);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
async function cmdPackage(chartDir, out, silent = false) {
|
|
448
|
-
if (!chartDir)
|
|
449
|
-
usageAndExit('Missing <chartDir>');
|
|
450
|
-
const validChartDir = chartDir;
|
|
451
|
-
const src = path.isAbsolute(validChartDir)
|
|
452
|
-
? validChartDir
|
|
453
|
-
: path.join(process.cwd(), validChartDir);
|
|
454
|
-
const chartYaml = path.join(src, 'Chart.yaml');
|
|
455
|
-
if (!fs.existsSync(chartYaml)) {
|
|
456
|
-
console.error(`Chart.yaml not found in ${src}`);
|
|
457
|
-
process.exit(1);
|
|
458
|
-
}
|
|
459
|
-
const outDir = out ? (path.isAbsolute(out) ? out : path.join(process.cwd(), out)) : src;
|
|
460
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
461
|
-
const helm = process.env['HELM_BIN'] || 'helm';
|
|
462
|
-
const args = ['package', src, '-d', outDir];
|
|
463
|
-
log(`> ${helm} ${args.join(' ')}`, silent);
|
|
464
|
-
const res = spawnSync(helm, args, { stdio: 'inherit' });
|
|
465
|
-
if (res.error) {
|
|
466
|
-
console.error(`✗ Failed to execute Helm: ${res.error.message}`);
|
|
467
|
-
console.error(` Install: ${HELM_INSTALL_URL}`);
|
|
468
|
-
console.error(` ${HELM_ENV_VAR_MSG}`);
|
|
469
|
-
process.exit(1);
|
|
470
|
-
}
|
|
471
|
-
if (res.status !== 0) {
|
|
472
|
-
process.exit(res.status ?? 1);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
function exampleUmbrellaTs(name) {
|
|
476
|
-
return `import { createUmbrella } from 'timonel';
|
|
477
|
-
import { Rutter } from 'timonel';
|
|
478
|
-
|
|
479
|
-
// Import subcharts (these will be created with 'tl umbrella add')
|
|
480
|
-
// import { rutter as fluentBit } from './charts/fluent-bit/chart';
|
|
481
|
-
// import { rutter as openObserve } from './charts/openobserve/chart';
|
|
482
|
-
|
|
483
|
-
// Create umbrella chart
|
|
484
|
-
const umbrella = createUmbrella({
|
|
485
|
-
meta: {
|
|
486
|
-
name: '${name}',
|
|
487
|
-
version: '0.1.0',
|
|
488
|
-
description: '${name} umbrella chart',
|
|
489
|
-
appVersion: '1.0.0',
|
|
490
|
-
},
|
|
491
|
-
subcharts: [
|
|
492
|
-
// Add your subcharts here:
|
|
493
|
-
// { name: 'fluent-bit', rutter: fluentBit },
|
|
494
|
-
// { name: 'openobserve', rutter: openObserve },
|
|
495
|
-
],
|
|
496
|
-
defaultValues: {
|
|
497
|
-
global: {
|
|
498
|
-
// Global values shared across subcharts
|
|
499
|
-
},
|
|
500
|
-
},
|
|
501
|
-
envValues: {
|
|
502
|
-
dev: {
|
|
503
|
-
// Development environment overrides
|
|
504
|
-
},
|
|
505
|
-
prod: {
|
|
506
|
-
// Production environment overrides
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
export default function run(outDir: string) {
|
|
512
|
-
umbrella.write(outDir);
|
|
513
|
-
}
|
|
514
|
-
`;
|
|
515
|
-
}
|
|
516
|
-
function exampleSubchartTs(name) {
|
|
517
|
-
return `import { Rutter } from 'timonel';
|
|
518
|
-
import { valuesRef, helm } from 'timonel/lib/helm';
|
|
519
|
-
|
|
520
|
-
// Create a new Rutter instance
|
|
521
|
-
const rutter = new Rutter({
|
|
522
|
-
meta: {
|
|
523
|
-
name: '${name}',
|
|
524
|
-
version: '0.1.0',
|
|
525
|
-
description: '${name} subchart',
|
|
526
|
-
appVersion: '1.0.0',
|
|
527
|
-
},
|
|
528
|
-
defaultValues: {
|
|
529
|
-
image: { repository: 'nginx', tag: '1.27' },
|
|
530
|
-
replicas: 1,
|
|
531
|
-
service: { port: 80 },
|
|
532
|
-
},
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
// Add Deployment using addManifest with native Kubernetes object
|
|
536
|
-
rutter.addManifest({
|
|
537
|
-
apiVersion: 'apps/v1',
|
|
538
|
-
kind: 'Deployment',
|
|
539
|
-
metadata: {
|
|
540
|
-
name: '${name}',
|
|
541
|
-
labels: {
|
|
542
|
-
app: '${name}'
|
|
543
|
-
}
|
|
544
|
-
},
|
|
545
|
-
spec: {
|
|
546
|
-
replicas: valuesRef('replicas'),
|
|
547
|
-
selector: {
|
|
548
|
-
matchLabels: {
|
|
549
|
-
app: '${name}'
|
|
550
|
-
}
|
|
551
|
-
},
|
|
552
|
-
template: {
|
|
553
|
-
metadata: {
|
|
554
|
-
labels: {
|
|
555
|
-
app: '${name}'
|
|
556
|
-
}
|
|
557
|
-
},
|
|
558
|
-
spec: {
|
|
559
|
-
containers: [
|
|
560
|
-
{
|
|
561
|
-
name: '${name}',
|
|
562
|
-
image: String(valuesRef('image.repository')) + ':' + String(valuesRef('image.tag')),
|
|
563
|
-
ports: [
|
|
564
|
-
{
|
|
565
|
-
name: 'http',
|
|
566
|
-
containerPort: 80,
|
|
567
|
-
protocol: 'TCP'
|
|
568
|
-
}
|
|
569
|
-
]
|
|
364
|
+
function parseFlags(args) {
|
|
365
|
+
const flags = {};
|
|
366
|
+
while (args.length > 0 && args[0]?.startsWith('-')) {
|
|
367
|
+
const flag = args.shift();
|
|
368
|
+
switch (flag) {
|
|
369
|
+
case '--dry-run':
|
|
370
|
+
flags.dryRun = true;
|
|
371
|
+
break;
|
|
372
|
+
case '--silent':
|
|
373
|
+
flags.silent = true;
|
|
374
|
+
break;
|
|
375
|
+
case '--env': {
|
|
376
|
+
const envValue = args.shift();
|
|
377
|
+
if (envValue)
|
|
378
|
+
flags.env = envValue;
|
|
379
|
+
break;
|
|
570
380
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
// Add Service using addManifest with native Kubernetes object
|
|
578
|
-
rutter.addManifest({
|
|
579
|
-
apiVersion: 'v1',
|
|
580
|
-
kind: 'Service',
|
|
581
|
-
metadata: {
|
|
582
|
-
name: '${name}',
|
|
583
|
-
labels: {
|
|
584
|
-
app: '${name}'
|
|
585
|
-
}
|
|
586
|
-
},
|
|
587
|
-
spec: {
|
|
588
|
-
type: 'ClusterIP',
|
|
589
|
-
ports: [
|
|
590
|
-
{
|
|
591
|
-
port: valuesRef('service.port'),
|
|
592
|
-
targetPort: 'http',
|
|
593
|
-
protocol: 'TCP',
|
|
594
|
-
name: 'http'
|
|
595
|
-
}
|
|
596
|
-
],
|
|
597
|
-
selector: {
|
|
598
|
-
app: '${name}'
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
}, '${name}-service');
|
|
602
|
-
|
|
603
|
-
export default function run(outDir: string) {
|
|
604
|
-
rutter.write(outDir);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// Export rutter for umbrella chart
|
|
608
|
-
export { rutter };
|
|
609
|
-
`;
|
|
610
|
-
}
|
|
611
|
-
function exampleChartTs(name) {
|
|
612
|
-
return `import { Rutter } from 'timonel';
|
|
613
|
-
import { valuesRef, helm } from 'timonel/lib/helm';
|
|
614
|
-
|
|
615
|
-
// Define chart metadata and default/env values
|
|
616
|
-
const rutter = new Rutter({
|
|
617
|
-
meta: {
|
|
618
|
-
name: '${name}',
|
|
619
|
-
version: '0.1.0',
|
|
620
|
-
description: 'Example Helm chart generated with timonel + cdk8s',
|
|
621
|
-
appVersion: '1.0.0',
|
|
622
|
-
},
|
|
623
|
-
defaultValues: {
|
|
624
|
-
image: { repository: 'nginx', tag: '1.27' },
|
|
625
|
-
replicas: 1,
|
|
626
|
-
service: { port: 80 },
|
|
627
|
-
ingress: { enabled: false, host: 'example.com', className: 'nginx' },
|
|
628
|
-
namespace: { create: false, name: '${name}' },
|
|
629
|
-
},
|
|
630
|
-
envValues: {
|
|
631
|
-
dev: { replicas: 1 },
|
|
632
|
-
prod: { replicas: 3 },
|
|
633
|
-
},
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
// Add Deployment using addManifest with native Kubernetes object
|
|
637
|
-
rutter.addManifest({
|
|
638
|
-
apiVersion: 'apps/v1',
|
|
639
|
-
kind: 'Deployment',
|
|
640
|
-
metadata: {
|
|
641
|
-
name: '${name}',
|
|
642
|
-
labels: {
|
|
643
|
-
app: '${name}'
|
|
644
|
-
}
|
|
645
|
-
},
|
|
646
|
-
spec: {
|
|
647
|
-
replicas: valuesRef('replicas'),
|
|
648
|
-
selector: {
|
|
649
|
-
matchLabels: {
|
|
650
|
-
app: '${name}'
|
|
651
|
-
}
|
|
652
|
-
},
|
|
653
|
-
template: {
|
|
654
|
-
metadata: {
|
|
655
|
-
labels: {
|
|
656
|
-
app: '${name}'
|
|
657
|
-
}
|
|
658
|
-
},
|
|
659
|
-
spec: {
|
|
660
|
-
containers: [
|
|
661
|
-
{
|
|
662
|
-
name: '${name}',
|
|
663
|
-
image: String(valuesRef('image.repository')) + ':' + String(valuesRef('image.tag')),
|
|
664
|
-
ports: [
|
|
665
|
-
{
|
|
666
|
-
name: 'http',
|
|
667
|
-
containerPort: 80,
|
|
668
|
-
protocol: 'TCP'
|
|
669
|
-
}
|
|
670
|
-
],
|
|
671
|
-
env: [
|
|
672
|
-
{
|
|
673
|
-
name: 'APP_NAME',
|
|
674
|
-
value: '${name}'
|
|
675
|
-
},
|
|
676
|
-
{
|
|
677
|
-
name: 'RELEASE',
|
|
678
|
-
value: helm.releaseName
|
|
679
|
-
}
|
|
680
|
-
]
|
|
681
|
-
}
|
|
682
|
-
]
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}, '${name}-deployment');
|
|
687
|
-
|
|
688
|
-
// Add Service using addManifest with native Kubernetes object
|
|
689
|
-
rutter.addManifest({
|
|
690
|
-
apiVersion: 'v1',
|
|
691
|
-
kind: 'Service',
|
|
692
|
-
metadata: {
|
|
693
|
-
name: '${name}',
|
|
694
|
-
labels: {
|
|
695
|
-
app: '${name}'
|
|
696
|
-
}
|
|
697
|
-
},
|
|
698
|
-
spec: {
|
|
699
|
-
type: 'ClusterIP',
|
|
700
|
-
ports: [
|
|
701
|
-
{
|
|
702
|
-
port: valuesRef('service.port'),
|
|
703
|
-
targetPort: 'http',
|
|
704
|
-
protocol: 'TCP',
|
|
705
|
-
name: 'http'
|
|
706
|
-
}
|
|
707
|
-
],
|
|
708
|
-
selector: {
|
|
709
|
-
app: '${name}'
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}, '${name}-service');
|
|
713
|
-
|
|
714
|
-
// Add Ingress using addConditionalManifest with native Kubernetes object
|
|
715
|
-
rutter.addConditionalManifest({
|
|
716
|
-
apiVersion: 'networking.k8s.io/v1',
|
|
717
|
-
kind: 'Ingress',
|
|
718
|
-
metadata: {
|
|
719
|
-
name: '${name}',
|
|
720
|
-
labels: {
|
|
721
|
-
app: '${name}'
|
|
722
|
-
}
|
|
723
|
-
},
|
|
724
|
-
spec: {
|
|
725
|
-
ingressClassName: String(valuesRef('ingress.className')),
|
|
726
|
-
rules: [
|
|
727
|
-
{
|
|
728
|
-
host: String(valuesRef('ingress.host')),
|
|
729
|
-
http: {
|
|
730
|
-
paths: [
|
|
731
|
-
{
|
|
732
|
-
path: '/',
|
|
733
|
-
pathType: 'Prefix',
|
|
734
|
-
backend: {
|
|
735
|
-
service: {
|
|
736
|
-
name: '${name}',
|
|
737
|
-
port: {
|
|
738
|
-
number: valuesRef('service.port')
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
]
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
]
|
|
747
|
-
}
|
|
748
|
-
}, valuesRef('ingress.enabled'), '${name}-ingress');
|
|
749
|
-
|
|
750
|
-
// Add Namespace using addConditionalManifest
|
|
751
|
-
rutter.addConditionalManifest({
|
|
752
|
-
apiVersion: 'v1',
|
|
753
|
-
kind: 'Namespace',
|
|
754
|
-
metadata: {
|
|
755
|
-
name: String(valuesRef('namespace.name')),
|
|
756
|
-
labels: {
|
|
757
|
-
app: '${name}',
|
|
758
|
-
'app.kubernetes.io/name': '${name}',
|
|
759
|
-
'app.kubernetes.io/instance': helm.releaseName
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
}, valuesRef('namespace.create'), '${name}-namespace');
|
|
763
|
-
|
|
764
|
-
export default function run(outDir: string) {
|
|
765
|
-
rutter.write(outDir);
|
|
766
|
-
}
|
|
767
|
-
`;
|
|
768
|
-
}
|
|
769
|
-
function parseArgs() {
|
|
770
|
-
const argv = process.argv.slice(2);
|
|
771
|
-
const flags = { dryRun: false, silent: false, set: {} };
|
|
772
|
-
const args = [];
|
|
773
|
-
let cmd = '';
|
|
774
|
-
for (let i = 0; i < argv.length; i++) {
|
|
775
|
-
const arg = argv[i];
|
|
776
|
-
if (arg === '--dry-run') {
|
|
777
|
-
flags.dryRun = true;
|
|
778
|
-
}
|
|
779
|
-
else if (arg === '--silent') {
|
|
780
|
-
flags.silent = true;
|
|
781
|
-
}
|
|
782
|
-
else if (arg === '--env' && i + 1 < argv.length) {
|
|
783
|
-
const nextArg = argv[++i];
|
|
784
|
-
if (nextArg)
|
|
785
|
-
flags.env = nextArg;
|
|
786
|
-
}
|
|
787
|
-
else if (arg === '--version-bump' && i + 1 < argv.length) {
|
|
788
|
-
const nextArg = argv[++i];
|
|
789
|
-
if (nextArg)
|
|
790
|
-
flags.versionBump = nextArg;
|
|
791
|
-
}
|
|
792
|
-
else if (arg === '--set' && i + 1 < argv.length) {
|
|
793
|
-
const setValue = argv[++i];
|
|
794
|
-
if (setValue) {
|
|
795
|
-
const [key, value] = setValue.split('=', 2);
|
|
796
|
-
if (key && value !== undefined) {
|
|
797
|
-
flags.set[key] = value;
|
|
381
|
+
case '--set': {
|
|
382
|
+
const setValue = args.shift();
|
|
383
|
+
if (setValue) {
|
|
384
|
+
flags.set = flags.set || [];
|
|
385
|
+
flags.set.push(setValue);
|
|
798
386
|
}
|
|
387
|
+
break;
|
|
799
388
|
}
|
|
389
|
+
case '--version':
|
|
390
|
+
case '-v':
|
|
391
|
+
console.log('Timonel v0.1.0');
|
|
392
|
+
process.exit(0);
|
|
393
|
+
break;
|
|
394
|
+
case '--help':
|
|
395
|
+
case '-h':
|
|
396
|
+
usageAndExit();
|
|
397
|
+
break;
|
|
398
|
+
default:
|
|
399
|
+
usageAndExit(`Unknown flag: ${flag}`);
|
|
800
400
|
}
|
|
801
|
-
else if (arg === '--version' || arg === '-v') {
|
|
802
|
-
showVersion();
|
|
803
|
-
process.exit(0);
|
|
804
|
-
}
|
|
805
|
-
else if (arg === '--help' || arg === '-h') {
|
|
806
|
-
usageAndExit();
|
|
807
|
-
}
|
|
808
|
-
else if (arg && !arg.startsWith('--')) {
|
|
809
|
-
if (!cmd) {
|
|
810
|
-
cmd = arg;
|
|
811
|
-
}
|
|
812
|
-
else {
|
|
813
|
-
args.push(arg);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
return { cmd, args, flags };
|
|
818
|
-
}
|
|
819
|
-
function showVersion() {
|
|
820
|
-
const packagePath = path.join(__dirname, '..', 'package.json');
|
|
821
|
-
try {
|
|
822
|
-
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
823
|
-
console.log(`timonel v${packageJson.version}`);
|
|
824
|
-
}
|
|
825
|
-
catch {
|
|
826
|
-
console.log('timonel (version unknown)');
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
function log(message, silent = false) {
|
|
830
|
-
if (!silent) {
|
|
831
|
-
console.log(message);
|
|
832
401
|
}
|
|
402
|
+
return flags;
|
|
833
403
|
}
|
|
834
|
-
async function
|
|
835
|
-
|
|
836
|
-
switch (cmd) {
|
|
404
|
+
async function executeCommand(command, args, flags) {
|
|
405
|
+
switch (command) {
|
|
837
406
|
case 'init':
|
|
838
407
|
await cmdInit(args[0], flags.silent);
|
|
839
408
|
break;
|
|
840
|
-
case 'validate':
|
|
841
|
-
await cmdValidate(args[0], flags.silent);
|
|
842
|
-
break;
|
|
843
409
|
case 'synth':
|
|
844
|
-
await cmdSynth(args[0],
|
|
410
|
+
await cmdSynth(args[0], flags);
|
|
845
411
|
break;
|
|
846
|
-
case '
|
|
847
|
-
await
|
|
412
|
+
case 'validate':
|
|
413
|
+
await cmdValidate(flags);
|
|
848
414
|
break;
|
|
849
415
|
case 'deploy':
|
|
850
416
|
await cmdDeploy(args[0], args[1], flags);
|
|
851
417
|
break;
|
|
852
|
-
case '
|
|
853
|
-
await
|
|
418
|
+
case 'templates':
|
|
419
|
+
await cmdTemplates(flags);
|
|
854
420
|
break;
|
|
855
421
|
case 'umbrella':
|
|
856
422
|
await cmdUmbrella(args[0], args.slice(1), flags);
|
|
857
423
|
break;
|
|
424
|
+
case undefined:
|
|
425
|
+
usageAndExit('Missing command');
|
|
426
|
+
break;
|
|
858
427
|
default:
|
|
859
|
-
usageAndExit();
|
|
428
|
+
usageAndExit(`Unknown command: ${command}`);
|
|
860
429
|
}
|
|
861
430
|
}
|
|
862
|
-
main()
|
|
863
|
-
|
|
431
|
+
async function main() {
|
|
432
|
+
const args = process.argv.slice(2);
|
|
433
|
+
const command = args.shift();
|
|
434
|
+
const flags = parseFlags(args);
|
|
435
|
+
await executeCommand(command, args, flags);
|
|
436
|
+
}
|
|
437
|
+
main().catch((error) => {
|
|
438
|
+
console.error('Error:', error.message);
|
|
864
439
|
process.exit(1);
|
|
865
440
|
});
|