@intentius/chant-lexicon-helm 0.0.16
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/README.md +22 -0
- package/dist/integrity.json +36 -0
- package/dist/manifest.json +37 -0
- package/dist/meta.json +208 -0
- package/dist/rules/chart-metadata.ts +64 -0
- package/dist/rules/helm-helpers.ts +64 -0
- package/dist/rules/no-hardcoded-image.ts +62 -0
- package/dist/rules/values-no-secrets.ts +82 -0
- package/dist/rules/whm101.ts +46 -0
- package/dist/rules/whm102.ts +33 -0
- package/dist/rules/whm103.ts +59 -0
- package/dist/rules/whm104.ts +35 -0
- package/dist/rules/whm105.ts +30 -0
- package/dist/rules/whm201.ts +36 -0
- package/dist/rules/whm202.ts +50 -0
- package/dist/rules/whm203.ts +39 -0
- package/dist/rules/whm204.ts +60 -0
- package/dist/rules/whm301.ts +41 -0
- package/dist/rules/whm302.ts +40 -0
- package/dist/rules/whm401.ts +57 -0
- package/dist/rules/whm402.ts +45 -0
- package/dist/rules/whm403.ts +45 -0
- package/dist/rules/whm404.ts +36 -0
- package/dist/rules/whm405.ts +53 -0
- package/dist/rules/whm406.ts +34 -0
- package/dist/rules/whm407.ts +83 -0
- package/dist/rules/whm501.ts +103 -0
- package/dist/rules/whm502.ts +94 -0
- package/dist/skills/chant-helm-chart-patterns.md +229 -0
- package/dist/skills/chant-helm-chart-security-patterns.md +192 -0
- package/dist/skills/chant-helm-create-chart.md +211 -0
- package/dist/types/index.d.ts +132 -0
- package/package.json +34 -0
- package/src/codegen/docs-cli.ts +4 -0
- package/src/codegen/docs.ts +483 -0
- package/src/codegen/generate-cli.ts +28 -0
- package/src/codegen/generate.ts +249 -0
- package/src/codegen/naming.ts +38 -0
- package/src/codegen/package.ts +64 -0
- package/src/composites/composites.test.ts +1050 -0
- package/src/composites/helm-batch-job.ts +209 -0
- package/src/composites/helm-crd-lifecycle.ts +184 -0
- package/src/composites/helm-cron-job.ts +177 -0
- package/src/composites/helm-daemon-set.ts +169 -0
- package/src/composites/helm-external-secret.ts +93 -0
- package/src/composites/helm-library.ts +51 -0
- package/src/composites/helm-microservice.ts +331 -0
- package/src/composites/helm-monitored-service.ts +252 -0
- package/src/composites/helm-namespace-env.ts +154 -0
- package/src/composites/helm-secure-ingress.ts +114 -0
- package/src/composites/helm-stateful-service.ts +213 -0
- package/src/composites/helm-web-app.ts +264 -0
- package/src/composites/helm-worker.ts +207 -0
- package/src/composites/index.ts +38 -0
- package/src/coverage.test.ts +21 -0
- package/src/coverage.ts +50 -0
- package/src/generated/index.d.ts +132 -0
- package/src/generated/index.ts +13 -0
- package/src/generated/lexicon-helm.json +208 -0
- package/src/helpers.test.ts +51 -0
- package/src/helpers.ts +100 -0
- package/src/import/generator.ts +285 -0
- package/src/import/import.test.ts +224 -0
- package/src/import/parser.ts +160 -0
- package/src/import/template-stripper.ts +123 -0
- package/src/index.ts +108 -0
- package/src/intrinsics.test.ts +380 -0
- package/src/intrinsics.ts +484 -0
- package/src/lint/post-synth/helm-helpers.ts +64 -0
- package/src/lint/post-synth/post-synth.test.ts +504 -0
- package/src/lint/post-synth/whm101.ts +46 -0
- package/src/lint/post-synth/whm102.ts +33 -0
- package/src/lint/post-synth/whm103.ts +59 -0
- package/src/lint/post-synth/whm104.ts +35 -0
- package/src/lint/post-synth/whm105.ts +30 -0
- package/src/lint/post-synth/whm201.ts +36 -0
- package/src/lint/post-synth/whm202.ts +50 -0
- package/src/lint/post-synth/whm203.ts +39 -0
- package/src/lint/post-synth/whm204.ts +60 -0
- package/src/lint/post-synth/whm301.ts +41 -0
- package/src/lint/post-synth/whm302.ts +40 -0
- package/src/lint/post-synth/whm401.ts +57 -0
- package/src/lint/post-synth/whm402.ts +45 -0
- package/src/lint/post-synth/whm403.ts +45 -0
- package/src/lint/post-synth/whm404.ts +36 -0
- package/src/lint/post-synth/whm405.ts +53 -0
- package/src/lint/post-synth/whm406.ts +34 -0
- package/src/lint/post-synth/whm407.ts +83 -0
- package/src/lint/post-synth/whm501.ts +103 -0
- package/src/lint/post-synth/whm502.ts +94 -0
- package/src/lint/rules/chart-metadata.ts +64 -0
- package/src/lint/rules/lint-rules.test.ts +97 -0
- package/src/lint/rules/no-hardcoded-image.ts +62 -0
- package/src/lint/rules/values-no-secrets.ts +82 -0
- package/src/lsp/completions.test.ts +72 -0
- package/src/lsp/completions.ts +20 -0
- package/src/lsp/hover.test.ts +46 -0
- package/src/lsp/hover.ts +46 -0
- package/src/package-cli.ts +28 -0
- package/src/plugin.test.ts +71 -0
- package/src/plugin.ts +206 -0
- package/src/resources.ts +77 -0
- package/src/serializer.test.ts +930 -0
- package/src/serializer.ts +835 -0
- package/src/skills/chart-patterns.md +229 -0
- package/src/skills/chart-security-patterns.md +192 -0
- package/src/skills/create-chart.md +211 -0
- package/src/validate-cli.ts +21 -0
- package/src/validate.test.ts +37 -0
- package/src/validate.ts +36 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Go template expression extraction and stripping.
|
|
3
|
+
*
|
|
4
|
+
* Strips Go template expressions (`{{ ... }}`) from Helm templates,
|
|
5
|
+
* replacing them with placeholder values so the underlying YAML can be parsed.
|
|
6
|
+
* Records the original expressions for later reconstruction as Helm intrinsics.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface StrippedExpression {
|
|
10
|
+
/** Placeholder key inserted into the YAML. */
|
|
11
|
+
placeholder: string;
|
|
12
|
+
/** Original Go template expression (without delimiters). */
|
|
13
|
+
expression: string;
|
|
14
|
+
/** Line number (1-based). */
|
|
15
|
+
line: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface StripResult {
|
|
19
|
+
/** YAML with template expressions replaced by placeholders. */
|
|
20
|
+
yaml: string;
|
|
21
|
+
/** Extracted expressions keyed by placeholder. */
|
|
22
|
+
expressions: Map<string, StrippedExpression>;
|
|
23
|
+
/** Block-level directives (if/range/with/end) that were removed entirely. */
|
|
24
|
+
blockDirectives: Array<{ directive: string; line: number }>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Strip Go template expressions from a Helm template file.
|
|
29
|
+
*
|
|
30
|
+
* - Inline expressions (`{{ .Values.x }}`) are replaced with `__HELM_PLACEHOLDERn__`
|
|
31
|
+
* - Block-level directives (`{{- if ... }}`, `{{- end }}`, `{{- range ... }}`) are removed
|
|
32
|
+
* - Action pipelines are preserved in the expression map
|
|
33
|
+
*/
|
|
34
|
+
export function stripTemplateExpressions(template: string): StripResult {
|
|
35
|
+
const expressions = new Map<string, StrippedExpression>();
|
|
36
|
+
const blockDirectives: Array<{ directive: string; line: number }> = [];
|
|
37
|
+
let counter = 0;
|
|
38
|
+
|
|
39
|
+
const lines = template.split("\n");
|
|
40
|
+
const outputLines: string[] = [];
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < lines.length; i++) {
|
|
43
|
+
const line = lines[i];
|
|
44
|
+
const lineNum = i + 1;
|
|
45
|
+
|
|
46
|
+
// Check if this is a standalone block directive line
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (isBlockDirectiveLine(trimmed)) {
|
|
49
|
+
blockDirectives.push({ directive: trimmed, line: lineNum });
|
|
50
|
+
continue; // Remove entire line
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Replace inline expressions with placeholders
|
|
54
|
+
const processed = line.replace(/\{\{-?\s*(.*?)\s*-?\}\}/g, (_match, expr: string) => {
|
|
55
|
+
const placeholder = `__HELM_PLACEHOLDER${counter}__`;
|
|
56
|
+
counter++;
|
|
57
|
+
expressions.set(placeholder, { placeholder, expression: expr.trim(), line: lineNum });
|
|
58
|
+
return placeholder;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
outputLines.push(processed);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
yaml: outputLines.join("\n"),
|
|
66
|
+
expressions,
|
|
67
|
+
blockDirectives,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if a line is a standalone block directive (if/else/end/range/with/define/block).
|
|
73
|
+
*/
|
|
74
|
+
function isBlockDirectiveLine(trimmed: string): boolean {
|
|
75
|
+
if (!trimmed.startsWith("{{")) return false;
|
|
76
|
+
|
|
77
|
+
// Extract the directive content
|
|
78
|
+
const match = trimmed.match(/^\{\{-?\s*(if|else|else if|end|range|with|define|block|template)\b/);
|
|
79
|
+
if (!match) return false;
|
|
80
|
+
|
|
81
|
+
// Verify the line is ONLY the directive (not mixed with YAML)
|
|
82
|
+
// A standalone directive line starts with {{ and ends with }}
|
|
83
|
+
return /\}\}\s*$/.test(trimmed);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Classify a Go template expression into a category for code generation.
|
|
88
|
+
*/
|
|
89
|
+
export type ExpressionKind =
|
|
90
|
+
| "values" // .Values.x.y
|
|
91
|
+
| "release" // .Release.Name, etc.
|
|
92
|
+
| "chart" // .Chart.Name, etc.
|
|
93
|
+
| "include" // include "name" .
|
|
94
|
+
| "toYaml" // toYaml .Values.x | nindent N
|
|
95
|
+
| "required" // required "msg" .Values.x
|
|
96
|
+
| "default" // default "val" .Values.x
|
|
97
|
+
| "printf" // printf "%s" .Values.x
|
|
98
|
+
| "quote" // .Values.x | quote
|
|
99
|
+
| "lookup" // lookup "v1" "Secret" "ns" "name"
|
|
100
|
+
| "tpl" // tpl .Values.x .
|
|
101
|
+
| "pipe" // any piped expression
|
|
102
|
+
| "other"; // unclassified
|
|
103
|
+
|
|
104
|
+
export function classifyExpression(expr: string): ExpressionKind {
|
|
105
|
+
const trimmed = expr.trim();
|
|
106
|
+
|
|
107
|
+
if (trimmed.startsWith("include ")) return "include";
|
|
108
|
+
if (trimmed.startsWith("toYaml ") || trimmed.includes("| toYaml")) return "toYaml";
|
|
109
|
+
if (trimmed.startsWith("required ")) return "required";
|
|
110
|
+
if (trimmed.startsWith("default ")) return "default";
|
|
111
|
+
if (trimmed.startsWith("printf ")) return "printf";
|
|
112
|
+
if (trimmed.startsWith("lookup ")) return "lookup";
|
|
113
|
+
if (trimmed.startsWith("tpl ")) return "tpl";
|
|
114
|
+
|
|
115
|
+
if (trimmed.includes("| quote")) return "quote";
|
|
116
|
+
if (trimmed.includes("|")) return "pipe";
|
|
117
|
+
|
|
118
|
+
if (trimmed.startsWith(".Values.")) return "values";
|
|
119
|
+
if (trimmed.startsWith(".Release.")) return "release";
|
|
120
|
+
if (trimmed.startsWith(".Chart.")) return "chart";
|
|
121
|
+
|
|
122
|
+
return "other";
|
|
123
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Serializer
|
|
2
|
+
export { helmSerializer } from "./serializer";
|
|
3
|
+
|
|
4
|
+
// Plugin
|
|
5
|
+
export { helmPlugin } from "./plugin";
|
|
6
|
+
|
|
7
|
+
// Resources
|
|
8
|
+
export { Chart, Values, HelmTest, HelmNotes, HelmHook, HelmDependency, HelmMaintainer, HelmCRD } from "./resources";
|
|
9
|
+
|
|
10
|
+
// Intrinsics
|
|
11
|
+
export {
|
|
12
|
+
HelmTpl,
|
|
13
|
+
HELM_TPL_KEY,
|
|
14
|
+
HELM_IF_KEY,
|
|
15
|
+
HELM_RANGE_KEY,
|
|
16
|
+
HELM_WITH_KEY,
|
|
17
|
+
values,
|
|
18
|
+
Release,
|
|
19
|
+
ChartRef,
|
|
20
|
+
include,
|
|
21
|
+
required,
|
|
22
|
+
helmDefault,
|
|
23
|
+
toYaml,
|
|
24
|
+
quote,
|
|
25
|
+
printf,
|
|
26
|
+
tpl,
|
|
27
|
+
lookup,
|
|
28
|
+
Capabilities,
|
|
29
|
+
Template,
|
|
30
|
+
filesGet,
|
|
31
|
+
filesGlob,
|
|
32
|
+
filesAsConfig,
|
|
33
|
+
filesAsSecrets,
|
|
34
|
+
ElseIf,
|
|
35
|
+
If,
|
|
36
|
+
Range,
|
|
37
|
+
With,
|
|
38
|
+
withOrder,
|
|
39
|
+
argoWave,
|
|
40
|
+
} from "./intrinsics";
|
|
41
|
+
export type { HelmConditional } from "./intrinsics";
|
|
42
|
+
|
|
43
|
+
// Helpers
|
|
44
|
+
export { generateHelpers } from "./helpers";
|
|
45
|
+
export type { HelpersConfig } from "./helpers";
|
|
46
|
+
|
|
47
|
+
// Code generation pipeline
|
|
48
|
+
export { generate, writeGeneratedFiles } from "./codegen/generate";
|
|
49
|
+
export { packageLexicon } from "./codegen/package";
|
|
50
|
+
export type { PackageOptions, PackageResult } from "./codegen/package";
|
|
51
|
+
|
|
52
|
+
// Composites
|
|
53
|
+
export {
|
|
54
|
+
HelmWebApp,
|
|
55
|
+
HelmStatefulService,
|
|
56
|
+
HelmCronJob,
|
|
57
|
+
HelmMicroservice,
|
|
58
|
+
HelmLibrary,
|
|
59
|
+
HelmCRDLifecycle,
|
|
60
|
+
HelmDaemonSet,
|
|
61
|
+
HelmWorker,
|
|
62
|
+
HelmExternalSecret,
|
|
63
|
+
HelmBatchJob,
|
|
64
|
+
HelmMonitoredService,
|
|
65
|
+
HelmSecureIngress,
|
|
66
|
+
HelmNamespaceEnv,
|
|
67
|
+
} from "./composites";
|
|
68
|
+
export type {
|
|
69
|
+
HelmWebAppProps,
|
|
70
|
+
HelmWebAppResult,
|
|
71
|
+
HelmStatefulServiceProps,
|
|
72
|
+
HelmStatefulServiceResult,
|
|
73
|
+
HelmCronJobProps,
|
|
74
|
+
HelmCronJobResult,
|
|
75
|
+
HelmMicroserviceProps,
|
|
76
|
+
HelmMicroserviceResult,
|
|
77
|
+
HelmLibraryProps,
|
|
78
|
+
HelmLibraryResult,
|
|
79
|
+
HelmCRDLifecycleProps,
|
|
80
|
+
HelmCRDLifecycleResult,
|
|
81
|
+
HelmDaemonSetProps,
|
|
82
|
+
HelmDaemonSetResult,
|
|
83
|
+
HelmWorkerProps,
|
|
84
|
+
HelmWorkerResult,
|
|
85
|
+
HelmExternalSecretProps,
|
|
86
|
+
HelmExternalSecretResult,
|
|
87
|
+
HelmBatchJobProps,
|
|
88
|
+
HelmBatchJobResult,
|
|
89
|
+
HelmMonitoredServiceProps,
|
|
90
|
+
HelmMonitoredServiceResult,
|
|
91
|
+
HelmSecureIngressProps,
|
|
92
|
+
HelmSecureIngressResult,
|
|
93
|
+
HelmNamespaceEnvProps,
|
|
94
|
+
HelmNamespaceEnvResult,
|
|
95
|
+
} from "./composites";
|
|
96
|
+
|
|
97
|
+
// Import pipeline
|
|
98
|
+
export { HelmParser } from "./import/parser";
|
|
99
|
+
export { HelmGenerator } from "./import/generator";
|
|
100
|
+
export { stripTemplateExpressions, classifyExpression } from "./import/template-stripper";
|
|
101
|
+
export type { StrippedExpression, StripResult, ExpressionKind } from "./import/template-stripper";
|
|
102
|
+
|
|
103
|
+
// LSP providers
|
|
104
|
+
export { helmCompletions } from "./lsp/completions";
|
|
105
|
+
export { helmHover } from "./lsp/hover";
|
|
106
|
+
|
|
107
|
+
// Docs generation
|
|
108
|
+
export { generateDocs } from "./codegen/docs";
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { INTRINSIC_MARKER } from "@intentius/chant/intrinsic";
|
|
3
|
+
import {
|
|
4
|
+
HelmTpl,
|
|
5
|
+
HELM_TPL_KEY,
|
|
6
|
+
HELM_IF_KEY,
|
|
7
|
+
HELM_RANGE_KEY,
|
|
8
|
+
HELM_WITH_KEY,
|
|
9
|
+
values,
|
|
10
|
+
Release,
|
|
11
|
+
ChartRef,
|
|
12
|
+
Capabilities,
|
|
13
|
+
Template,
|
|
14
|
+
filesGet,
|
|
15
|
+
filesGlob,
|
|
16
|
+
filesAsConfig,
|
|
17
|
+
filesAsSecrets,
|
|
18
|
+
ElseIf,
|
|
19
|
+
include,
|
|
20
|
+
required,
|
|
21
|
+
helmDefault,
|
|
22
|
+
toYaml,
|
|
23
|
+
quote,
|
|
24
|
+
printf,
|
|
25
|
+
tpl,
|
|
26
|
+
lookup,
|
|
27
|
+
If,
|
|
28
|
+
Range,
|
|
29
|
+
With,
|
|
30
|
+
withOrder,
|
|
31
|
+
argoWave,
|
|
32
|
+
} from "./intrinsics";
|
|
33
|
+
|
|
34
|
+
describe("HelmTpl", () => {
|
|
35
|
+
test("has INTRINSIC_MARKER", () => {
|
|
36
|
+
const t = new HelmTpl("{{ .Values.x }}");
|
|
37
|
+
expect(t[INTRINSIC_MARKER]).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("toJSON returns __helm_tpl marker", () => {
|
|
41
|
+
const t = new HelmTpl("{{ .Values.x }}");
|
|
42
|
+
expect(t.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.x }}" });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("pipe chains template functions", () => {
|
|
46
|
+
const t = new HelmTpl("{{ .Values.x }}");
|
|
47
|
+
const piped = t.pipe("upper");
|
|
48
|
+
expect(piped.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.x | upper }}" });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("multiple pipes chain correctly", () => {
|
|
52
|
+
const t = new HelmTpl("{{ .Values.x }}");
|
|
53
|
+
const piped = t.pipe("upper").pipe("quote");
|
|
54
|
+
expect(piped.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.x | upper | quote }}" });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("toString returns expression", () => {
|
|
58
|
+
const t = new HelmTpl("{{ .Values.x }}");
|
|
59
|
+
expect(t.toString()).toBe("{{ .Values.x }}");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("values proxy", () => {
|
|
64
|
+
test("simple property access", () => {
|
|
65
|
+
const v = values.replicas;
|
|
66
|
+
expect(v[INTRINSIC_MARKER]).toBe(true);
|
|
67
|
+
expect(v.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.replicas }}" });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("nested property access", () => {
|
|
71
|
+
const v = values.image.repository;
|
|
72
|
+
expect(v.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.image.repository }}" });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("deeply nested access", () => {
|
|
76
|
+
const v = values.a.b.c.d;
|
|
77
|
+
expect(v.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.a.b.c.d }}" });
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("Release built-in", () => {
|
|
82
|
+
test("Release.Name", () => {
|
|
83
|
+
expect(Release.Name.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Release.Name }}" });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("Release.Namespace", () => {
|
|
87
|
+
expect(Release.Namespace.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Release.Namespace }}" });
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("Release.IsUpgrade", () => {
|
|
91
|
+
expect(Release.IsUpgrade.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Release.IsUpgrade }}" });
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("ChartRef built-in", () => {
|
|
96
|
+
test("ChartRef.Name", () => {
|
|
97
|
+
expect(ChartRef.Name.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Chart.Name }}" });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("ChartRef.Version", () => {
|
|
101
|
+
expect(ChartRef.Version.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Chart.Version }}" });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("ChartRef.Description", () => {
|
|
105
|
+
expect(ChartRef.Description.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Chart.Description }}" });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("ChartRef.Home", () => {
|
|
109
|
+
expect(ChartRef.Home.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Chart.Home }}" });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("ChartRef.KubeVersion", () => {
|
|
113
|
+
expect(ChartRef.KubeVersion.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Chart.KubeVersion }}" });
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("template functions", () => {
|
|
118
|
+
test("include", () => {
|
|
119
|
+
const t = include("my-app.fullname");
|
|
120
|
+
expect(t.toJSON()).toEqual({ [HELM_TPL_KEY]: '{{ include "my-app.fullname" . }}' });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("include with custom context", () => {
|
|
124
|
+
const t = include("my-app.labels", "$");
|
|
125
|
+
expect(t.toJSON()).toEqual({ [HELM_TPL_KEY]: '{{ include "my-app.labels" $ }}' });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("required", () => {
|
|
129
|
+
const t = required("image.tag is required", values.image.tag);
|
|
130
|
+
expect(t.toJSON()).toEqual({
|
|
131
|
+
[HELM_TPL_KEY]: '{{ required "image.tag is required" .Values.image.tag }}',
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("helmDefault", () => {
|
|
136
|
+
const t = helmDefault("nginx", values.image.repository);
|
|
137
|
+
expect(t.toJSON()).toEqual({
|
|
138
|
+
[HELM_TPL_KEY]: '{{ default "nginx" .Values.image.repository }}',
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("helmDefault with number", () => {
|
|
143
|
+
const t = helmDefault(3, values.replicaCount);
|
|
144
|
+
expect(t.toJSON()).toEqual({
|
|
145
|
+
[HELM_TPL_KEY]: "{{ default 3 .Values.replicaCount }}",
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("toYaml without indent", () => {
|
|
150
|
+
const t = toYaml(values.resources);
|
|
151
|
+
expect(t.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ toYaml .Values.resources }}" });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("toYaml with indent", () => {
|
|
155
|
+
const t = toYaml(values.resources, 12);
|
|
156
|
+
expect(t.toJSON()).toEqual({
|
|
157
|
+
[HELM_TPL_KEY]: "{{ toYaml .Values.resources | nindent 12 }}",
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("quote", () => {
|
|
162
|
+
const t = quote(values.image.tag);
|
|
163
|
+
expect(t.toJSON()).toEqual({
|
|
164
|
+
[HELM_TPL_KEY]: "{{ .Values.image.tag | quote }}",
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("printf", () => {
|
|
169
|
+
const t = printf("%s:%s", values.image.repository, values.image.tag);
|
|
170
|
+
expect(t.toJSON()).toEqual({
|
|
171
|
+
[HELM_TPL_KEY]: '{{ printf "%s:%s" .Values.image.repository .Values.image.tag }}',
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("tpl", () => {
|
|
176
|
+
const t = tpl(values.someTemplate);
|
|
177
|
+
expect(t.toJSON()).toEqual({
|
|
178
|
+
[HELM_TPL_KEY]: "{{ tpl .Values.someTemplate . }}",
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("lookup", () => {
|
|
183
|
+
const t = lookup("v1", "Secret", "default", "my-secret");
|
|
184
|
+
expect(t.toJSON()).toEqual({
|
|
185
|
+
[HELM_TPL_KEY]: '{{ lookup "v1" "Secret" "default" "my-secret" }}',
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("control flow", () => {
|
|
191
|
+
test("If produces __helm_if marker", () => {
|
|
192
|
+
const cond = If(values.ingress.enabled, { key: "value" });
|
|
193
|
+
const json = cond.toJSON();
|
|
194
|
+
expect(json).toHaveProperty(HELM_IF_KEY);
|
|
195
|
+
expect((json as Record<string, unknown>)[HELM_IF_KEY]).toBe(".Values.ingress.enabled");
|
|
196
|
+
expect((json as Record<string, unknown>).body).toEqual({ key: "value" });
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("If with else", () => {
|
|
200
|
+
const cond = If(values.enabled, "yes", "no");
|
|
201
|
+
const json = cond.toJSON() as Record<string, unknown>;
|
|
202
|
+
expect(json[HELM_IF_KEY]).toBe(".Values.enabled");
|
|
203
|
+
expect(json.body).toBe("yes");
|
|
204
|
+
expect(json.else).toBe("no");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("Range produces __helm_range marker", () => {
|
|
208
|
+
const r = Range(values.hosts, { host: "item" });
|
|
209
|
+
const json = r.toJSON() as Record<string, unknown>;
|
|
210
|
+
expect(json).toHaveProperty(HELM_RANGE_KEY);
|
|
211
|
+
expect(json[HELM_RANGE_KEY]).toBe(".Values.hosts");
|
|
212
|
+
expect(json.body).toEqual({ host: "item" });
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("With produces __helm_with marker", () => {
|
|
216
|
+
const w = With(values.nodeSelector, { key: "value" });
|
|
217
|
+
const json = w.toJSON() as Record<string, unknown>;
|
|
218
|
+
expect(json).toHaveProperty(HELM_WITH_KEY);
|
|
219
|
+
expect(json[HELM_WITH_KEY]).toBe(".Values.nodeSelector");
|
|
220
|
+
expect(json.body).toEqual({ key: "value" });
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("If has INTRINSIC_MARKER", () => {
|
|
224
|
+
const cond = If(values.x, "y");
|
|
225
|
+
expect(cond[INTRINSIC_MARKER]).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("If with string condition", () => {
|
|
229
|
+
const cond = If(".Values.ingress.enabled", { enabled: true });
|
|
230
|
+
const json = cond.toJSON() as Record<string, unknown>;
|
|
231
|
+
expect(json[HELM_IF_KEY]).toBe(".Values.ingress.enabled");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("Range has INTRINSIC_MARKER", () => {
|
|
235
|
+
const r = Range(values.hosts, { host: "item" });
|
|
236
|
+
expect(r[INTRINSIC_MARKER]).toBe(true);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("With has INTRINSIC_MARKER", () => {
|
|
240
|
+
const w = With(values.nodeSelector, {});
|
|
241
|
+
expect(w[INTRINSIC_MARKER]).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("Capabilities built-in", () => {
|
|
246
|
+
test("Capabilities.KubeVersion.Version", () => {
|
|
247
|
+
expect(Capabilities.KubeVersion.Version.toJSON()).toEqual({
|
|
248
|
+
[HELM_TPL_KEY]: "{{ .Capabilities.KubeVersion.Version }}",
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("Capabilities.APIVersions", () => {
|
|
253
|
+
expect(Capabilities.APIVersions.toJSON()).toEqual({
|
|
254
|
+
[HELM_TPL_KEY]: "{{ .Capabilities.APIVersions }}",
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("Capabilities.HelmVersion.Version", () => {
|
|
259
|
+
expect(Capabilities.HelmVersion.Version.toJSON()).toEqual({
|
|
260
|
+
[HELM_TPL_KEY]: "{{ .Capabilities.HelmVersion.Version }}",
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("has INTRINSIC_MARKER", () => {
|
|
265
|
+
expect(Capabilities[INTRINSIC_MARKER]).toBe(true);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe("Template built-in", () => {
|
|
270
|
+
test("Template.Name", () => {
|
|
271
|
+
expect(Template.Name.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Template.Name }}" });
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("Template.BasePath", () => {
|
|
275
|
+
expect(Template.BasePath.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Template.BasePath }}" });
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe("Files helpers", () => {
|
|
280
|
+
test("filesGet", () => {
|
|
281
|
+
expect(filesGet("config.ini").toJSON()).toEqual({
|
|
282
|
+
[HELM_TPL_KEY]: '{{ .Files.Get "config.ini" }}',
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test("filesGlob", () => {
|
|
287
|
+
expect(filesGlob("conf/*").toJSON()).toEqual({
|
|
288
|
+
[HELM_TPL_KEY]: '{{ .Files.Glob "conf/*" }}',
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("filesAsConfig", () => {
|
|
293
|
+
expect(filesAsConfig("conf/*").toJSON()).toEqual({
|
|
294
|
+
[HELM_TPL_KEY]: '{{ (.Files.Glob "conf/*").AsConfig }}',
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("filesAsSecrets", () => {
|
|
299
|
+
expect(filesAsSecrets("secrets/*").toJSON()).toEqual({
|
|
300
|
+
[HELM_TPL_KEY]: '{{ (.Files.Glob "secrets/*").AsSecrets }}',
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe("ElseIf", () => {
|
|
306
|
+
test("produces __helm_if marker", () => {
|
|
307
|
+
const ei = ElseIf(values.backup, "silver");
|
|
308
|
+
const json = ei.toJSON() as Record<string, unknown>;
|
|
309
|
+
expect(json[HELM_IF_KEY]).toBe(".Values.backup");
|
|
310
|
+
expect(json.body).toBe("silver");
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("with else body", () => {
|
|
314
|
+
const ei = ElseIf(values.backup, "silver", "bronze");
|
|
315
|
+
const json = ei.toJSON() as Record<string, unknown>;
|
|
316
|
+
expect(json[HELM_IF_KEY]).toBe(".Values.backup");
|
|
317
|
+
expect(json.body).toBe("silver");
|
|
318
|
+
expect(json.else).toBe("bronze");
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("with string condition", () => {
|
|
322
|
+
const ei = ElseIf(".Values.x", "yes");
|
|
323
|
+
const json = ei.toJSON() as Record<string, unknown>;
|
|
324
|
+
expect(json[HELM_IF_KEY]).toBe(".Values.x");
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
describe("values proxy pipe chaining", () => {
|
|
329
|
+
test("single pipe on values proxy", () => {
|
|
330
|
+
const v = (values.environment as any).pipe("upper");
|
|
331
|
+
expect(v.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.environment | upper }}" });
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test("double pipe on values proxy", () => {
|
|
335
|
+
const v = (values.environment as any).pipe("upper").pipe("quote");
|
|
336
|
+
expect(v.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.environment | upper | quote }}" });
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("triple pipe on values proxy", () => {
|
|
340
|
+
const v = (values.x as any).pipe("lower").pipe("trimSuffix", ).pipe("quote");
|
|
341
|
+
expect(v.toJSON()).toEqual({
|
|
342
|
+
[HELM_TPL_KEY]: "{{ .Values.x | lower | trimSuffix | quote }}",
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("pipe on nested values proxy", () => {
|
|
347
|
+
const v = (values.image.tag as any).pipe("quote");
|
|
348
|
+
expect(v.toJSON()).toEqual({ [HELM_TPL_KEY]: "{{ .Values.image.tag | quote }}" });
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
describe("withOrder", () => {
|
|
353
|
+
test("returns hook annotations with weight", () => {
|
|
354
|
+
const annotations = withOrder(5);
|
|
355
|
+
expect(annotations["helm.sh/hook"]).toBe("pre-install,pre-upgrade");
|
|
356
|
+
expect(annotations["helm.sh/hook-weight"]).toBe("5");
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("handles negative weights", () => {
|
|
360
|
+
const annotations = withOrder(-10);
|
|
361
|
+
expect(annotations["helm.sh/hook-weight"]).toBe("-10");
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("handles zero weight", () => {
|
|
365
|
+
const annotations = withOrder(0);
|
|
366
|
+
expect(annotations["helm.sh/hook-weight"]).toBe("0");
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe("argoWave", () => {
|
|
371
|
+
test("returns sync wave annotation", () => {
|
|
372
|
+
const annotations = argoWave(2);
|
|
373
|
+
expect(annotations["argocd.argoproj.io/sync-wave"]).toBe("2");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("handles negative waves", () => {
|
|
377
|
+
const annotations = argoWave(-1);
|
|
378
|
+
expect(annotations["argocd.argoproj.io/sync-wave"]).toBe("-1");
|
|
379
|
+
});
|
|
380
|
+
});
|