timonel 3.1.1 → 3.1.2
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 +323 -695
- package/dist/cli.js +275 -15
- package/dist/index.d.ts +5 -1
- package/dist/index.js +12 -0
- package/dist/lib/helm.d.ts +471 -0
- package/dist/lib/helm.js +483 -0
- package/dist/lib/helmChartWriter.d.ts +157 -0
- package/dist/lib/helmChartWriter.js +171 -1
- package/dist/lib/policy/configurationLoader.d.ts +132 -0
- package/dist/lib/policy/configurationLoader.js +132 -0
- package/dist/lib/policy/errorContextGenerator.d.ts +89 -0
- package/dist/lib/policy/errorContextGenerator.js +99 -2
- package/dist/lib/policy/errors.d.ts +35 -0
- package/dist/lib/policy/errors.js +36 -0
- package/dist/lib/policy/index.d.ts +9 -0
- package/dist/lib/policy/index.js +16 -0
- package/dist/lib/policy/parallelExecutor.d.ts +90 -0
- package/dist/lib/policy/parallelExecutor.js +86 -3
- package/dist/lib/policy/pluginLoader.d.ts +92 -0
- package/dist/lib/policy/pluginLoader.js +92 -1
- package/dist/lib/policy/pluginRegistry.d.ts +54 -0
- package/dist/lib/policy/pluginRegistry.js +56 -0
- package/dist/lib/policy/policyEngine.d.ts +137 -0
- package/dist/lib/policy/policyEngine.js +191 -5
- package/dist/lib/policy/resultAggregator.d.ts +46 -0
- package/dist/lib/policy/resultAggregator.js +69 -1
- package/dist/lib/policy/resultFormatter.d.ts +88 -0
- package/dist/lib/policy/resultFormatter.js +101 -0
- package/dist/lib/policy/types.d.ts +136 -0
- package/dist/lib/policy/types.js +8 -0
- package/dist/lib/policy/validationCache.d.ts +146 -0
- package/dist/lib/policy/validationCache.js +142 -6
- package/dist/lib/resources/baseResourceProvider.d.ts +45 -0
- package/dist/lib/resources/baseResourceProvider.js +48 -1
- package/dist/lib/resources/cloud/aws/awsResources.d.ts +192 -0
- package/dist/lib/resources/cloud/aws/awsResources.js +163 -1
- package/dist/lib/resources/cloud/aws/karpenterResources.d.ts +131 -0
- package/dist/lib/resources/cloud/aws/karpenterResources.js +77 -0
- package/dist/lib/rutter.d.ts +381 -3
- package/dist/lib/rutter.js +439 -28
- package/dist/lib/security.d.ts +123 -0
- package/dist/lib/security.js +162 -4
- package/dist/lib/templates/flexible-subchart.d.ts +52 -0
- package/dist/lib/templates/flexible-subchart.js +70 -0
- package/dist/lib/templates/umbrella-chart.d.ts +27 -0
- package/dist/lib/templates/umbrella-chart.js +89 -0
- package/dist/lib/types.d.ts +26 -0
- package/dist/lib/umbrella.d.ts +23 -0
- package/dist/lib/umbrella.js +23 -0
- package/dist/lib/umbrellaRutter.d.ts +75 -0
- package/dist/lib/umbrellaRutter.js +82 -2
- package/dist/lib/utils/envVarsLoader.d.ts +49 -0
- package/dist/lib/utils/envVarsLoader.js +53 -0
- package/dist/lib/utils/helmConstructSerializer.d.ts +17 -0
- package/dist/lib/utils/helmConstructSerializer.js +22 -0
- package/dist/lib/utils/helmControlStructures.d.ts +194 -0
- package/dist/lib/utils/helmControlStructures.js +180 -0
- package/dist/lib/utils/helmHelpers/envHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/envHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/gitopsHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/gitopsHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/index.d.ts +74 -0
- package/dist/lib/utils/helmHelpers/index.js +85 -1
- package/dist/lib/utils/helmHelpers/observabilityHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/observabilityHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/types.d.ts +23 -0
- package/dist/lib/utils/helmHelpers/types.js +4 -0
- package/dist/lib/utils/helmHelpers/validationHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/validationHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers.d.ts +62 -0
- package/dist/lib/utils/helmHelpers.js +77 -0
- package/dist/lib/utils/helmYamlSerializer.d.ts +77 -0
- package/dist/lib/utils/helmYamlSerializer.js +398 -21
- package/dist/lib/utils/logger.d.ts +153 -0
- package/dist/lib/utils/logger.js +170 -2
- package/dist/lib/utils/valuesRef.d.ts +181 -50
- package/dist/lib/utils/valuesRef.js +168 -170
- package/dist/lib/validation/inputValidator.d.ts +45 -0
- package/dist/lib/validation/inputValidator.js +67 -2
- package/dist/types/index.d.ts +34 -0
- package/dist/types/index.js +3 -0
- package/package.json +31 -38
|
@@ -1,22 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Helm chart writer for generating complete Helm chart structures
|
|
3
|
+
* @since 2.8.0+
|
|
4
|
+
*/
|
|
1
5
|
import { type TimonelLogger } from './utils/logger.js';
|
|
2
6
|
import type { HelperDefinition as ExternalHelperDefinition } from './utils/helmHelpers/types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Helm template helper definition shared across helper modules.
|
|
9
|
+
* @since 2.8.0+ (unified with utils helper definitions in 2.12.2)
|
|
10
|
+
*/
|
|
3
11
|
export type HelperDefinition = ExternalHelperDefinition;
|
|
12
|
+
/**
|
|
13
|
+
* Metadata for a Helm chart
|
|
14
|
+
*
|
|
15
|
+
* Contains all the information needed for Chart.yaml file generation
|
|
16
|
+
* following Helm chart specification.
|
|
17
|
+
*
|
|
18
|
+
* @interface HelmChartMeta
|
|
19
|
+
* @since 2.8.0+
|
|
20
|
+
*/
|
|
4
21
|
export interface HelmChartMeta {
|
|
22
|
+
/** Chart name */
|
|
5
23
|
name: string;
|
|
24
|
+
/** SemVer chart version */
|
|
6
25
|
version: string;
|
|
26
|
+
/** Chart description */
|
|
7
27
|
description?: string;
|
|
28
|
+
/** Underlying application version */
|
|
8
29
|
appVersion?: string;
|
|
30
|
+
/** Chart type */
|
|
9
31
|
type?: 'application' | 'library';
|
|
32
|
+
/** Kubernetes version constraint */
|
|
10
33
|
kubeVersion?: string;
|
|
34
|
+
/** Chart keywords for searchability */
|
|
11
35
|
keywords?: string[];
|
|
36
|
+
/** Chart home page URL */
|
|
12
37
|
home?: string;
|
|
38
|
+
/** Source code URLs */
|
|
13
39
|
sources?: string[];
|
|
40
|
+
/** Chart maintainers */
|
|
14
41
|
maintainers?: {
|
|
15
42
|
name: string;
|
|
16
43
|
email?: string;
|
|
17
44
|
url?: string;
|
|
18
45
|
}[];
|
|
46
|
+
/** Chart icon URL */
|
|
19
47
|
icon?: string;
|
|
48
|
+
/** Chart dependencies */
|
|
20
49
|
dependencies?: Array<{
|
|
21
50
|
name: string;
|
|
22
51
|
version: string;
|
|
@@ -27,34 +56,162 @@ export interface HelmChartMeta {
|
|
|
27
56
|
importValues?: unknown;
|
|
28
57
|
}>;
|
|
29
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Environment-specific values mapping
|
|
61
|
+
*
|
|
62
|
+
* @interface EnvValuesMap
|
|
63
|
+
* @since 2.8.0+
|
|
64
|
+
*/
|
|
30
65
|
export interface EnvValuesMap {
|
|
31
66
|
[env: string]: Record<string, unknown>;
|
|
32
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Synthesized Kubernetes asset for Helm chart
|
|
70
|
+
*
|
|
71
|
+
* Represents a Kubernetes manifest that will be written to the chart's
|
|
72
|
+
* templates or crds directory.
|
|
73
|
+
*
|
|
74
|
+
* @interface SynthAsset
|
|
75
|
+
* @since 2.8.0+
|
|
76
|
+
*/
|
|
33
77
|
export interface SynthAsset {
|
|
78
|
+
/** Filename or logical identifier */
|
|
34
79
|
id: string;
|
|
80
|
+
/** YAML content, may contain multiple documents separated by --- */
|
|
35
81
|
yaml: string;
|
|
82
|
+
/** Target directory inside chart */
|
|
36
83
|
target?: 'templates' | 'crds';
|
|
84
|
+
/** If true, don't split into multiple files even with multiple docs */
|
|
37
85
|
singleFile?: boolean;
|
|
38
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Options for writing a complete Helm chart
|
|
89
|
+
*
|
|
90
|
+
* @interface HelmChartWriteOptions
|
|
91
|
+
* @since 2.8.0+
|
|
92
|
+
*/
|
|
39
93
|
export interface HelmChartWriteOptions {
|
|
94
|
+
/** Output directory path (e.g., dist/charts/my-chart) */
|
|
40
95
|
outDir: string;
|
|
96
|
+
/** Chart metadata */
|
|
41
97
|
meta: HelmChartMeta;
|
|
98
|
+
/** Default values for values.yaml */
|
|
42
99
|
defaultValues?: Record<string, unknown> | undefined;
|
|
100
|
+
/** Environment-specific values (e.g., { dev: {...}, prod: {...} }) */
|
|
43
101
|
envValues?: EnvValuesMap | undefined;
|
|
102
|
+
/** Kubernetes manifests to place under templates/ */
|
|
44
103
|
assets: SynthAsset[];
|
|
104
|
+
/** Helm helpers content for templates/_helpers.tpl */
|
|
45
105
|
helpersTpl?: string | HelperDefinition[];
|
|
106
|
+
/** NOTES.txt content under templates/ */
|
|
46
107
|
notesTpl?: string;
|
|
108
|
+
/** JSON schema for values validation */
|
|
47
109
|
valuesSchema?: Record<string, unknown>;
|
|
110
|
+
/**
|
|
111
|
+
* Custom logger instance
|
|
112
|
+
* @since 2.13.0
|
|
113
|
+
*/
|
|
48
114
|
logger?: TimonelLogger;
|
|
49
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Helm chart writer for generating complete chart structures
|
|
118
|
+
*
|
|
119
|
+
* This class provides static methods to generate a complete Helm chart
|
|
120
|
+
* including Chart.yaml, values.yaml, templates, and auxiliary files.
|
|
121
|
+
*
|
|
122
|
+
* @class HelmChartWriter
|
|
123
|
+
* @since 2.8.0+
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* HelmChartWriter.write({
|
|
128
|
+
* outDir: './dist/my-chart',
|
|
129
|
+
* meta: { name: 'my-app', version: '1.0.0' },
|
|
130
|
+
* assets: []
|
|
131
|
+
* });
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
50
134
|
export declare class HelmChartWriter {
|
|
135
|
+
/**
|
|
136
|
+
* Writes a complete Helm chart to the specified directory
|
|
137
|
+
*
|
|
138
|
+
* @param {HelmChartWriteOptions} opts - Chart writing options
|
|
139
|
+
* @throws {Error} If chart cannot be written
|
|
140
|
+
* @since 2.8.0+
|
|
141
|
+
*/
|
|
51
142
|
static write(opts: HelmChartWriteOptions): void;
|
|
143
|
+
/**
|
|
144
|
+
* Creates the necessary directory structure for the chart
|
|
145
|
+
*
|
|
146
|
+
* @private
|
|
147
|
+
* @param {string} outDir - Output directory path (must be pre-validated)
|
|
148
|
+
* @throws {Error} If directories cannot be created
|
|
149
|
+
* @since 2.8.0+
|
|
150
|
+
*/
|
|
52
151
|
private static createDirectories;
|
|
152
|
+
/**
|
|
153
|
+
* Writes the Chart.yaml file with chart metadata
|
|
154
|
+
*
|
|
155
|
+
* @private
|
|
156
|
+
* @param {string} outDir - Output directory path
|
|
157
|
+
* @param {HelmChartMeta} meta - Chart metadata
|
|
158
|
+
* @throws {Error} If Chart.yaml cannot be written
|
|
159
|
+
* @since 2.8.0+
|
|
160
|
+
*/
|
|
53
161
|
private static writeChartYaml;
|
|
162
|
+
/**
|
|
163
|
+
* Writes values.yaml and environment-specific values files
|
|
164
|
+
*
|
|
165
|
+
* @private
|
|
166
|
+
* @param {string} outDir - Output directory path
|
|
167
|
+
* @param {Record<string, unknown>} defaultValues - Default values
|
|
168
|
+
* @param {EnvValuesMap} envValues - Environment-specific values
|
|
169
|
+
* @throws {Error} If values files cannot be written
|
|
170
|
+
* @since 2.8.0+
|
|
171
|
+
*/
|
|
54
172
|
private static writeValuesFiles;
|
|
173
|
+
/**
|
|
174
|
+
* Writes Kubernetes manifest assets to templates directory
|
|
175
|
+
*
|
|
176
|
+
* @private
|
|
177
|
+
* @param {string} outDir - Output directory path
|
|
178
|
+
* @param {SynthAsset[]} assets - Kubernetes manifests to write
|
|
179
|
+
* @since 2.8.0+
|
|
180
|
+
*/
|
|
55
181
|
private static writeAssets;
|
|
182
|
+
/**
|
|
183
|
+
* Writes Helm template helpers to _helpers.tpl file
|
|
184
|
+
*
|
|
185
|
+
* @private
|
|
186
|
+
* @param {string} outDir - Output directory path
|
|
187
|
+
* @param {string | HelperDefinition[]} [helpersTpl] - Helpers content
|
|
188
|
+
* @since 2.8.0+
|
|
189
|
+
*/
|
|
56
190
|
private static writeHelpers;
|
|
191
|
+
/**
|
|
192
|
+
* Writes NOTES.txt file for post-install instructions
|
|
193
|
+
*
|
|
194
|
+
* @private
|
|
195
|
+
* @param {string} outDir - Output directory path
|
|
196
|
+
* @param {string} [notesTpl] - Notes content
|
|
197
|
+
* @since 2.8.0+
|
|
198
|
+
*/
|
|
57
199
|
private static writeNotes;
|
|
200
|
+
/**
|
|
201
|
+
* Writes values.schema.json for values validation
|
|
202
|
+
*
|
|
203
|
+
* @private
|
|
204
|
+
* @param {string} outDir - Output directory path
|
|
205
|
+
* @param {Record<string, unknown>} [valuesSchema] - JSON schema
|
|
206
|
+
* @since 2.8.0+
|
|
207
|
+
*/
|
|
58
208
|
private static writeSchema;
|
|
209
|
+
/**
|
|
210
|
+
* Writes .helmignore file with common ignore patterns
|
|
211
|
+
*
|
|
212
|
+
* @private
|
|
213
|
+
* @param {string} outDir - Output directory path
|
|
214
|
+
* @since 2.8.0+
|
|
215
|
+
*/
|
|
59
216
|
private static writeHelmIgnore;
|
|
60
217
|
}
|
|
@@ -1,9 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Helm chart writer for generating complete Helm chart structures
|
|
3
|
+
* @since 2.8.0+
|
|
4
|
+
*/
|
|
1
5
|
import * as fs from 'fs';
|
|
2
6
|
import * as path from 'path';
|
|
3
7
|
import { SecurityUtils } from './security.js';
|
|
4
8
|
import { createLogger } from './utils/logger.js';
|
|
5
9
|
import { dumpHelmAwareYaml, postProcessFieldConditionals } from './utils/helmYamlSerializer.js';
|
|
10
|
+
/**
|
|
11
|
+
* Helm chart writer for generating complete chart structures
|
|
12
|
+
*
|
|
13
|
+
* This class provides static methods to generate a complete Helm chart
|
|
14
|
+
* including Chart.yaml, values.yaml, templates, and auxiliary files.
|
|
15
|
+
*
|
|
16
|
+
* @class HelmChartWriter
|
|
17
|
+
* @since 2.8.0+
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* HelmChartWriter.write({
|
|
22
|
+
* outDir: './dist/my-chart',
|
|
23
|
+
* meta: { name: 'my-app', version: '1.0.0' },
|
|
24
|
+
* assets: []
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
6
28
|
export class HelmChartWriter {
|
|
29
|
+
/**
|
|
30
|
+
* Writes a complete Helm chart to the specified directory
|
|
31
|
+
*
|
|
32
|
+
* @param {HelmChartWriteOptions} opts - Chart writing options
|
|
33
|
+
* @throws {Error} If chart cannot be written
|
|
34
|
+
* @since 2.8.0+
|
|
35
|
+
*/
|
|
7
36
|
static write(opts) {
|
|
8
37
|
const { outDir, meta, defaultValues = {}, envValues = {}, assets, helpersTpl, notesTpl, valuesSchema, logger: customLogger, } = opts;
|
|
9
38
|
const logger = customLogger ?? createLogger('helm-chart-writer');
|
|
@@ -15,10 +44,13 @@ export class HelmChartWriter {
|
|
|
15
44
|
assetCount: assets.length,
|
|
16
45
|
operation: 'helm_write_start',
|
|
17
46
|
});
|
|
47
|
+
// Validate output directory path
|
|
18
48
|
const validatedOutDir = SecurityUtils.validatePath(outDir, process.cwd(), {
|
|
19
49
|
allowAbsolute: true,
|
|
20
50
|
});
|
|
51
|
+
// Create directory structure
|
|
21
52
|
this.createDirectories(validatedOutDir);
|
|
53
|
+
// Write chart files
|
|
22
54
|
this.writeChartYaml(validatedOutDir, meta);
|
|
23
55
|
this.writeValuesFiles(validatedOutDir, defaultValues, envValues);
|
|
24
56
|
this.writeAssets(validatedOutDir, assets);
|
|
@@ -32,12 +64,31 @@ export class HelmChartWriter {
|
|
|
32
64
|
outputDirectory: validatedOutDir,
|
|
33
65
|
operation: 'helm_write_complete',
|
|
34
66
|
});
|
|
35
|
-
timer();
|
|
67
|
+
timer(); // Complete timing measurement
|
|
36
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Creates the necessary directory structure for the chart
|
|
71
|
+
*
|
|
72
|
+
* @private
|
|
73
|
+
* @param {string} outDir - Output directory path (must be pre-validated)
|
|
74
|
+
* @throws {Error} If directories cannot be created
|
|
75
|
+
* @since 2.8.0+
|
|
76
|
+
*/
|
|
37
77
|
static createDirectories(outDir) {
|
|
38
78
|
const templatesDir = SecurityUtils.validatePath(path.join(outDir, 'templates'), outDir);
|
|
79
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
39
80
|
fs.mkdirSync(templatesDir, { recursive: true });
|
|
81
|
+
// Create crds directory only if needed later
|
|
40
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Writes the Chart.yaml file with chart metadata
|
|
85
|
+
*
|
|
86
|
+
* @private
|
|
87
|
+
* @param {string} outDir - Output directory path
|
|
88
|
+
* @param {HelmChartMeta} meta - Chart metadata
|
|
89
|
+
* @throws {Error} If Chart.yaml cannot be written
|
|
90
|
+
* @since 2.8.0+
|
|
91
|
+
*/
|
|
41
92
|
static writeChartYaml(outDir, meta) {
|
|
42
93
|
const chartYaml = dumpHelmAwareYaml({
|
|
43
94
|
apiVersion: 'v2',
|
|
@@ -55,20 +106,50 @@ export class HelmChartWriter {
|
|
|
55
106
|
dependencies: meta.dependencies,
|
|
56
107
|
});
|
|
57
108
|
const chartYamlPath = SecurityUtils.validatePath(path.join(outDir, 'Chart.yaml'), outDir);
|
|
109
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
58
110
|
fs.writeFileSync(chartYamlPath, chartYaml);
|
|
59
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Writes values.yaml and environment-specific values files
|
|
114
|
+
*
|
|
115
|
+
* @private
|
|
116
|
+
* @param {string} outDir - Output directory path
|
|
117
|
+
* @param {Record<string, unknown>} defaultValues - Default values
|
|
118
|
+
* @param {EnvValuesMap} envValues - Environment-specific values
|
|
119
|
+
* @throws {Error} If values files cannot be written
|
|
120
|
+
* @since 2.8.0+
|
|
121
|
+
*/
|
|
60
122
|
static writeValuesFiles(outDir, defaultValues, envValues) {
|
|
61
123
|
const valuesPath = SecurityUtils.validatePath(path.join(outDir, 'values.yaml'), outDir);
|
|
124
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
62
125
|
fs.writeFileSync(valuesPath, dumpHelmAwareYaml(defaultValues));
|
|
63
126
|
for (const [env, values] of Object.entries(envValues)) {
|
|
127
|
+
// Use centralized environment name sanitization
|
|
64
128
|
const sanitizedEnv = SecurityUtils.sanitizeEnvironmentName(env);
|
|
65
129
|
const envValuesPath = SecurityUtils.validatePath(path.join(outDir, `values-${sanitizedEnv}.yaml`), outDir);
|
|
130
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
66
131
|
fs.writeFileSync(envValuesPath, dumpHelmAwareYaml(values));
|
|
67
132
|
}
|
|
68
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Writes Kubernetes manifest assets to templates directory
|
|
136
|
+
*
|
|
137
|
+
* @private
|
|
138
|
+
* @param {string} outDir - Output directory path
|
|
139
|
+
* @param {SynthAsset[]} assets - Kubernetes manifests to write
|
|
140
|
+
* @since 2.8.0+
|
|
141
|
+
*/
|
|
69
142
|
static writeAssets(outDir, assets) {
|
|
70
143
|
writeAssets(outDir, assets);
|
|
71
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Writes Helm template helpers to _helpers.tpl file
|
|
147
|
+
*
|
|
148
|
+
* @private
|
|
149
|
+
* @param {string} outDir - Output directory path
|
|
150
|
+
* @param {string | HelperDefinition[]} [helpersTpl] - Helpers content
|
|
151
|
+
* @since 2.8.0+
|
|
152
|
+
*/
|
|
72
153
|
static writeHelpers(outDir, helpersTpl) {
|
|
73
154
|
if (!helpersTpl)
|
|
74
155
|
return;
|
|
@@ -82,22 +163,49 @@ export class HelmChartWriter {
|
|
|
82
163
|
.join('\n');
|
|
83
164
|
}
|
|
84
165
|
const helpersPath = SecurityUtils.validatePath(path.join(outDir, 'templates', '_helpers.tpl'), outDir);
|
|
166
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
85
167
|
fs.writeFileSync(helpersPath, content);
|
|
86
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Writes NOTES.txt file for post-install instructions
|
|
171
|
+
*
|
|
172
|
+
* @private
|
|
173
|
+
* @param {string} outDir - Output directory path
|
|
174
|
+
* @param {string} [notesTpl] - Notes content
|
|
175
|
+
* @since 2.8.0+
|
|
176
|
+
*/
|
|
87
177
|
static writeNotes(outDir, notesTpl) {
|
|
88
178
|
if (!notesTpl)
|
|
89
179
|
return;
|
|
90
180
|
const notesPath = SecurityUtils.validatePath(path.join(outDir, 'templates', 'NOTES.txt'), outDir);
|
|
181
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
91
182
|
fs.writeFileSync(notesPath, notesTpl.endsWith('\n') ? notesTpl : notesTpl + '\n');
|
|
92
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Writes values.schema.json for values validation
|
|
186
|
+
*
|
|
187
|
+
* @private
|
|
188
|
+
* @param {string} outDir - Output directory path
|
|
189
|
+
* @param {Record<string, unknown>} [valuesSchema] - JSON schema
|
|
190
|
+
* @since 2.8.0+
|
|
191
|
+
*/
|
|
93
192
|
static writeSchema(outDir, valuesSchema) {
|
|
94
193
|
if (!valuesSchema)
|
|
95
194
|
return;
|
|
96
195
|
const schemaPath = SecurityUtils.validatePath(path.join(outDir, 'values.schema.json'), outDir);
|
|
196
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
97
197
|
fs.writeFileSync(schemaPath, JSON.stringify(valuesSchema, null, 2) + '\n');
|
|
98
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Writes .helmignore file with common ignore patterns
|
|
201
|
+
*
|
|
202
|
+
* @private
|
|
203
|
+
* @param {string} outDir - Output directory path
|
|
204
|
+
* @since 2.8.0+
|
|
205
|
+
*/
|
|
99
206
|
static writeHelmIgnore(outDir) {
|
|
100
207
|
const helmIgnorePath = SecurityUtils.validatePath(path.join(outDir, '.helmignore'), outDir);
|
|
208
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
101
209
|
if (!fs.existsSync(helmIgnorePath)) {
|
|
102
210
|
const helmIgnore = [
|
|
103
211
|
'# VCS',
|
|
@@ -121,16 +229,34 @@ export class HelmChartWriter {
|
|
|
121
229
|
'*.tgz',
|
|
122
230
|
'',
|
|
123
231
|
].join('\n');
|
|
232
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Chart writer needs dynamic paths
|
|
124
233
|
fs.writeFileSync(helmIgnorePath, helmIgnore);
|
|
125
234
|
}
|
|
126
235
|
}
|
|
127
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Splits YAML string into individual documents
|
|
239
|
+
*
|
|
240
|
+
* @private
|
|
241
|
+
* @param {string} yamlStr - YAML string with potential document separators
|
|
242
|
+
* @returns {string[]} Array of individual YAML documents
|
|
243
|
+
* @since 2.8.0+
|
|
244
|
+
*/
|
|
128
245
|
function splitDocs(yamlStr) {
|
|
129
246
|
return yamlStr
|
|
130
247
|
.split(/^---\s*$/m)
|
|
131
248
|
.map((p) => p.trim())
|
|
132
249
|
.filter((p) => p.length);
|
|
133
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Writes all synthesized assets to the chart directory
|
|
253
|
+
*
|
|
254
|
+
* @private
|
|
255
|
+
* @param {string} outDir - Output directory path
|
|
256
|
+
* @param {SynthAsset[]} assets - Assets to write
|
|
257
|
+
* @throws {Error} If asset ID contains invalid characters
|
|
258
|
+
* @since 2.8.0+
|
|
259
|
+
*/
|
|
134
260
|
function writeAssets(outDir, assets) {
|
|
135
261
|
for (const asset of assets) {
|
|
136
262
|
const targetDir = getTargetDirectory(asset.target);
|
|
@@ -149,45 +275,89 @@ function writeAssets(outDir, assets) {
|
|
|
149
275
|
}
|
|
150
276
|
}
|
|
151
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Gets the target directory for an asset
|
|
280
|
+
*
|
|
281
|
+
* @private
|
|
282
|
+
* @param {('templates' | 'crds')} [target] - Target directory type
|
|
283
|
+
* @returns {string} Directory name
|
|
284
|
+
* @since 2.8.0+
|
|
285
|
+
*/
|
|
152
286
|
function getTargetDirectory(target) {
|
|
153
287
|
return target === 'crds' ? 'crds' : 'templates';
|
|
154
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Writes a single asset file without document splitting
|
|
291
|
+
*
|
|
292
|
+
* @private
|
|
293
|
+
* @param {string} outDir - Output directory path
|
|
294
|
+
* @param {string} targetDir - Target subdirectory
|
|
295
|
+
* @param {string} assetId - Asset identifier
|
|
296
|
+
* @param {string} yaml - YAML content
|
|
297
|
+
* @since 2.8.0+
|
|
298
|
+
*/
|
|
155
299
|
function writeSingleAssetFile(outDir, targetDir, directorySegments, fileBaseName, yaml) {
|
|
156
300
|
const chartSubdir = path.join(outDir, targetDir, ...directorySegments);
|
|
157
301
|
SecurityUtils.validatePath(chartSubdir, outDir);
|
|
302
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
158
303
|
fs.mkdirSync(chartSubdir, { recursive: true });
|
|
304
|
+
// Validate fileBaseName to prevent path traversal
|
|
159
305
|
if (fileBaseName.includes('..') || fileBaseName.includes('/') || fileBaseName.includes('\\')) {
|
|
160
306
|
throw new Error(`Invalid fileBaseName: ${fileBaseName}`);
|
|
161
307
|
}
|
|
162
308
|
const filename = `${fileBaseName}.yaml`;
|
|
309
|
+
// Validate filename to prevent path traversal
|
|
163
310
|
if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
|
|
164
311
|
throw new Error(`Invalid filename: ${filename}`);
|
|
165
312
|
}
|
|
166
313
|
const absolutePath = path.join(chartSubdir, filename);
|
|
167
314
|
SecurityUtils.validatePath(absolutePath, outDir);
|
|
315
|
+
// Post-process YAML to transform field-level conditionals
|
|
168
316
|
const processedYaml = postProcessFieldConditionals(yaml);
|
|
317
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
169
318
|
fs.writeFileSync(absolutePath, processedYaml.endsWith('\n') ? processedYaml : `${processedYaml}\n`);
|
|
170
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Writes multiple asset files by splitting YAML documents
|
|
322
|
+
*
|
|
323
|
+
* @private
|
|
324
|
+
* @param {string} outDir - Output directory path
|
|
325
|
+
* @param {string} targetDir - Target subdirectory
|
|
326
|
+
* @param {string} assetId - Asset identifier
|
|
327
|
+
* @param {string} yaml - YAML content with potential multiple documents
|
|
328
|
+
* @since 2.8.0+
|
|
329
|
+
*/
|
|
171
330
|
function writeMultipleAssetFiles(outDir, targetDir, directorySegments, fileBaseName, yaml) {
|
|
172
331
|
const chartSubdir = path.join(outDir, targetDir, ...directorySegments);
|
|
173
332
|
SecurityUtils.validatePath(chartSubdir, outDir);
|
|
333
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Chart writer needs dynamic paths
|
|
174
334
|
fs.mkdirSync(chartSubdir, { recursive: true });
|
|
335
|
+
// Post-process YAML to transform field-level conditionals
|
|
175
336
|
const processedYaml = postProcessFieldConditionals(yaml);
|
|
176
337
|
const parts = splitDocs(processedYaml);
|
|
177
338
|
parts.forEach((doc, index) => {
|
|
339
|
+
// Validate fileBaseName to prevent path traversal
|
|
178
340
|
if (fileBaseName.includes('..') || fileBaseName.includes('/') || fileBaseName.includes('\\')) {
|
|
179
341
|
throw new Error(`Invalid fileBaseName: ${fileBaseName}`);
|
|
180
342
|
}
|
|
181
343
|
const suffix = parts.length > 1 ? `-${index + 1}` : '';
|
|
182
344
|
const filename = `${fileBaseName}${suffix}.yaml`;
|
|
345
|
+
// Validate filename to prevent path traversal
|
|
183
346
|
if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
|
|
184
347
|
throw new Error(`Invalid filename: ${filename}`);
|
|
185
348
|
}
|
|
186
349
|
const absolutePath = path.join(chartSubdir, filename);
|
|
187
350
|
SecurityUtils.validatePath(absolutePath, outDir);
|
|
351
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Path validated by SecurityUtils
|
|
188
352
|
fs.writeFileSync(absolutePath, doc.endsWith('\n') ? doc : `${doc}\n`);
|
|
189
353
|
});
|
|
190
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Validates an asset identifier and converts it into directory segments and file basename.
|
|
357
|
+
* @param assetId - Identifier provided by the synthesizer.
|
|
358
|
+
* @returns Normalized directory segments and filename stem.
|
|
359
|
+
* @since 2.12.2 Preserves human-friendly asset identifiers without compromising safety.
|
|
360
|
+
*/
|
|
191
361
|
function resolveAssetPath(assetId) {
|
|
192
362
|
if (!assetId || typeof assetId !== 'string') {
|
|
193
363
|
throw new Error('Asset ID must be a non-empty string');
|
|
@@ -1,46 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Loader
|
|
3
|
+
*
|
|
4
|
+
* This module provides configuration loading and validation for policy plugins.
|
|
5
|
+
* It supports environment-specific configuration, schema validation, and
|
|
6
|
+
* configuration inheritance.
|
|
7
|
+
*
|
|
8
|
+
* @since 3.0.0
|
|
9
|
+
*/
|
|
1
10
|
import type { JSONSchema, PolicyPlugin, ConfigurationLoaderOptions } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Configuration source types
|
|
13
|
+
*/
|
|
2
14
|
export type ConfigurationSource = 'environment' | 'file' | 'inline' | 'default';
|
|
15
|
+
/**
|
|
16
|
+
* Configuration entry with metadata
|
|
17
|
+
*/
|
|
3
18
|
export interface ConfigurationEntry {
|
|
19
|
+
/** Configuration value */
|
|
4
20
|
readonly value: unknown;
|
|
21
|
+
/** Source of the configuration */
|
|
5
22
|
readonly source: ConfigurationSource;
|
|
23
|
+
/** Environment this configuration applies to */
|
|
6
24
|
readonly environment?: string;
|
|
25
|
+
/** Priority for configuration merging (higher wins) */
|
|
7
26
|
readonly priority: number;
|
|
27
|
+
/** Validation schema for this configuration */
|
|
8
28
|
readonly schema?: JSONSchema;
|
|
9
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Plugin configuration with validation
|
|
32
|
+
*/
|
|
10
33
|
export interface PluginConfiguration {
|
|
34
|
+
/** Plugin name */
|
|
11
35
|
readonly pluginName: string;
|
|
36
|
+
/** Merged configuration value */
|
|
12
37
|
readonly config: Record<string, unknown>;
|
|
38
|
+
/** Configuration entries that were merged */
|
|
13
39
|
readonly entries: ConfigurationEntry[];
|
|
40
|
+
/** Whether configuration was validated */
|
|
14
41
|
readonly validated: boolean;
|
|
42
|
+
/** Validation errors if any */
|
|
15
43
|
readonly validationErrors?: string[];
|
|
16
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Environment-specific configuration
|
|
47
|
+
*/
|
|
17
48
|
export interface EnvironmentConfiguration {
|
|
49
|
+
/** Environment name (e.g., 'development', 'production') */
|
|
18
50
|
readonly environment: string;
|
|
51
|
+
/** Plugin configurations for this environment */
|
|
19
52
|
readonly plugins: Record<string, unknown>;
|
|
53
|
+
/** Global configuration options */
|
|
20
54
|
readonly global?: Record<string, unknown>;
|
|
21
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Configuration loader for policy plugins
|
|
58
|
+
*/
|
|
22
59
|
export declare class ConfigurationLoader {
|
|
23
60
|
private readonly options;
|
|
24
61
|
private readonly logger;
|
|
25
62
|
private readonly configurations;
|
|
26
63
|
private readonly environmentConfigs;
|
|
27
64
|
constructor(options?: ConfigurationLoaderOptions);
|
|
65
|
+
/**
|
|
66
|
+
* Loads configuration for a plugin
|
|
67
|
+
* @param plugin - Plugin to load configuration for
|
|
68
|
+
* @param environment - Target environment
|
|
69
|
+
* @param inlineConfig - Inline configuration to merge
|
|
70
|
+
* @returns Plugin configuration
|
|
71
|
+
*/
|
|
28
72
|
loadPluginConfiguration(plugin: PolicyPlugin, environment?: string, inlineConfig?: Record<string, unknown>): Promise<PluginConfiguration>;
|
|
73
|
+
/**
|
|
74
|
+
* Collects configuration entries from all sources
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
29
77
|
private collectConfigurationEntries;
|
|
78
|
+
/**
|
|
79
|
+
* Validates plugin configuration
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
30
82
|
private validatePluginConfiguration;
|
|
83
|
+
/**
|
|
84
|
+
* Loads environment-specific configuration
|
|
85
|
+
* @param environment - Environment name
|
|
86
|
+
* @returns Environment configuration
|
|
87
|
+
*/
|
|
31
88
|
loadEnvironmentConfiguration(environment: string): Promise<EnvironmentConfiguration | undefined>;
|
|
89
|
+
/**
|
|
90
|
+
* Validates configuration against a JSON schema
|
|
91
|
+
* @param config - Configuration to validate
|
|
92
|
+
* @param schema - JSON schema to validate against
|
|
93
|
+
* @param pluginName - Plugin name for error reporting
|
|
94
|
+
* @throws {PluginConfigurationError} When validation fails
|
|
95
|
+
*/
|
|
32
96
|
validateConfiguration(config: Record<string, unknown>, schema: JSONSchema, pluginName: string): void;
|
|
97
|
+
/**
|
|
98
|
+
* Adds a configuration entry for a plugin
|
|
99
|
+
* @param pluginName - Plugin name
|
|
100
|
+
* @param entry - Configuration entry to add
|
|
101
|
+
*/
|
|
33
102
|
addConfigurationEntry(pluginName: string, entry: ConfigurationEntry): void;
|
|
103
|
+
/**
|
|
104
|
+
* Gets all configuration entries for a plugin
|
|
105
|
+
* @param pluginName - Plugin name
|
|
106
|
+
* @returns Array of configuration entries
|
|
107
|
+
*/
|
|
34
108
|
getConfigurationEntries(pluginName: string): ConfigurationEntry[];
|
|
109
|
+
/**
|
|
110
|
+
* Clears all cached configurations
|
|
111
|
+
*/
|
|
35
112
|
clearCache(): void;
|
|
113
|
+
/**
|
|
114
|
+
* Loads file-based configuration for a plugin
|
|
115
|
+
* @param _pluginName - Plugin name (unused)
|
|
116
|
+
* @param _environment - Target environment (unused)
|
|
117
|
+
* @returns Configuration object or undefined
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
36
120
|
private loadFileConfiguration;
|
|
121
|
+
/**
|
|
122
|
+
* Loads environment variable configuration for a plugin
|
|
123
|
+
* @param pluginName - Plugin name
|
|
124
|
+
* @returns Configuration object or undefined
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
37
127
|
private loadEnvironmentVariableConfiguration;
|
|
128
|
+
/**
|
|
129
|
+
* Loads environment configuration from files
|
|
130
|
+
* @param _environment - Environment name (unused)
|
|
131
|
+
* @returns Environment configuration or undefined
|
|
132
|
+
* @private
|
|
133
|
+
*/
|
|
38
134
|
private loadEnvironmentFromFiles;
|
|
135
|
+
/**
|
|
136
|
+
* Merges configuration entries by priority
|
|
137
|
+
* @param entries - Configuration entries to merge
|
|
138
|
+
* @returns Merged configuration
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
39
141
|
private mergeConfigurations;
|
|
142
|
+
/**
|
|
143
|
+
* Parses environment variable value to appropriate type
|
|
144
|
+
* @param value - Environment variable value
|
|
145
|
+
* @returns Parsed value
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
40
148
|
private parseEnvironmentValue;
|
|
149
|
+
/**
|
|
150
|
+
* Validates a configuration object against a JSON schema
|
|
151
|
+
* @param config - Configuration to validate
|
|
152
|
+
* @param schema - JSON schema
|
|
153
|
+
* @param path - Current path for error reporting
|
|
154
|
+
* @throws {Error} When validation fails
|
|
155
|
+
* @private
|
|
156
|
+
*/
|
|
41
157
|
private validateAgainstSchema;
|
|
158
|
+
/**
|
|
159
|
+
* Validates basic type constraints
|
|
160
|
+
* @private
|
|
161
|
+
*/
|
|
42
162
|
private validateBasicType;
|
|
163
|
+
/**
|
|
164
|
+
* Validates object properties
|
|
165
|
+
* @private
|
|
166
|
+
*/
|
|
43
167
|
private validateObjectProperties;
|
|
168
|
+
/**
|
|
169
|
+
* Validates required properties
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
44
172
|
private validateRequiredProperties;
|
|
173
|
+
/**
|
|
174
|
+
* Validates each property in the object
|
|
175
|
+
* @private
|
|
176
|
+
*/
|
|
45
177
|
private validateEachProperty;
|
|
46
178
|
}
|