timonel 2.8.3 → 2.9.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 +6 -0
- package/dist/cli.js +255 -679
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/lib/helmChartWriter.js +27 -0
- package/dist/lib/rutter.d.ts +28 -16
- package/dist/lib/rutter.js +283 -19
- 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 +224 -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 +9 -7
- package/README.md +0 -74
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,10 @@ export * from './lib/helmChartWriter.js';
|
|
|
3
3
|
export * from './lib/rutter.js';
|
|
4
4
|
export * from './lib/security.js';
|
|
5
5
|
export * from './lib/umbrella.js';
|
|
6
|
+
export { UmbrellaChartTemplate as UmbrellaChart } from './lib/templates/umbrella-chart.js';
|
|
7
|
+
export { Subchart } from './lib/templates/subchart.js';
|
|
8
|
+
export { BasicChart } from './lib/templates/basic-chart.js';
|
|
6
9
|
export type { AWSALBIngressSpec, AWSEBSStorageClassSpec, AWSEFSStorageClassSpec, AWSIRSAServiceAccountSpec, } from './lib/resources/cloud/aws/awsResources.js';
|
|
7
10
|
export type { KarpenterDisruption, KarpenterDisruptionBudget, KarpenterEC2NodeClassSpec, KarpenterNodeClaimSpec, KarpenterNodePoolSpec, } from './lib/resources/cloud/aws/karpenterResources.js';
|
|
8
11
|
export { DEFAULT_TERMINATION_GRACE_PERIOD, isValidDisruptionBudget, isValidKubernetesDuration, KarpenterVersionUtils, } from './lib/resources/cloud/aws/karpenterResources.js';
|
|
9
|
-
export { AWS_HELPERS, formatHelpers, generateHelpersTemplate, getDefaultHelpers, STANDARD_HELPERS, type HelperDefinition, } from './lib/utils/helmHelpers.js';
|
|
12
|
+
export { AWS_HELPERS, createHelper, FILE_ACCESS_HELPERS, formatHelpers, generateHelpersTemplate, getDefaultHelpers, getHelpersByCategory, KUBERNETES_HELPERS, SPRIG_HELPERS, STANDARD_HELPERS, TEMPLATE_FUNCTION_HELPERS, type HelperDefinition, } from './lib/utils/helmHelpers.js';
|
package/dist/index.js
CHANGED
|
@@ -3,5 +3,8 @@ export * from './lib/helmChartWriter.js';
|
|
|
3
3
|
export * from './lib/rutter.js';
|
|
4
4
|
export * from './lib/security.js';
|
|
5
5
|
export * from './lib/umbrella.js';
|
|
6
|
+
export { UmbrellaChartTemplate as UmbrellaChart } from './lib/templates/umbrella-chart.js';
|
|
7
|
+
export { Subchart } from './lib/templates/subchart.js';
|
|
8
|
+
export { BasicChart } from './lib/templates/basic-chart.js';
|
|
6
9
|
export { DEFAULT_TERMINATION_GRACE_PERIOD, isValidDisruptionBudget, isValidKubernetesDuration, KarpenterVersionUtils, } from './lib/resources/cloud/aws/karpenterResources.js';
|
|
7
|
-
export { AWS_HELPERS, formatHelpers, generateHelpersTemplate, getDefaultHelpers, STANDARD_HELPERS, } from './lib/utils/helmHelpers.js';
|
|
10
|
+
export { AWS_HELPERS, createHelper, FILE_ACCESS_HELPERS, formatHelpers, generateHelpersTemplate, getDefaultHelpers, getHelpersByCategory, KUBERNETES_HELPERS, SPRIG_HELPERS, STANDARD_HELPERS, TEMPLATE_FUNCTION_HELPERS, } from './lib/utils/helmHelpers.js';
|
|
@@ -6,14 +6,20 @@ export class HelmChartWriter {
|
|
|
6
6
|
static write(opts) {
|
|
7
7
|
const { outDir, meta, defaultValues = {}, envValues = {}, assets, helpersTpl, notesTpl, valuesSchema, } = opts;
|
|
8
8
|
const validatedOutDir = SecurityUtils.validatePath(outDir, process.cwd());
|
|
9
|
+
console.log('📝 Validated outDir:', validatedOutDir);
|
|
9
10
|
this.createDirectories(validatedOutDir);
|
|
11
|
+
console.log('📝 Directories created');
|
|
10
12
|
this.writeChartYaml(validatedOutDir, meta);
|
|
13
|
+
console.log('📝 Chart.yaml written');
|
|
11
14
|
this.writeValuesFiles(validatedOutDir, defaultValues, envValues);
|
|
15
|
+
console.log('📝 Values files written');
|
|
12
16
|
this.writeAssets(validatedOutDir, assets);
|
|
17
|
+
console.log('📝 Assets written');
|
|
13
18
|
this.writeHelpers(validatedOutDir, helpersTpl);
|
|
14
19
|
this.writeNotes(validatedOutDir, notesTpl);
|
|
15
20
|
this.writeSchema(validatedOutDir, valuesSchema);
|
|
16
21
|
this.writeHelmIgnore(validatedOutDir);
|
|
22
|
+
console.log('📝 HelmChartWriter.write completed');
|
|
17
23
|
}
|
|
18
24
|
static createDirectories(outDir) {
|
|
19
25
|
fs.mkdirSync(path.join(outDir, 'templates'), { recursive: true });
|
|
@@ -106,12 +112,20 @@ function splitDocs(yamlStr) {
|
|
|
106
112
|
.filter((p) => p.length);
|
|
107
113
|
}
|
|
108
114
|
function writeAssets(outDir, assets) {
|
|
115
|
+
console.log('🔍 writeAssets called with', assets.length, 'assets');
|
|
109
116
|
for (const asset of assets) {
|
|
110
117
|
const sanitizedId = asset.id.replace(/[^a-zA-Z0-9-_]/g, '');
|
|
111
118
|
if (sanitizedId !== asset.id) {
|
|
112
119
|
throw new Error(`Invalid asset ID: ${SecurityUtils.sanitizeLogMessage(asset.id)}`);
|
|
113
120
|
}
|
|
114
121
|
const targetDir = getTargetDirectory(asset.target);
|
|
122
|
+
console.log(`🔍 Processing asset: ${asset.id} -> ${sanitizedId}, singleFile: ${asset.singleFile}, target: ${asset.target}`);
|
|
123
|
+
if (asset.id === 'ingress') {
|
|
124
|
+
console.log('🔍 Ingress asset YAML preview:', asset.yaml.substring(0, 200));
|
|
125
|
+
if (asset.yaml.includes('number:')) {
|
|
126
|
+
console.log('🔍 Ingress contains number field:', asset.yaml.split('\n').filter((line) => line.includes('number:')));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
115
129
|
if (asset.singleFile) {
|
|
116
130
|
writeSingleAssetFile(outDir, targetDir, sanitizedId, asset.yaml);
|
|
117
131
|
}
|
|
@@ -124,14 +138,27 @@ function getTargetDirectory(target) {
|
|
|
124
138
|
return target === 'crds' ? 'crds' : 'templates';
|
|
125
139
|
}
|
|
126
140
|
function writeSingleAssetFile(outDir, targetDir, assetId, yaml) {
|
|
141
|
+
if (assetId === 'ingress' && yaml.includes('number:')) {
|
|
142
|
+
console.log('🔍 Writing ingress file with content:');
|
|
143
|
+
const numberLines = yaml.split('\n').filter((line) => line.includes('number:'));
|
|
144
|
+
console.log('Number lines:', numberLines);
|
|
145
|
+
}
|
|
127
146
|
const filename = `${assetId}.yaml`;
|
|
128
147
|
fs.mkdirSync(path.join(outDir, targetDir), { recursive: true });
|
|
129
148
|
fs.writeFileSync(path.join(outDir, targetDir, filename), yaml + '\n');
|
|
130
149
|
}
|
|
131
150
|
function writeMultipleAssetFiles(outDir, targetDir, assetId, yaml) {
|
|
151
|
+
if (assetId === 'ingress' && yaml.includes('number:')) {
|
|
152
|
+
console.log('🔍 Writing ingress file (multiple) with content:');
|
|
153
|
+
const numberLines = yaml.split('\n').filter((line) => line.includes('number:'));
|
|
154
|
+
console.log('Number lines:', numberLines);
|
|
155
|
+
}
|
|
132
156
|
const parts = splitDocs(yaml);
|
|
133
157
|
parts.forEach((doc, index) => {
|
|
134
158
|
const filename = `${assetId}${parts.length > 1 ? `-${index + 1}` : ''}.yaml`;
|
|
159
|
+
if (assetId === 'ingress' && doc.includes('number:')) {
|
|
160
|
+
console.log(`🔍 Writing document ${index + 1} for ingress:`, doc.split('\n').filter((line) => line.includes('number:')));
|
|
161
|
+
}
|
|
135
162
|
fs.mkdirSync(path.join(outDir, targetDir), { recursive: true });
|
|
136
163
|
fs.writeFileSync(path.join(outDir, targetDir, filename), doc + '\n');
|
|
137
164
|
});
|
package/dist/lib/rutter.d.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { ApiObject } from 'cdk8s';
|
|
2
2
|
import type { ChartProps } from 'cdk8s';
|
|
3
|
+
import type { Ingress, ServiceAccount } from 'cdk8s-plus-33';
|
|
3
4
|
import type { Construct } from 'constructs';
|
|
4
|
-
import type { ServiceAccount, Ingress } from 'cdk8s-plus-33';
|
|
5
5
|
import type { AWSALBIngressSpec, AWSEBSStorageClassSpec, AWSECRServiceAccountSpec, AWSEFSStorageClassSpec, AWSIRSAServiceAccountSpec } from './resources/cloud/aws/awsResources.js';
|
|
6
6
|
import type { KarpenterEC2NodeClassSpec, KarpenterNodeClaimSpec, KarpenterNodePoolSpec } from './resources/cloud/aws/karpenterResources.js';
|
|
7
7
|
import type { HelperDefinition } from './utils/helmHelpers.js';
|
|
8
8
|
export declare class Rutter {
|
|
9
9
|
private static readonly HELPER_NAME;
|
|
10
10
|
private readonly app;
|
|
11
|
-
private readonly
|
|
11
|
+
private readonly assets;
|
|
12
12
|
private readonly awsResources;
|
|
13
|
-
private readonly
|
|
14
|
-
private readonly meta;
|
|
13
|
+
private readonly chart;
|
|
15
14
|
private readonly defaultValues;
|
|
16
15
|
private readonly envValues;
|
|
16
|
+
private readonly karpenterResources;
|
|
17
|
+
private readonly meta;
|
|
17
18
|
private readonly props;
|
|
18
|
-
private readonly assets;
|
|
19
19
|
constructor(props: RutterProps);
|
|
20
20
|
addAWSEBSStorageClass(spec: AWSEBSStorageClassSpec): ApiObject;
|
|
21
21
|
addAWSEFSStorageClass(spec: AWSEFSStorageClassSpec): ApiObject;
|
|
@@ -79,6 +79,7 @@ export declare class Rutter {
|
|
|
79
79
|
}): ApiObject;
|
|
80
80
|
addAWSECRServiceAccount(spec: AWSECRServiceAccountSpec): ServiceAccount;
|
|
81
81
|
addManifest(yamlOrObject: string | Record<string, unknown>, id: string): ApiObject;
|
|
82
|
+
addTemplateManifest(yamlTemplate: string, id: string): ApiObject;
|
|
82
83
|
addConditionalManifest(manifestObject: Record<string, unknown>, condition: string, id: string): ApiObject;
|
|
83
84
|
private validateManifestStructure;
|
|
84
85
|
getMeta(): ChartMetadata;
|
|
@@ -91,31 +92,42 @@ export declare class Rutter {
|
|
|
91
92
|
}>;
|
|
92
93
|
private toSynthArray;
|
|
93
94
|
private processHelmTemplates;
|
|
95
|
+
private fixCharacterMappingIssues;
|
|
96
|
+
private processCharacterMapping;
|
|
97
|
+
private applyHelmTemplateReplacements;
|
|
98
|
+
private fixIncludeStatements;
|
|
99
|
+
private fixFunctionCalls;
|
|
100
|
+
private fixChartReferences;
|
|
101
|
+
private removeHelmExpressionQuotes;
|
|
102
|
+
private fixConditionalBlocks;
|
|
103
|
+
private fixPipeExpressions;
|
|
104
|
+
private fixNestedQuotes;
|
|
105
|
+
private cleanupArtifacts;
|
|
94
106
|
write(outDir: string): void;
|
|
95
107
|
}
|
|
96
108
|
export interface ChartMetadata {
|
|
97
|
-
name: string;
|
|
98
|
-
version: string;
|
|
99
109
|
description?: string;
|
|
100
|
-
keywords?: string[];
|
|
101
110
|
home?: string;
|
|
102
|
-
|
|
111
|
+
keywords?: string[];
|
|
103
112
|
maintainers?: Array<{
|
|
104
|
-
name: string;
|
|
105
113
|
email?: string;
|
|
114
|
+
name: string;
|
|
106
115
|
url?: string;
|
|
107
116
|
}>;
|
|
117
|
+
name: string;
|
|
118
|
+
sources?: string[];
|
|
119
|
+
version: string;
|
|
108
120
|
}
|
|
109
121
|
export interface RutterProps {
|
|
110
|
-
|
|
111
|
-
|
|
122
|
+
chartProps?: ChartProps;
|
|
123
|
+
cloudProvider?: 'aws';
|
|
112
124
|
defaultValues?: Record<string, unknown>;
|
|
113
125
|
envValues?: Record<string, Record<string, unknown>>;
|
|
114
|
-
namespace?: string;
|
|
115
|
-
chartProps?: ChartProps;
|
|
116
126
|
helpersTpl?: string | HelperDefinition[];
|
|
117
|
-
cloudProvider?: 'aws';
|
|
118
127
|
manifestPrefix?: string;
|
|
128
|
+
meta: ChartMetadata;
|
|
129
|
+
namespace?: string;
|
|
130
|
+
scope?: Construct;
|
|
119
131
|
singleManifestFile?: boolean;
|
|
120
132
|
}
|
|
121
|
-
export type { AWSEBSStorageClassSpec, AWSEFSStorageClassSpec, AWSIRSAServiceAccountSpec,
|
|
133
|
+
export type { AWSALBIngressSpec, AWSEBSStorageClassSpec, AWSEFSStorageClassSpec, AWSIRSAServiceAccountSpec, } from './resources/cloud/aws/awsResources.js';
|
package/dist/lib/rutter.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiObject, App, Chart, Testing } from 'cdk8s';
|
|
2
|
+
import Handlebars from 'handlebars';
|
|
2
3
|
import YAML from 'yaml';
|
|
3
4
|
import { helm, include } from './helm.js';
|
|
4
5
|
import { HelmChartWriter } from './helmChartWriter.js';
|
|
@@ -8,10 +9,10 @@ import { generateHelpersTemplate } from './utils/helmHelpers.js';
|
|
|
8
9
|
export class Rutter {
|
|
9
10
|
constructor(props) {
|
|
10
11
|
this.assets = [];
|
|
11
|
-
this.props = props;
|
|
12
|
-
this.meta = props.meta;
|
|
13
12
|
this.defaultValues = props.defaultValues ?? {};
|
|
14
13
|
this.envValues = props.envValues ?? {};
|
|
14
|
+
this.meta = props.meta;
|
|
15
|
+
this.props = props;
|
|
15
16
|
this.app = new App();
|
|
16
17
|
this.chart = new Chart(this.app, props.meta.name, {
|
|
17
18
|
...props.chartProps,
|
|
@@ -74,6 +75,19 @@ export class Rutter {
|
|
|
74
75
|
spec: manifestObject['spec'],
|
|
75
76
|
});
|
|
76
77
|
}
|
|
78
|
+
addTemplateManifest(yamlTemplate, id) {
|
|
79
|
+
const templateAsset = {
|
|
80
|
+
id,
|
|
81
|
+
yaml: yamlTemplate,
|
|
82
|
+
target: 'templates',
|
|
83
|
+
};
|
|
84
|
+
this.assets.push(templateAsset);
|
|
85
|
+
return new ApiObject(this.chart, id, {
|
|
86
|
+
apiVersion: 'v1',
|
|
87
|
+
kind: 'ConfigMap',
|
|
88
|
+
metadata: { name: id },
|
|
89
|
+
});
|
|
90
|
+
}
|
|
77
91
|
addConditionalManifest(manifestObject, condition, id) {
|
|
78
92
|
if (!condition || typeof condition !== 'string') {
|
|
79
93
|
throw new Error('Condition must be a non-empty string');
|
|
@@ -86,13 +100,32 @@ export class Rutter {
|
|
|
86
100
|
else {
|
|
87
101
|
helmCondition = `.Values.${condition}`;
|
|
88
102
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
try {
|
|
104
|
+
const yamlContent = YAML.stringify(manifestObject, {
|
|
105
|
+
lineWidth: 0,
|
|
106
|
+
doubleQuotedAsJSON: false,
|
|
107
|
+
simpleKeys: true,
|
|
108
|
+
});
|
|
109
|
+
const conditionalTemplate = `\\{{- if ${helmCondition} }}
|
|
110
|
+
{{{manifestYaml}}}
|
|
111
|
+
\\{{- end }}`;
|
|
112
|
+
const template = Handlebars.compile(conditionalTemplate, {
|
|
113
|
+
noEscape: true,
|
|
114
|
+
});
|
|
115
|
+
const conditionalYaml = template({
|
|
116
|
+
manifestYaml: yamlContent.trim(),
|
|
117
|
+
});
|
|
118
|
+
const conditionalAsset = {
|
|
119
|
+
id,
|
|
120
|
+
yaml: conditionalYaml,
|
|
121
|
+
target: 'templates',
|
|
122
|
+
};
|
|
123
|
+
this.assets.push(conditionalAsset);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
throw new Error(`Failed to compile Handlebars template for manifest '${id}': ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
127
|
+
}
|
|
128
|
+
return new ApiObject(this.chart, `${id}-placeholder`, {
|
|
96
129
|
apiVersion: manifestObject['apiVersion'],
|
|
97
130
|
kind: manifestObject['kind'],
|
|
98
131
|
metadata: {
|
|
@@ -100,6 +133,7 @@ export class Rutter {
|
|
|
100
133
|
annotations: {
|
|
101
134
|
...(manifestObject['metadata']?.['annotations'] || {}),
|
|
102
135
|
'timonel.sh/conditional': condition,
|
|
136
|
+
'timonel.sh/placeholder': 'true',
|
|
103
137
|
},
|
|
104
138
|
},
|
|
105
139
|
spec: manifestObject['spec'],
|
|
@@ -148,13 +182,22 @@ export class Rutter {
|
|
|
148
182
|
return [...this.assets];
|
|
149
183
|
}
|
|
150
184
|
toSynthArray() {
|
|
185
|
+
console.log('🔍 toSynthArray called');
|
|
151
186
|
const apiObjectIds = [];
|
|
152
187
|
for (const child of this.chart.node.children) {
|
|
153
|
-
if (child instanceof ApiObject) {
|
|
188
|
+
if (child instanceof ApiObject && !child.node.id.endsWith('-placeholder')) {
|
|
154
189
|
apiObjectIds.push(child.node.id);
|
|
155
190
|
}
|
|
156
191
|
}
|
|
157
|
-
const
|
|
192
|
+
const allManifestObjs = Testing.synth(this.chart);
|
|
193
|
+
const manifestObjs = allManifestObjs.filter((obj) => {
|
|
194
|
+
if (obj && typeof obj === 'object') {
|
|
195
|
+
const o = obj;
|
|
196
|
+
const annotations = o.metadata?.annotations || {};
|
|
197
|
+
return annotations['timonel.sh/placeholder'] !== 'true';
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
});
|
|
158
201
|
const enriched = manifestObjs.map((obj) => {
|
|
159
202
|
if (obj && typeof obj === 'object') {
|
|
160
203
|
const o = obj;
|
|
@@ -178,6 +221,7 @@ export class Rutter {
|
|
|
178
221
|
return obj;
|
|
179
222
|
});
|
|
180
223
|
const synthAssets = [];
|
|
224
|
+
console.log('🔍 singleManifestFile:', this.props.singleManifestFile);
|
|
181
225
|
if (this.props.singleManifestFile) {
|
|
182
226
|
const combinedYaml = enriched
|
|
183
227
|
.map((obj) => this.processHelmTemplates(YAML.stringify(obj).trim()))
|
|
@@ -188,10 +232,11 @@ export class Rutter {
|
|
|
188
232
|
}
|
|
189
233
|
else {
|
|
190
234
|
enriched.forEach((obj, index) => {
|
|
235
|
+
const apiObjectId = apiObjectIds[index];
|
|
236
|
+
const manifestId = apiObjectId || `manifest-${index + 1}`;
|
|
237
|
+
console.log(`🔍 Processing asset ${manifestId} (index ${index})`);
|
|
191
238
|
const yaml = this.processHelmTemplates(YAML.stringify(obj).trim());
|
|
192
239
|
if (yaml) {
|
|
193
|
-
const apiObjectId = apiObjectIds[index];
|
|
194
|
-
const manifestId = apiObjectId || `manifest-${index + 1}`;
|
|
195
240
|
synthAssets.push({ id: manifestId, yaml });
|
|
196
241
|
}
|
|
197
242
|
});
|
|
@@ -202,13 +247,229 @@ export class Rutter {
|
|
|
202
247
|
return synthAssets;
|
|
203
248
|
}
|
|
204
249
|
processHelmTemplates(yaml) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
.
|
|
208
|
-
.
|
|
209
|
-
.
|
|
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
|
+
let processed = this.fixCharacterMappingIssues(yaml);
|
|
257
|
+
processed = this.applyHelmTemplateReplacements(processed);
|
|
258
|
+
console.log('✅ processHelmTemplates completed');
|
|
259
|
+
return processed;
|
|
260
|
+
}
|
|
261
|
+
fixCharacterMappingIssues(yaml) {
|
|
262
|
+
const lines = yaml.split('\n');
|
|
263
|
+
const fixedLines = [];
|
|
264
|
+
let i = 0;
|
|
265
|
+
while (i < lines.length) {
|
|
266
|
+
const line = lines[i];
|
|
267
|
+
if (!line) {
|
|
268
|
+
i++;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const charKeyMatch = line.match(/^(\s*)"(\d+)":\s*(.+)$/);
|
|
272
|
+
if (charKeyMatch && charKeyMatch[2] && parseInt(charKeyMatch[2]) === 0) {
|
|
273
|
+
const indent = charKeyMatch[1] || '';
|
|
274
|
+
const result = this.processCharacterMapping(lines, i, indent);
|
|
275
|
+
if (result.reconstructed) {
|
|
276
|
+
fixedLines.push(result.reconstructed);
|
|
277
|
+
i = result.nextIndex;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (line) {
|
|
282
|
+
fixedLines.push(line);
|
|
283
|
+
}
|
|
284
|
+
i++;
|
|
285
|
+
}
|
|
286
|
+
return fixedLines.join('\n');
|
|
287
|
+
}
|
|
288
|
+
processCharacterMapping(lines, startIndex, indent) {
|
|
289
|
+
const charMappings = [];
|
|
290
|
+
let j = startIndex;
|
|
291
|
+
while (j < lines.length) {
|
|
292
|
+
const currentLine = lines[j];
|
|
293
|
+
if (!currentLine) {
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
const currentMatch = currentLine.match(/^(\s*)"(\d+)":\s*(.+)$/);
|
|
297
|
+
if (currentMatch && currentMatch[1] === indent && currentMatch[2] && currentMatch[3]) {
|
|
298
|
+
const index = parseInt(currentMatch[2]);
|
|
299
|
+
let char = currentMatch[3];
|
|
300
|
+
if (char && char.startsWith('"') && char.endsWith('"')) {
|
|
301
|
+
char = char.slice(1, -1);
|
|
302
|
+
}
|
|
303
|
+
charMappings.push({ index, char });
|
|
304
|
+
j++;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (charMappings.length > 3) {
|
|
311
|
+
charMappings.sort((a, b) => a.index - b.index);
|
|
312
|
+
const reconstructed = charMappings.map((m) => m.char).join('');
|
|
313
|
+
if (reconstructed.includes('{{') && reconstructed.includes('}}')) {
|
|
314
|
+
return { reconstructed: `${indent}${reconstructed}`, nextIndex: j };
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return { nextIndex: j };
|
|
318
|
+
}
|
|
319
|
+
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
|
+
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
|
+
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
|
+
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
|
+
processed = this.cleanupArtifacts(processed);
|
|
349
|
+
if (processed.includes('number:')) {
|
|
350
|
+
console.log('DEBUG: After cleanupArtifacts:', processed.split('\n').filter((line) => line.includes('number:')));
|
|
351
|
+
}
|
|
352
|
+
return processed;
|
|
353
|
+
}
|
|
354
|
+
fixIncludeStatements(processed) {
|
|
355
|
+
processed = processed.replace(/\{\{ include \\"([^"]+)\\" \. \| nindent (\d+) \}\}/g, '{{ include "$1" . | nindent $2 }}');
|
|
356
|
+
processed = processed.replace(/\{\{ include \\"([^"]+)\\" \. \}\}/g, '{{ include "$1" . }}');
|
|
357
|
+
return processed;
|
|
358
|
+
}
|
|
359
|
+
fixFunctionCalls(processed) {
|
|
360
|
+
return processed.replace(/\{\{ (\w+) \\"([^"]*)\\" ([^}]*) \}\}/g, '{{ $1 "$2" $3 }}');
|
|
361
|
+
}
|
|
362
|
+
fixChartReferences(processed) {
|
|
363
|
+
return processed.replace(/\{\{ \.Chart\.(\w+) \| replace \\"([^"]*)\\" \\"([^"]*)\\" ([^}]*) \}\}/g, '{{ .Chart.$1 | replace "$2" "$3" $4 }}');
|
|
364
|
+
}
|
|
365
|
+
removeHelmExpressionQuotes(processed) {
|
|
366
|
+
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
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\|\s*(?:int|float|bool|number)[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
372
|
+
processed = processed.replace(/^(\s*)(port):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
373
|
+
processed = processed.replace(/^(\s*)(port\.number):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
374
|
+
processed = processed.replace(/^(\s*)(service\.port):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
375
|
+
processed = processed.replace(/^(\s*)(backend\.service\.port\.number):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
376
|
+
processed = processed.replace(/^(\s*)(spec\.port):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
377
|
+
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
|
+
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
|
+
processed = processed.replace(/^(\s*)(replicas):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
396
|
+
processed = processed.replace(/^(\s*)(targetPort):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
397
|
+
processed = processed.replace(/^(\s*)(nodePort):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
398
|
+
processed = processed.replace(/^(\s*)(containerPort):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
399
|
+
processed = processed.replace(/^(\s*)(hostPort):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
400
|
+
processed = processed.replace(/^(\s*)(weight):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
401
|
+
processed = processed.replace(/^(\s*)(priority):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
402
|
+
processed = processed.replace(/^(\s*)(timeoutSeconds):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
403
|
+
processed = processed.replace(/^(\s*)(periodSeconds):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
404
|
+
processed = processed.replace(/^(\s*)(successThreshold):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
405
|
+
processed = processed.replace(/^(\s*)(failureThreshold):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
406
|
+
processed = processed.replace(/^(\s*)(initialDelaySeconds):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
407
|
+
processed = processed.replace(/^(\s*)(terminationGracePeriodSeconds):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
408
|
+
processed = processed.replace(/^(\s*)(activeDeadlineSeconds):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
409
|
+
processed = processed.replace(/^(\s*)(backoffLimit):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
410
|
+
processed = processed.replace(/^(\s*)(parallelism):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
411
|
+
processed = processed.replace(/^(\s*)(completions):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
412
|
+
processed = processed.replace(/^(\s*)(revisionHistoryLimit):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
413
|
+
processed = processed.replace(/^(\s*)(progressDeadlineSeconds):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
414
|
+
processed = processed.replace(/^(\s*)(minReadySeconds):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
415
|
+
processed = processed.replace(/^(\s*)(maxUnavailable):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
416
|
+
processed = processed.replace(/^(\s*)(maxSurge):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
417
|
+
processed = processed.replace(/^(\s*)(cpu):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
418
|
+
processed = processed.replace(/^(\s*)(memory):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
419
|
+
processed = processed.replace(/^(\s*)(enabled):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
420
|
+
processed = processed.replace(/^(\s*)(create):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
421
|
+
processed = processed.replace(/^(\s*)(allow):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
422
|
+
processed = processed.replace(/^(\s*)(disable):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
423
|
+
processed = processed.replace(/^(\s*)(force):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
424
|
+
processed = processed.replace(/^(\s*)(required):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
425
|
+
processed = processed.replace(/^(\s*)(optional):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
426
|
+
processed = processed.replace(/^(\s*)(readOnly):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
427
|
+
processed = processed.replace(/^(\s*)(runAsNonRoot):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
428
|
+
processed = processed.replace(/^(\s*)(privileged):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
429
|
+
processed = processed.replace(/^(\s*)(allowPrivilegeEscalation):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
430
|
+
processed = processed.replace(/^(\s*)(readOnlyRootFilesystem):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
431
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*true[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
432
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*false[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
433
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\d+[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
434
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\beq\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
435
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\bne\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
436
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\blt\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
437
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\ble\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
438
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\bgt\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
439
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\bge\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
440
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\band\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
441
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\bor\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
442
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*\bnot\b[^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
443
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*[+][^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
444
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*[-][^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
445
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*[*][^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
446
|
+
processed = processed.replace(/:\s*"([^"]*\{\{[^}]+\}\}[^"]*)"/g, ': $1');
|
|
447
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"(true|false)"$/gm, '$1$2: $3');
|
|
448
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"(-?\d+)"$/gm, '$1$2: $3');
|
|
449
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"(-?\d+\.\d+)"$/gm, '$1$2: $3');
|
|
450
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"(-?\d+\.?\d*[eE][+-]?\d+)"$/gm, '$1$2: $3');
|
|
451
|
+
processed = processed.replace(/^(\s*)([^:\s]+):\s*"\{\{([^}]*)\}\}"/gm, HELM_EXPRESSION_REPLACEMENT);
|
|
452
|
+
processed = processed.replace(/"\{\{([^}]*)\}\}"/g, '{{$1}}');
|
|
453
|
+
processed = processed.replace(/^(\s*)-\s*"\{\{([^}]*)\}\}"/gm, '$1- {{$2}}');
|
|
454
|
+
return processed;
|
|
455
|
+
}
|
|
456
|
+
fixConditionalBlocks(processed) {
|
|
457
|
+
return processed.replace(/"\{\{-\s*(if|with|range)([^}]*)\}\}([^"]*)\{\{-\s*end\s*\}\}"/g, '{{- $1$2}}$3{{- end }}');
|
|
458
|
+
}
|
|
459
|
+
fixPipeExpressions(processed) {
|
|
460
|
+
return processed.replace(/"\{\{([^}]*\|[^}]*)\}\}"/g, '{{$1}}');
|
|
461
|
+
}
|
|
462
|
+
fixNestedQuotes(processed) {
|
|
463
|
+
return processed.replace(/\{\{([^}]*)\\"([^"]*)\\"([^}]*)\}\}/g, '{{$1"$2"$3}}');
|
|
464
|
+
}
|
|
465
|
+
cleanupArtifacts(processed) {
|
|
466
|
+
processed = processed.replace(/": "\.nan"/g, ': .nan');
|
|
467
|
+
processed = processed.replace(/: \{\{([^}]+)\}\}$/gm, ': {{$1}}');
|
|
468
|
+
return processed;
|
|
210
469
|
}
|
|
211
470
|
write(outDir) {
|
|
471
|
+
console.log('🚀 Rutter.write called with outDir:', outDir);
|
|
472
|
+
console.log('📊 Assets count:', this.assets.length);
|
|
212
473
|
let helpersContent;
|
|
213
474
|
if (this.props.helpersTpl) {
|
|
214
475
|
if (typeof this.props.helpersTpl === 'string') {
|
|
@@ -228,14 +489,17 @@ ${helper.template}
|
|
|
228
489
|
else {
|
|
229
490
|
helpersContent = generateHelpersTemplate(this.props.cloudProvider);
|
|
230
491
|
}
|
|
492
|
+
const synthAssets = this.toSynthArray();
|
|
493
|
+
console.log('📦 Generated synth assets:', synthAssets.length);
|
|
231
494
|
HelmChartWriter.write({
|
|
232
495
|
outDir,
|
|
233
496
|
meta: this.meta,
|
|
234
497
|
defaultValues: this.defaultValues,
|
|
235
498
|
envValues: this.envValues,
|
|
236
|
-
assets:
|
|
499
|
+
assets: synthAssets,
|
|
237
500
|
helpersTpl: helpersContent,
|
|
238
501
|
});
|
|
502
|
+
console.log('✅ Rutter.write completed');
|
|
239
503
|
}
|
|
240
504
|
}
|
|
241
505
|
Rutter.HELPER_NAME = 'chart.name';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Chart } from 'cdk8s';
|
|
2
|
+
import type { ChartProps } from 'cdk8s';
|
|
3
|
+
import type { Construct } from 'constructs';
|
|
4
|
+
export interface BasicChartProps extends ChartProps {
|
|
5
|
+
appName?: string;
|
|
6
|
+
image?: string;
|
|
7
|
+
port?: number;
|
|
8
|
+
replicas?: number;
|
|
9
|
+
createNamespace?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class BasicChart extends Chart {
|
|
12
|
+
private rutter;
|
|
13
|
+
private props;
|
|
14
|
+
constructor(scope: Construct, id: string, props?: BasicChartProps);
|
|
15
|
+
private initializeRutter;
|
|
16
|
+
writeHelmChart(outDir: string): void;
|
|
17
|
+
private generateKubernetesManifests;
|
|
18
|
+
}
|
|
19
|
+
export declare function generateBasicChart(appName?: string): string;
|