timonel 2.9.0 → 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 +7 -0
- package/README.md +109 -0
- package/dist/cli.js +0 -1
- package/dist/lib/helmChartWriter.js +12 -17
- package/dist/lib/rutter.js +29 -91
- package/dist/lib/templates/subchart.js +0 -3
- package/package.json +5 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.9.1](https://github.com/KenkoGeek/timonel/compare/v2.9.0...v2.9.1) (2025-09-19)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- **deps:** merge conflict solved [skip ci] ([3422464](https://github.com/KenkoGeek/timonel/commit/3422464ddee4a1408019ba4617b6f8e6f6c8fd12))
|
|
6
|
+
- **deps:** upgrade timonel version [skip ci] ([63901ef](https://github.com/KenkoGeek/timonel/commit/63901ef4277a1ebf702fc55337ed1eb7f6b226ea))
|
|
7
|
+
|
|
1
8
|
# [2.9.0](https://github.com/KenkoGeek/timonel/compare/v2.8.3...v2.9.0) (2025-09-17)
|
|
2
9
|
|
|
3
10
|
### Features
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Timonel
|
|
2
|
+
|
|
3
|
+
[![License: MIT][license-badge]][license-url]
|
|
4
|
+
[![npm version][npm-badge]][npm-url]
|
|
5
|
+
[![Security][security-badge]][security-url]
|
|
6
|
+
[![CodeQL][codeql-badge]][codeql-url]
|
|
7
|
+
[![CI][ci-badge]][ci-url]
|
|
8
|
+
[![pnpm][pnpm-badge]][pnpm-url]
|
|
9
|
+
[![Node.js][node-badge]][node-url]
|
|
10
|
+
[![TypeScript][ts-badge]][ts-url]
|
|
11
|
+
[![Maintained by KenkoGeek][maintained-badge]][maintained-url]
|
|
12
|
+
|
|
13
|
+
Timonel (Spanish for "helmsman") is a TypeScript library to programmatically generate Helm charts
|
|
14
|
+
using cdk8s. Define Kubernetes resources with classes and synthesize a full Helm chart with
|
|
15
|
+
`Chart.yaml`, `values.yaml`, per‑environment values files, and `templates/`.
|
|
16
|
+
|
|
17
|
+
## ✨ Key Features
|
|
18
|
+
|
|
19
|
+
- **Type-safe API** with strict TypeScript and cdk8s constructs.
|
|
20
|
+
- **Flexible resource creation** with built-in methods and `addManifest()` for custom resources.
|
|
21
|
+
- **Multi-environment support** with automatic values files generation.
|
|
22
|
+
- **Umbrella Charts** for managing multiple subcharts as a single unit.
|
|
23
|
+
- **AWS integrations**:
|
|
24
|
+
- **AWS**: EBS/EFS StorageClass, ALB Ingress, IRSA ServiceAccount.
|
|
25
|
+
- **Security-first approach** with NetworkPolicies and best practices.
|
|
26
|
+
- **Minimal CLI** (`tl`) for scaffolding and chart generation.
|
|
27
|
+
|
|
28
|
+
## 🚀 Quick Start
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Install Timonel globally
|
|
32
|
+
npm install -g timonel
|
|
33
|
+
|
|
34
|
+
# Create your first chart
|
|
35
|
+
tl init my-app
|
|
36
|
+
|
|
37
|
+
# Generate Helm chart
|
|
38
|
+
tl synth charts/my-app charts/my-app-dist
|
|
39
|
+
|
|
40
|
+
# Use with Helm
|
|
41
|
+
helm install my-app charts/my-app-dist
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 📚 Documentation
|
|
45
|
+
|
|
46
|
+
## 🔧 Troubleshooting
|
|
47
|
+
|
|
48
|
+
### CDK8s Module Not Found Error
|
|
49
|
+
|
|
50
|
+
If you get `Error: Cannot find module 'cdk8s'` when running `tl umbrella synth`:
|
|
51
|
+
|
|
52
|
+
**Problem**: Timonel is installed globally, but your project needs CDK8s dependencies locally.
|
|
53
|
+
|
|
54
|
+
**Solution**: Create a `package.json` in your project directory:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"name": "my-timonel-project",
|
|
59
|
+
"version": "1.0.0",
|
|
60
|
+
"type": "module",
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"cdk8s": "^2.70.11",
|
|
63
|
+
"cdk8s-plus-33": "^2.3.5",
|
|
64
|
+
"constructs": "^10.4.2",
|
|
65
|
+
"timonel": "^2.9.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/node": "^24.3.0",
|
|
69
|
+
"typescript": "^5.9.2"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then run:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm install
|
|
78
|
+
tl umbrella synth # Now it works!
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 🤝 Contributing
|
|
82
|
+
|
|
83
|
+
See our [Contributing Guide](https://github.com/KenkoGeek/timonel/wiki/Contributing) for development
|
|
84
|
+
setup and guidelines.
|
|
85
|
+
|
|
86
|
+
## 📄 License
|
|
87
|
+
|
|
88
|
+
MIT
|
|
89
|
+
|
|
90
|
+
<!-- Badges -->
|
|
91
|
+
|
|
92
|
+
[license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
|
|
93
|
+
[license-url]: https://opensource.org/licenses/MIT
|
|
94
|
+
[npm-badge]: https://img.shields.io/npm/v/timonel.svg
|
|
95
|
+
[npm-url]: https://www.npmjs.com/package/timonel
|
|
96
|
+
[security-badge]: https://img.shields.io/badge/Security-Policy-2ea44f?logo=security&logoColor=fff
|
|
97
|
+
[security-url]: SECURITY.md
|
|
98
|
+
[pnpm-badge]: https://img.shields.io/badge/pm-pnpm-ffd95a?logo=pnpm&logoColor=fff&labelColor=24292e
|
|
99
|
+
[pnpm-url]: https://pnpm.io/
|
|
100
|
+
[node-badge]: https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=fff
|
|
101
|
+
[node-url]: https://nodejs.org/
|
|
102
|
+
[ts-badge]: https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=fff
|
|
103
|
+
[ts-url]: https://www.typescriptlang.org/
|
|
104
|
+
[maintained-badge]: https://img.shields.io/badge/maintained%20by-KenkoGeek-6C78AF?style=flat
|
|
105
|
+
[maintained-url]: https://github.com/kenkogeek/
|
|
106
|
+
[ci-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/test.yaml/badge.svg?branch=main
|
|
107
|
+
[ci-url]: https://github.com/KenkoGeek/timonel/actions/workflows/teast.yaml
|
|
108
|
+
[codeql-badge]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yaml/badge.svg
|
|
109
|
+
[codeql-url]: https://github.com/KenkoGeek/timonel/actions/workflows/codeql.yaml
|
package/dist/cli.js
CHANGED
|
@@ -37,7 +37,6 @@ function usageAndExit(msg) {
|
|
|
37
37
|
' --silent Suppress output (useful for CI)',
|
|
38
38
|
' --env <environment> Use environment-specific values',
|
|
39
39
|
' --set <key=value> Override values (can be used multiple times)',
|
|
40
|
-
' --version, -v Show version information',
|
|
41
40
|
' --help, -h Show this help message',
|
|
42
41
|
'',
|
|
43
42
|
'Examples:',
|
|
@@ -5,21 +5,17 @@ import { SecurityUtils } from './security.js';
|
|
|
5
5
|
export class HelmChartWriter {
|
|
6
6
|
static write(opts) {
|
|
7
7
|
const { outDir, meta, defaultValues = {}, envValues = {}, assets, helpersTpl, notesTpl, valuesSchema, } = opts;
|
|
8
|
+
console.log(`📝 Writing Helm chart: ${meta.name} v${meta.version} to ${outDir}`);
|
|
8
9
|
const validatedOutDir = SecurityUtils.validatePath(outDir, process.cwd());
|
|
9
|
-
console.log('📝 Validated outDir:', validatedOutDir);
|
|
10
10
|
this.createDirectories(validatedOutDir);
|
|
11
|
-
console.log('📝 Directories created');
|
|
12
11
|
this.writeChartYaml(validatedOutDir, meta);
|
|
13
|
-
console.log('📝 Chart.yaml written');
|
|
14
12
|
this.writeValuesFiles(validatedOutDir, defaultValues, envValues);
|
|
15
|
-
console.log('📝 Values files written');
|
|
16
13
|
this.writeAssets(validatedOutDir, assets);
|
|
17
|
-
console.log('📝 Assets written');
|
|
18
14
|
this.writeHelpers(validatedOutDir, helpersTpl);
|
|
19
15
|
this.writeNotes(validatedOutDir, notesTpl);
|
|
20
16
|
this.writeSchema(validatedOutDir, valuesSchema);
|
|
21
17
|
this.writeHelmIgnore(validatedOutDir);
|
|
22
|
-
console.log(
|
|
18
|
+
console.log(`✅ Helm chart written successfully to ${validatedOutDir}`);
|
|
23
19
|
}
|
|
24
20
|
static createDirectories(outDir) {
|
|
25
21
|
fs.mkdirSync(path.join(outDir, 'templates'), { recursive: true });
|
|
@@ -112,25 +108,24 @@ function splitDocs(yamlStr) {
|
|
|
112
108
|
.filter((p) => p.length);
|
|
113
109
|
}
|
|
114
110
|
function writeAssets(outDir, assets) {
|
|
115
|
-
console.log('🔍 writeAssets called with', assets.length, 'assets');
|
|
116
111
|
for (const asset of assets) {
|
|
117
112
|
const sanitizedId = asset.id.replace(/[^a-zA-Z0-9-_]/g, '');
|
|
118
113
|
if (sanitizedId !== asset.id) {
|
|
114
|
+
console.error(`❌ Invalid asset ID detected: ${SecurityUtils.sanitizeLogMessage(asset.id)}`);
|
|
119
115
|
throw new Error(`Invalid asset ID: ${SecurityUtils.sanitizeLogMessage(asset.id)}`);
|
|
120
116
|
}
|
|
121
117
|
const targetDir = getTargetDirectory(asset.target);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
118
|
+
try {
|
|
119
|
+
if (asset.singleFile) {
|
|
120
|
+
writeSingleAssetFile(outDir, targetDir, sanitizedId, asset.yaml);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
writeMultipleAssetFiles(outDir, targetDir, sanitizedId, asset.yaml);
|
|
127
124
|
}
|
|
128
125
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
else {
|
|
133
|
-
writeMultipleAssetFiles(outDir, targetDir, sanitizedId, asset.yaml);
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error(`❌ Failed to write asset ${asset.id}:`, error);
|
|
128
|
+
throw error;
|
|
134
129
|
}
|
|
135
130
|
}
|
|
136
131
|
}
|
package/dist/lib/rutter.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ApiObject, App, Chart, Testing } from 'cdk8s';
|
|
2
2
|
import Handlebars from 'handlebars';
|
|
3
3
|
import YAML from 'yaml';
|
|
4
|
-
import {
|
|
4
|
+
import { include } from './helm.js';
|
|
5
5
|
import { HelmChartWriter } from './helmChartWriter.js';
|
|
6
6
|
import { AWSResources } from './resources/cloud/aws/awsResources.js';
|
|
7
7
|
import { KarpenterResources } from './resources/cloud/aws/karpenterResources.js';
|
|
@@ -9,6 +9,7 @@ import { generateHelpersTemplate } from './utils/helmHelpers.js';
|
|
|
9
9
|
export class Rutter {
|
|
10
10
|
constructor(props) {
|
|
11
11
|
this.assets = [];
|
|
12
|
+
console.log(`🚀 Initializing Rutter chart: ${props.meta.name} v${props.meta.version}`);
|
|
12
13
|
this.defaultValues = props.defaultValues ?? {};
|
|
13
14
|
this.envValues = props.envValues ?? {};
|
|
14
15
|
this.meta = props.meta;
|
|
@@ -140,33 +141,26 @@ export class Rutter {
|
|
|
140
141
|
});
|
|
141
142
|
}
|
|
142
143
|
validateManifestStructure(manifest) {
|
|
143
|
-
if (!manifest
|
|
144
|
-
|
|
144
|
+
if (!manifest.apiVersion) {
|
|
145
|
+
console.error('❌ Manifest validation failed: Missing apiVersion');
|
|
146
|
+
throw new Error('Manifest must have an apiVersion');
|
|
145
147
|
}
|
|
146
|
-
if (!manifest
|
|
147
|
-
|
|
148
|
+
if (!manifest.kind) {
|
|
149
|
+
console.error('❌ Manifest validation failed: Missing kind');
|
|
150
|
+
throw new Error('Manifest must have a kind');
|
|
148
151
|
}
|
|
149
|
-
if (!manifest
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
throw new Error('Kubernetes manifest must have valid metadata');
|
|
152
|
+
if (!manifest.metadata) {
|
|
153
|
+
console.error('❌ Manifest validation failed: Missing metadata');
|
|
154
|
+
throw new Error('Manifest must have metadata');
|
|
153
155
|
}
|
|
154
|
-
const metadata = manifest
|
|
155
|
-
if (!metadata
|
|
156
|
-
|
|
156
|
+
const metadata = manifest.metadata;
|
|
157
|
+
if (!metadata.name) {
|
|
158
|
+
console.error('❌ Manifest validation failed: Missing metadata.name');
|
|
159
|
+
throw new Error('Manifest metadata must have a name');
|
|
157
160
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
throw new Error('CustomResourceDefinition must have a valid spec');
|
|
162
|
-
}
|
|
163
|
-
const spec = manifest['spec'];
|
|
164
|
-
if (!spec['group'] || typeof spec['group'] !== 'string') {
|
|
165
|
-
throw new Error('CustomResourceDefinition spec must have a valid group');
|
|
166
|
-
}
|
|
167
|
-
if (!Array.isArray(spec['versions']) || spec['versions'].length === 0) {
|
|
168
|
-
throw new Error('CustomResourceDefinition must have at least one version in spec.versions');
|
|
169
|
-
}
|
|
161
|
+
if (typeof metadata.name !== 'string') {
|
|
162
|
+
console.error('❌ Manifest validation failed: metadata.name must be a string');
|
|
163
|
+
throw new Error('Manifest metadata.name must be a string');
|
|
170
164
|
}
|
|
171
165
|
}
|
|
172
166
|
getMeta() {
|
|
@@ -182,7 +176,7 @@ export class Rutter {
|
|
|
182
176
|
return [...this.assets];
|
|
183
177
|
}
|
|
184
178
|
toSynthArray() {
|
|
185
|
-
console.log(
|
|
179
|
+
console.log(`📦 Synthesizing chart assets for: ${this.meta.name}`);
|
|
186
180
|
const apiObjectIds = [];
|
|
187
181
|
for (const child of this.chart.node.children) {
|
|
188
182
|
if (child instanceof ApiObject && !child.node.id.endsWith('-placeholder')) {
|
|
@@ -198,6 +192,7 @@ export class Rutter {
|
|
|
198
192
|
}
|
|
199
193
|
return true;
|
|
200
194
|
});
|
|
195
|
+
console.log(`📋 Found ${manifestObjs.length} manifest objects to process`);
|
|
201
196
|
const enriched = manifestObjs.map((obj) => {
|
|
202
197
|
if (obj && typeof obj === 'object') {
|
|
203
198
|
const o = obj;
|
|
@@ -205,12 +200,12 @@ export class Rutter {
|
|
|
205
200
|
o.metadata.labels = o.metadata.labels ?? {};
|
|
206
201
|
const labels = o.metadata.labels;
|
|
207
202
|
const defaults = {
|
|
208
|
-
'helm.sh/chart':
|
|
209
|
-
'app.kubernetes.io/name': include
|
|
210
|
-
'app.kubernetes.io/instance':
|
|
211
|
-
'app.kubernetes.io/version':
|
|
203
|
+
'helm.sh/chart': `{{ .Chart.Name }}-{{ .Chart.Version }}`,
|
|
204
|
+
'app.kubernetes.io/name': `{{ ${include} "${Rutter.HELPER_NAME}" . }}`,
|
|
205
|
+
'app.kubernetes.io/instance': '{{ .Release.Name }}',
|
|
206
|
+
'app.kubernetes.io/version': '{{ .Chart.Version }}',
|
|
212
207
|
'app.kubernetes.io/managed-by': '{{ .Release.Service }}',
|
|
213
|
-
'app.kubernetes.io/part-of':
|
|
208
|
+
'app.kubernetes.io/part-of': '{{ .Chart.Name }}',
|
|
214
209
|
};
|
|
215
210
|
for (const [key, value] of Object.entries(defaults)) {
|
|
216
211
|
if (!(key in labels)) {
|
|
@@ -221,7 +216,6 @@ export class Rutter {
|
|
|
221
216
|
return obj;
|
|
222
217
|
});
|
|
223
218
|
const synthAssets = [];
|
|
224
|
-
console.log('🔍 singleManifestFile:', this.props.singleManifestFile);
|
|
225
219
|
if (this.props.singleManifestFile) {
|
|
226
220
|
const combinedYaml = enriched
|
|
227
221
|
.map((obj) => this.processHelmTemplates(YAML.stringify(obj).trim()))
|
|
@@ -234,7 +228,6 @@ export class Rutter {
|
|
|
234
228
|
enriched.forEach((obj, index) => {
|
|
235
229
|
const apiObjectId = apiObjectIds[index];
|
|
236
230
|
const manifestId = apiObjectId || `manifest-${index + 1}`;
|
|
237
|
-
console.log(`🔍 Processing asset ${manifestId} (index ${index})`);
|
|
238
231
|
const yaml = this.processHelmTemplates(YAML.stringify(obj).trim());
|
|
239
232
|
if (yaml) {
|
|
240
233
|
synthAssets.push({ id: manifestId, yaml });
|
|
@@ -247,15 +240,8 @@ export class Rutter {
|
|
|
247
240
|
return synthAssets;
|
|
248
241
|
}
|
|
249
242
|
processHelmTemplates(yaml) {
|
|
250
|
-
console.log('🔍 processHelmTemplates called with YAML length:', yaml.length);
|
|
251
|
-
if (yaml.includes('{{ .Values.port }}')) {
|
|
252
|
-
console.log('🎯 Found YAML with port template:');
|
|
253
|
-
const portIndex = yaml.indexOf('{{ .Values.port }}');
|
|
254
|
-
console.log(yaml.substring(Math.max(0, portIndex - 50), portIndex + 100));
|
|
255
|
-
}
|
|
256
243
|
let processed = this.fixCharacterMappingIssues(yaml);
|
|
257
244
|
processed = this.applyHelmTemplateReplacements(processed);
|
|
258
|
-
console.log('✅ processHelmTemplates completed');
|
|
259
245
|
return processed;
|
|
260
246
|
}
|
|
261
247
|
fixCharacterMappingIssues(yaml) {
|
|
@@ -317,38 +303,11 @@ export class Rutter {
|
|
|
317
303
|
return { nextIndex: j };
|
|
318
304
|
}
|
|
319
305
|
applyHelmTemplateReplacements(processed) {
|
|
320
|
-
processed = this.fixIncludeStatements(processed);
|
|
321
|
-
processed = this.fixFunctionCalls(processed);
|
|
322
|
-
processed = this.fixChartReferences(processed);
|
|
323
|
-
processed = this.removeHelmExpressionQuotes(processed);
|
|
324
|
-
if (processed.includes('number:')) {
|
|
325
|
-
console.log('DEBUG: Before fixConditionalBlocks:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
326
|
-
}
|
|
327
306
|
processed = this.fixConditionalBlocks(processed);
|
|
328
|
-
if (processed.includes('number:')) {
|
|
329
|
-
console.log('DEBUG: After fixConditionalBlocks:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
330
|
-
}
|
|
331
|
-
if (processed.includes('number:')) {
|
|
332
|
-
console.log('DEBUG: Before fixPipeExpressions:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
333
|
-
}
|
|
334
307
|
processed = this.fixPipeExpressions(processed);
|
|
335
|
-
if (processed.includes('number:')) {
|
|
336
|
-
console.log('DEBUG: After fixPipeExpressions:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
337
|
-
}
|
|
338
|
-
if (processed.includes('number:')) {
|
|
339
|
-
console.log('DEBUG: Before fixNestedQuotes:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
340
|
-
}
|
|
341
308
|
processed = this.fixNestedQuotes(processed);
|
|
342
|
-
if (processed.includes('number:')) {
|
|
343
|
-
console.log('DEBUG: After fixNestedQuotes:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
344
|
-
}
|
|
345
|
-
if (processed.includes('number:')) {
|
|
346
|
-
console.log('DEBUG: Before cleanupArtifacts:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
347
|
-
}
|
|
348
309
|
processed = this.cleanupArtifacts(processed);
|
|
349
|
-
|
|
350
|
-
console.log('DEBUG: After cleanupArtifacts:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
351
|
-
}
|
|
310
|
+
processed = this.removeHelmExpressionQuotes(processed);
|
|
352
311
|
return processed;
|
|
353
312
|
}
|
|
354
313
|
fixIncludeStatements(processed) {
|
|
@@ -364,10 +323,6 @@ export class Rutter {
|
|
|
364
323
|
}
|
|
365
324
|
removeHelmExpressionQuotes(processed) {
|
|
366
325
|
const HELM_EXPRESSION_REPLACEMENT = '$1$2: {{$3}}';
|
|
367
|
-
console.log('🔧 removeHelmExpressionQuotes called');
|
|
368
|
-
if (processed.includes('number:')) {
|
|
369
|
-
console.log('DEBUG: removeHelmExpressionQuotes input contains number:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
370
|
-
}
|
|
371
326
|
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\|\s*(?:int|float|bool|number)[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
372
327
|
processed = processed.replace(/^(\s*)(port):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
373
328
|
processed = processed.replace(/^(\s*)(port\.number):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
@@ -375,23 +330,7 @@ export class Rutter {
|
|
|
375
330
|
processed = processed.replace(/^(\s*)(backend\.service\.port\.number):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
376
331
|
processed = processed.replace(/^(\s*)(spec\.port):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
377
332
|
processed = processed.replace(/^(\s*)([^:\s]*\.number):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
378
|
-
const numberMatches = processed.match(/^(\s*)(number):\s*"\{\{([^}]*)\}\}"/gm);
|
|
379
|
-
if (numberMatches) {
|
|
380
|
-
console.log('DEBUG: Found number pattern matches:', numberMatches);
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
383
|
-
const anyNumberFields = processed.match(/number:\s*"[^"]*"/gm);
|
|
384
|
-
if (anyNumberFields) {
|
|
385
|
-
console.log('DEBUG: Found number fields but regex did not match:', anyNumberFields);
|
|
386
|
-
const lines = processed.split('\n');
|
|
387
|
-
const numberLines = lines.filter((line) => line.includes('number:'));
|
|
388
|
-
console.log('DEBUG: Number field lines:', numberLines);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
333
|
processed = processed.replace(/^(\s*)(number):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
392
|
-
if (processed.includes('number:')) {
|
|
393
|
-
console.log('DEBUG: removeHelmExpressionQuotes after number replacement:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
394
|
-
}
|
|
395
334
|
processed = processed.replace(/^(\s*)(replicas):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
396
335
|
processed = processed.replace(/^(\s*)(targetPort):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
397
336
|
processed = processed.replace(/^(\s*)(nodePort):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
@@ -468,8 +407,7 @@ export class Rutter {
|
|
|
468
407
|
return processed;
|
|
469
408
|
}
|
|
470
409
|
write(outDir) {
|
|
471
|
-
console.log(
|
|
472
|
-
console.log('📊 Assets count:', this.assets.length);
|
|
410
|
+
console.log(`✍️ Writing Helm chart '${this.meta.name}' to: ${outDir}`);
|
|
473
411
|
let helpersContent;
|
|
474
412
|
if (this.props.helpersTpl) {
|
|
475
413
|
if (typeof this.props.helpersTpl === 'string') {
|
|
@@ -490,7 +428,7 @@ ${helper.template}
|
|
|
490
428
|
helpersContent = generateHelpersTemplate(this.props.cloudProvider);
|
|
491
429
|
}
|
|
492
430
|
const synthAssets = this.toSynthArray();
|
|
493
|
-
console.log(
|
|
431
|
+
console.log(`📄 Generated ${synthAssets.length} assets for chart`);
|
|
494
432
|
HelmChartWriter.write({
|
|
495
433
|
outDir,
|
|
496
434
|
meta: this.meta,
|
|
@@ -499,7 +437,7 @@ ${helper.template}
|
|
|
499
437
|
assets: synthAssets,
|
|
500
438
|
helpersTpl: helpersContent,
|
|
501
439
|
});
|
|
502
|
-
console.log('
|
|
440
|
+
console.log(`✅ Helm chart '${this.meta.name}' written successfully`);
|
|
503
441
|
}
|
|
504
442
|
}
|
|
505
443
|
Rutter.HELPER_NAME = 'chart.name';
|
|
@@ -184,10 +184,7 @@ spec:
|
|
|
184
184
|
return this.rutter;
|
|
185
185
|
}
|
|
186
186
|
writeHelmChart(outDir) {
|
|
187
|
-
console.log('🔧 Subchart.writeHelmChart called with outDir:', outDir);
|
|
188
|
-
console.log('🔧 Rutter instance exists:', !!this.rutter);
|
|
189
187
|
this.rutter.write(outDir);
|
|
190
|
-
console.log('🔧 Subchart.writeHelmChart completed');
|
|
191
188
|
}
|
|
192
189
|
}
|
|
193
190
|
export function generateSubchart(props = {}, outDir = 'dist') {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "timonel",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.9.
|
|
4
|
+
"version": "2.9.1",
|
|
5
5
|
"description": "Timonel: programmatic Helm chart generator using cdk8s (TypeScript)",
|
|
6
6
|
"bin": {
|
|
7
7
|
"timonel": "dist/cli.js",
|
|
@@ -37,13 +37,9 @@
|
|
|
37
37
|
"deps:update": "pnpm update --latest",
|
|
38
38
|
"md:lint": "markdownlint \"**/*.md\"",
|
|
39
39
|
"md:fix": "markdownlint --fix \"**/*.md\"",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"_test:integration": "vitest run --config vitest.integration.config.ts",
|
|
44
|
-
"_test:watch": "vitest",
|
|
45
|
-
"_test:coverage": "vitest run --coverage",
|
|
46
|
-
"_test:basic-integration": "vitest run tests/integration/basic-chart-generation.int.spec.ts --config vitest.integration.config.ts",
|
|
40
|
+
"test:integration": "vitest run --config vitest.config.ts",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"test:coverage": "vitest run --coverage",
|
|
47
43
|
"ci:check": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm build",
|
|
48
44
|
"release": "semantic-release",
|
|
49
45
|
"release:dry": "semantic-release --dry-run",
|
|
@@ -89,6 +85,7 @@
|
|
|
89
85
|
"cdk8s-plus-33": "^2.3.5",
|
|
90
86
|
"constructs": "^10.4.2",
|
|
91
87
|
"handlebars": "^4.7.8",
|
|
88
|
+
"timonel": "^2.9.0",
|
|
92
89
|
"ts-node": "^10.9.2",
|
|
93
90
|
"yaml": "^2.8.1"
|
|
94
91
|
},
|