timonel 2.11.0 → 2.12.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/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ # [2.12.0](https://github.com/KenkoGeek/timonel/compare/v2.11.0...v2.12.0) (2025-09-28)
2
+
3
+ ### Bug Fixes
4
+
5
+ - **cli:** harden flag handling and path validation ([#130](https://github.com/KenkoGeek/timonel/issues/130)) ([2186d88](https://github.com/KenkoGeek/timonel/commit/2186d886428752025e7bf531c4a6541dade683a6))
6
+
7
+ ### Features
8
+
9
+ - **docs:** update references and CLI [skip ci] ([23704d3](https://github.com/KenkoGeek/timonel/commit/23704d3164cfbb92d183af40c70e27a32b566d06))
10
+ - **docs:** update references and CLI [skip ci] ([be4bcdc](https://github.com/KenkoGeek/timonel/commit/be4bcdca62cb3a9339e5ad70efc0ba31a6d9d5be))
11
+
1
12
  # [2.11.0](https://github.com/KenkoGeek/timonel/compare/v2.10.2...v2.11.0) (2025-09-26)
2
13
 
3
14
  ### 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
@@ -157,10 +154,10 @@ If you get `Error: Cannot find module 'cdk8s'` when running `tl umbrella synth`:
157
154
  "version": "1.0.0",
158
155
  "type": "module",
159
156
  "dependencies": {
160
- "cdk8s": "^2.70.15",
161
- "cdk8s-plus-33": "^2.3.6",
157
+ "cdk8s": "^2.70.16",
158
+ "cdk8s-plus-33": "^2.3.8",
162
159
  "constructs": "^10.4.2",
163
- "timonel": "^2.9.2"
160
+ "timonel": "^2.11.0"
164
161
  },
165
162
  "devDependencies": {
166
163
  "@types/node": "^24.5.2",
@@ -176,45 +173,6 @@ npm install
176
173
  tl umbrella synth # Now it works!
177
174
  ```
178
175
 
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
176
  ## 🤝 Contributing
219
177
 
220
178
  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 possiblePaths = [
16
- path.resolve(__dirname, '..', PACKAGE_JSON_FILE),
17
- path.resolve(__dirname, '..', '..', PACKAGE_JSON_FILE),
18
- path.resolve(process.cwd(), PACKAGE_JSON_FILE),
19
- path.resolve(__dirname, PACKAGE_JSON_FILE),
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) {
@@ -162,7 +145,23 @@ await import(pathToFileURL('${tempChartFile}').href);
162
145
  }
163
146
  }
164
147
  async function cmdValidate(flags) {
165
- const result = spawnSync('helm', ['lint', '.'], {
148
+ const lintArgs = ['lint', '.'];
149
+ if (flags?.env) {
150
+ try {
151
+ const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(flags.env);
152
+ lintArgs.push('-f', `values-${sanitizedEnv}.yaml`);
153
+ }
154
+ catch (error) {
155
+ console.error(SecurityUtils.sanitizeLogMessage(error.message));
156
+ process.exit(1);
157
+ }
158
+ }
159
+ if (flags?.set) {
160
+ for (const setValue of flags.set) {
161
+ lintArgs.push('--set', setValue);
162
+ }
163
+ }
164
+ const result = spawnSync('helm', lintArgs, {
166
165
  stdio: flags?.silent ? 'pipe' : 'inherit',
167
166
  encoding: 'utf8',
168
167
  });
@@ -180,6 +179,21 @@ async function cmdDeploy(release, namespace, flags) {
180
179
  if (namespace) {
181
180
  args.push('--namespace', namespace);
182
181
  }
182
+ if (flags?.env) {
183
+ try {
184
+ const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(flags.env);
185
+ args.push('-f', `values-${sanitizedEnv}.yaml`);
186
+ }
187
+ catch (error) {
188
+ console.error(SecurityUtils.sanitizeLogMessage(error.message));
189
+ process.exit(1);
190
+ }
191
+ }
192
+ if (flags?.set) {
193
+ for (const setValue of flags.set) {
194
+ args.push('--set', setValue);
195
+ }
196
+ }
183
197
  const result = spawnSync('helm', args.filter((arg) => Boolean(arg)), {
184
198
  stdio: flags?.silent ? 'pipe' : 'inherit',
185
199
  encoding: 'utf8',
@@ -343,8 +357,19 @@ async function cmdUmbrellaAdd(subchartPath, silent = false) {
343
357
  }
344
358
  const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
345
359
  const subchartName = path.basename(validSubchartPath);
346
- const subchartDir = path.join(process.cwd(), 'charts', validSubchartPath);
360
+ const chartsRoot = path.join(process.cwd(), 'charts');
361
+ const targetSubchartPath = path.join(chartsRoot, validSubchartPath);
362
+ let subchartDir;
363
+ try {
364
+ subchartDir = SecurityUtils.validatePath(targetSubchartPath, chartsRoot);
365
+ }
366
+ catch (error) {
367
+ console.error(SecurityUtils.sanitizeLogMessage(error.message));
368
+ process.exit(1);
369
+ }
347
370
  fs.mkdirSync(subchartDir, { recursive: true });
371
+ const relativeSubchartPath = path.relative(chartsRoot, subchartDir) || subchartName;
372
+ const normalizedSubchartPath = relativeSubchartPath.split(path.sep).join('/');
348
373
  const chartFile = path.join(subchartDir, 'chart.ts');
349
374
  const { generateFlexibleSubchartTemplate } = await import('./lib/templates/flexible-subchart.js');
350
375
  const subchartContent = generateFlexibleSubchartTemplate(subchartName);
@@ -352,11 +377,11 @@ async function cmdUmbrellaAdd(subchartPath, silent = false) {
352
377
  config.subcharts.push({
353
378
  name: subchartName,
354
379
  version: getVersion(),
355
- path: `./charts/${validSubchartPath}/chart.ts`,
380
+ path: `./charts/${normalizedSubchartPath}/chart.ts`,
356
381
  });
357
382
  fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
358
- updateUmbrellaTs(validSubchartPath, subchartName);
359
- log(`Subchart ${subchartName} added to umbrella at path '${validSubchartPath}'`, silent);
383
+ updateUmbrellaTs(normalizedSubchartPath, subchartName);
384
+ log(`Subchart ${subchartName} added to umbrella at path '${normalizedSubchartPath}'`, silent);
360
385
  }
361
386
  async function cmdUmbrellaSynth(outDir, flags) {
362
387
  const umbrellaFile = path.join(process.cwd(), UMBRELLA_FILE_NAME);
@@ -217,7 +217,7 @@ function isQuotesBalanced(str) {
217
217
  function validateFunctionCalls(_content) {
218
218
  }
219
219
  function checkCommonIssues(yaml, warnings) {
220
- const deprecatedFunctions = ['template', 'default'];
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 = [/'\(\\{\\{[^}]+\\}\\}\)'/g, /"\(\\{\\{[^}]+\\}\\}\)"/g];
235
+ const quotedPatterns = [/'(\{\{[^}]+\}\})'/g, /"(\{\{[^}]+\}\})"/g];
236
236
  for (const pattern of quotedPatterns) {
237
237
  let match;
238
238
  pattern.lastIndex = 0;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "timonel",
3
3
  "type": "module",
4
- "version": "2.11.0",
4
+ "version": "2.12.0",
5
5
  "description": "Timonel: programmatic Helm chart generator using cdk8s (TypeScript)",
6
6
  "bin": {
7
7
  "timonel": "dist/cli.js",