@intentius/chant-lexicon-helm 0.124.31 → 0.124.33
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/dist/archive-render.js +76 -0
- package/dist/codegen/docs-cli.js +3 -0
- package/dist/codegen/docs.js +113 -0
- package/dist/codegen/generate-cli.js +19 -0
- package/dist/codegen/generate.js +210 -0
- package/dist/codegen/naming.js +29 -0
- package/dist/codegen/package.js +44 -0
- package/dist/codegen/values-type.js +87 -0
- package/dist/commands.js +413 -0
- package/dist/components/builders.js +9 -0
- package/dist/components/capability-plugin.js +17 -0
- package/dist/components/helm-upgrade.js +109 -0
- package/dist/components/index.js +11 -0
- package/dist/composites/catalog.js +1119 -0
- package/dist/composites/helm-batch-job.js +148 -0
- package/dist/composites/helm-crd-lifecycle.js +145 -0
- package/dist/composites/helm-cron-job.js +130 -0
- package/dist/composites/helm-daemon-set.js +125 -0
- package/dist/composites/helm-external-secret.js +63 -0
- package/dist/composites/helm-library.js +24 -0
- package/dist/composites/helm-microservice.js +260 -0
- package/dist/composites/helm-monitored-service.js +188 -0
- package/dist/composites/helm-namespace-env.js +116 -0
- package/dist/composites/helm-secure-ingress.js +86 -0
- package/dist/composites/helm-stateful-service.js +160 -0
- package/dist/composites/helm-web-app.js +201 -0
- package/dist/composites/helm-worker.js +164 -0
- package/dist/composites/index.js +13 -0
- package/dist/config.js +161 -0
- package/dist/coverage.js +37 -0
- package/dist/deep-observe-hooks.js +19 -0
- package/dist/deep-observe.js +55 -0
- package/dist/describe-resources.js +119 -0
- package/dist/describe-stack-status.js +66 -0
- package/dist/detect.js +11 -0
- package/dist/generated/index.js +11 -0
- package/dist/helpers.js +84 -0
- package/dist/import/generator.js +292 -0
- package/dist/import/parser.js +141 -0
- package/dist/import/template-stripper.js +86 -0
- package/dist/index.js +55 -0
- package/dist/integrity.json +2 -2
- package/dist/intrinsics.js +482 -0
- package/dist/lint/audit-catalog.js +33 -0
- package/dist/lint/audit-lineage.js +53 -0
- package/dist/lint/post-synth/helm-helpers.js +60 -0
- package/dist/lint/post-synth/index.js +48 -0
- package/dist/lint/post-synth/whm005-no-empty-wrapper.js +47 -0
- package/dist/lint/post-synth/whm101.js +38 -0
- package/dist/lint/post-synth/whm102.js +26 -0
- package/dist/lint/post-synth/whm103.js +51 -0
- package/dist/lint/post-synth/whm104.js +29 -0
- package/dist/lint/post-synth/whm105.js +23 -0
- package/dist/lint/post-synth/whm201.js +29 -0
- package/dist/lint/post-synth/whm202.js +42 -0
- package/dist/lint/post-synth/whm203.js +32 -0
- package/dist/lint/post-synth/whm204.js +51 -0
- package/dist/lint/post-synth/whm301.js +31 -0
- package/dist/lint/post-synth/whm302.js +33 -0
- package/dist/lint/post-synth/whm401.js +52 -0
- package/dist/lint/post-synth/whm402.js +38 -0
- package/dist/lint/post-synth/whm403.js +38 -0
- package/dist/lint/post-synth/whm404.js +30 -0
- package/dist/lint/post-synth/whm405.js +47 -0
- package/dist/lint/post-synth/whm406.js +27 -0
- package/dist/lint/post-synth/whm407.js +72 -0
- package/dist/lint/post-synth/whm501.js +89 -0
- package/dist/lint/post-synth/whm502.js +85 -0
- package/dist/lint/post-synth/whm503.js +82 -0
- package/dist/lint/post-synth/whm504.js +47 -0
- package/dist/lint/rules/chart-metadata.js +53 -0
- package/dist/lint/rules/no-hardcoded-image.js +50 -0
- package/dist/lint/rules/values-no-helm-tpl.js +78 -0
- package/dist/lint/rules/values-no-secrets.js +65 -0
- package/dist/list-artifacts.js +134 -0
- package/dist/lsp/completions.d.ts.map +1 -1
- package/dist/lsp/completions.js +18 -0
- package/dist/lsp/hover.d.ts.map +1 -1
- package/dist/lsp/hover.js +40 -0
- package/dist/manifest.json +1 -1
- package/dist/op/activities/cluster-probe.js +116 -0
- package/dist/op/activities/helm.js +449 -0
- package/dist/op/activities/index.js +10 -0
- package/dist/op/builders.js +45 -0
- package/dist/package-cli.js +21 -0
- package/dist/pinnability/actions.js +207 -0
- package/dist/pinnability/classify.js +416 -0
- package/dist/pinnability/conditions.js +0 -0
- package/dist/pinnability/index.js +16 -0
- package/dist/pinnability/localize.js +518 -0
- package/dist/pinnability/render-stream.js +53 -0
- package/dist/pinnability/values.js +151 -0
- package/dist/plugin.js +458 -0
- package/dist/project-codegen.js +215 -0
- package/dist/release-observe.js +287 -0
- package/dist/render-diff.js +279 -0
- package/dist/render-digest.js +0 -0
- package/dist/render-records.js +29 -0
- package/dist/render-store.js +401 -0
- package/dist/render-wrapper.js +164 -0
- package/dist/render.js +394 -0
- package/dist/resources.js +109 -0
- package/dist/serializer.js +903 -0
- package/dist/validate-cli.js +17 -0
- package/dist/validate.js +30 -0
- package/dist/values-probe.js +504 -0
- package/dist/wrapper-chart.js +95 -0
- package/package.json +11 -11
- package/src/codegen/package.ts +2 -2
- package/src/lsp/completions.ts +2 -1
- package/src/lsp/hover.ts +2 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `addArchiveHelmRender` — fold a pinned render into a `BuildArchiveManifest`
|
|
3
|
+
* (#1240, epic #1228 Phase 3).
|
|
4
|
+
*
|
|
5
|
+
* #1238 gave a pinned render durable, content-addressed storage
|
|
6
|
+
* (./render-store.ts's `RenderManifest`, modeled on core's
|
|
7
|
+
* `BuildArchiveManifest`). This module is the seam between the two: it folds
|
|
8
|
+
* one render into a *build archive*'s manifest, the same self-contained
|
|
9
|
+
* bundle `docker-build`/`addArchiveTemplate` write into
|
|
10
|
+
* (packages/core/src/components/verbs/build-archive.ts), so a component that
|
|
11
|
+
* composes a `HelmRender` alongside an image/template gets one archive that
|
|
12
|
+
* carries all of it.
|
|
13
|
+
*
|
|
14
|
+
* A rendered chart is a template: it reuses `kind: "template"` rather than
|
|
15
|
+
* adding a new `BuildArchiveEntryKind`. What distinguishes a render from a
|
|
16
|
+
* synthesized CloudFormation/other IaC template is only its media type,
|
|
17
|
+
* `HELM_RENDER_MEDIA_TYPE` — and that is enough. Core's
|
|
18
|
+
* `lifecycle/oras-referrer-lookup.ts` classifies *referrers* (SBOM/
|
|
19
|
+
* provenance/signature) by the referrer's own media type, keyed only by the
|
|
20
|
+
* subject *digest* it discovers against — never by the subject entry's own
|
|
21
|
+
* `kind`/`mediaType`. A render's digest is therefore attachable exactly like
|
|
22
|
+
* an image's: `oras discover <repo>@<renderDigest>` finds the same
|
|
23
|
+
* referrers a signing/attestation pipeline would attach to any other
|
|
24
|
+
* artifact, with no change to that lookup needed (see
|
|
25
|
+
* ./archive-render.test.ts for the referrer-classification proof).
|
|
26
|
+
*
|
|
27
|
+
* Mirrors `addArchiveTemplate` (packages/core/src/components/verbs/build.ts)
|
|
28
|
+
* in every way that matters: a plain, pure function (not a registered
|
|
29
|
+
* capability — rendering happens at chant's existing build/synth time, not
|
|
30
|
+
* as a new deploy-time verb), accumulates onto a caller-supplied manifest so
|
|
31
|
+
* a component's whole build phase shares one archive, and threads an
|
|
32
|
+
* optional `sourceRef` into a `provenance` link (#614). It differs in one
|
|
33
|
+
* respect: rather than recomputing a digest from raw content bytes (core's
|
|
34
|
+
* `contentDigest`), it reuses the render's own `contentDigest` — the real
|
|
35
|
+
* `sha256:` over the canonical rendered stream that `helmContentDigest`
|
|
36
|
+
* already computed and `persistHelmRender` already wrote to the render
|
|
37
|
+
* store — so the archive entry's identity and the render store's identity
|
|
38
|
+
* are the same digest, not two independent hashes of the same bytes.
|
|
39
|
+
*/
|
|
40
|
+
import { addArchiveEntry, createBuildArchiveManifest, } from "@intentius/chant/components/verbs/build-archive";
|
|
41
|
+
/**
|
|
42
|
+
* Media type for a pinned helm render folded into a build archive as a
|
|
43
|
+
* `template`-kind entry. `v1` names the canonical-stream shape
|
|
44
|
+
* `canonicalizeRender` (./render-digest.ts) produces — a versioned suffix so
|
|
45
|
+
* a future incompatible canonicalization can be told apart from this one
|
|
46
|
+
* without guessing from content alone.
|
|
47
|
+
*/
|
|
48
|
+
export const HELM_RENDER_MEDIA_TYPE = "application/vnd.chant.helm-render.v1+yaml";
|
|
49
|
+
/**
|
|
50
|
+
* Fold one pinned render into a build archive's manifest as a
|
|
51
|
+
* `template`-kind entry with `mediaType: HELM_RENDER_MEDIA_TYPE` — the
|
|
52
|
+
* render peer of `addArchiveTemplate`. The entry's digest is the render's
|
|
53
|
+
* own `contentDigest` (real `sha256:` over the canonical rendered stream,
|
|
54
|
+
* ./render-digest.ts's `helmContentDigest`), so the same digest that keys
|
|
55
|
+
* the render store's `sha256-<hex>/` entry (./render-store.ts) also keys
|
|
56
|
+
* this archive entry — one identity, not a second hash of the same bytes.
|
|
57
|
+
* Adding the entry recomputes `manifest.manifestDigest`
|
|
58
|
+
* (`computeManifestDigest`), so the render rolls into the archive's own
|
|
59
|
+
* aggregate identity exactly as an image or template does.
|
|
60
|
+
*/
|
|
61
|
+
export function addArchiveHelmRender(input) {
|
|
62
|
+
const digest = input.render.contentDigest;
|
|
63
|
+
const base = input.manifest ?? createBuildArchiveManifest("unknown");
|
|
64
|
+
const sourceRef = input.sourceRef ?? input.render.sourceRef ?? undefined;
|
|
65
|
+
const provenance = sourceRef
|
|
66
|
+
? { sourceRef, artifactDigest: digest }
|
|
67
|
+
: undefined;
|
|
68
|
+
const manifest = addArchiveEntry(base, {
|
|
69
|
+
kind: "template",
|
|
70
|
+
path: input.path,
|
|
71
|
+
digest,
|
|
72
|
+
mediaType: HELM_RENDER_MEDIA_TYPE,
|
|
73
|
+
...(provenance ? { provenance } : {}),
|
|
74
|
+
});
|
|
75
|
+
return { digest, manifest };
|
|
76
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation generation for the Helm lexicon.
|
|
3
|
+
*
|
|
4
|
+
* Generates Starlight MDX pages for Helm entities using the core docs pipeline.
|
|
5
|
+
*/
|
|
6
|
+
import { dirname, join } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { docsPipeline, writeDocsSite } from "@intentius/chant/codegen/docs";
|
|
9
|
+
function serviceFromType(resourceType) {
|
|
10
|
+
const parts = resourceType.split("::");
|
|
11
|
+
return parts.length >= 2 ? parts[1] : "Helm";
|
|
12
|
+
}
|
|
13
|
+
const overview = `The **Helm** lexicon generates production-quality, parameterized Helm charts from TypeScript.
|
|
14
|
+
|
|
15
|
+
Unlike static manifest generators, the Helm lexicon produces real \`{{ .Values.x }}\` directives, \`values.yaml\`, \`values.schema.json\`, conditional resources, Helm hooks, and standard \`_helpers.tpl\`.
|
|
16
|
+
|
|
17
|
+
Install it with:
|
|
18
|
+
|
|
19
|
+
\`\`\`bash
|
|
20
|
+
npm install --save-dev @intentius/chant-lexicon-helm @intentius/chant-lexicon-k8s
|
|
21
|
+
\`\`\`
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
\`\`\`typescript
|
|
26
|
+
import { Chart, Values } from "@intentius/chant-lexicon-helm";
|
|
27
|
+
import { values, include, printf, toYaml } from "@intentius/chant-lexicon-helm";
|
|
28
|
+
import { Deployment, Service } from "@intentius/chant-lexicon-k8s";
|
|
29
|
+
|
|
30
|
+
export const chart = new Chart({
|
|
31
|
+
apiVersion: "v2",
|
|
32
|
+
name: "my-app",
|
|
33
|
+
version: "0.1.0",
|
|
34
|
+
type: "application",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const valuesSchema = new Values({
|
|
38
|
+
replicaCount: 1,
|
|
39
|
+
image: { repository: "nginx", tag: "", pullPolicy: "IfNotPresent" },
|
|
40
|
+
service: { type: "ClusterIP", port: 80 },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const deployment = new Deployment({
|
|
44
|
+
metadata: {
|
|
45
|
+
name: include("my-app.fullname"),
|
|
46
|
+
labels: include("my-app.labels"),
|
|
47
|
+
},
|
|
48
|
+
spec: {
|
|
49
|
+
replicas: values.replicaCount,
|
|
50
|
+
template: {
|
|
51
|
+
spec: {
|
|
52
|
+
containers: [{
|
|
53
|
+
name: "my-app",
|
|
54
|
+
image: printf("%s:%s", values.image.repository, values.image.tag),
|
|
55
|
+
ports: [{ containerPort: values.service.port, name: "http" }],
|
|
56
|
+
resources: toYaml(values.resources),
|
|
57
|
+
}],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
\`\`\`
|
|
63
|
+
|
|
64
|
+
Build the chart:
|
|
65
|
+
|
|
66
|
+
\`\`\`bash
|
|
67
|
+
chant build
|
|
68
|
+
helm lint dist/
|
|
69
|
+
helm template test dist/
|
|
70
|
+
\`\`\`
|
|
71
|
+
`;
|
|
72
|
+
const outputFormat = `## Output structure
|
|
73
|
+
|
|
74
|
+
The Helm serializer produces a complete chart directory:
|
|
75
|
+
|
|
76
|
+
\`\`\`
|
|
77
|
+
dist/
|
|
78
|
+
Chart.yaml ← from Chart entity
|
|
79
|
+
values.yaml ← from Values entity (defaults)
|
|
80
|
+
values.schema.json ← auto-generated JSON Schema from Values types
|
|
81
|
+
.helmignore
|
|
82
|
+
templates/
|
|
83
|
+
_helpers.tpl ← auto-generated standard helpers
|
|
84
|
+
deployment.yaml ← K8s resources with {{ .Values.x }} directives
|
|
85
|
+
service.yaml
|
|
86
|
+
NOTES.txt ← from HelmNotes entity
|
|
87
|
+
tests/
|
|
88
|
+
test-connection.yaml ← from HelmTest entities
|
|
89
|
+
\`\`\`
|
|
90
|
+
`;
|
|
91
|
+
export async function generateDocs(opts) {
|
|
92
|
+
const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
93
|
+
const config = {
|
|
94
|
+
name: "helm",
|
|
95
|
+
displayName: "Helm",
|
|
96
|
+
description: "Typed constructors for parameterized Helm charts",
|
|
97
|
+
distDir: join(pkgDir, "dist"),
|
|
98
|
+
outDir: join(pkgDir, "docs"),
|
|
99
|
+
basePath: process.env.DOCS_BASE_PATH ?? "/chant/lexicons/helm/",
|
|
100
|
+
overview,
|
|
101
|
+
outputFormat,
|
|
102
|
+
serviceFromType,
|
|
103
|
+
// `lint-rules` under docs/pages/ is helm's rules documentation; the
|
|
104
|
+
// generated `rules` table duplicated it, and helm's own declared rules
|
|
105
|
+
// page was being silently overwritten by that table (#1312).
|
|
106
|
+
suppressPages: ["pseudo-parameters"],
|
|
107
|
+
};
|
|
108
|
+
const result = docsPipeline(config);
|
|
109
|
+
writeDocsSite(config, result);
|
|
110
|
+
if (opts?.verbose) {
|
|
111
|
+
console.error(`Generated ${result.pages.size} documentation pages`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for Helm lexicon generation.
|
|
4
|
+
*/
|
|
5
|
+
import { dirname } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { generate, writeGeneratedFiles } from "./generate.js";
|
|
8
|
+
const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
9
|
+
async function main() {
|
|
10
|
+
const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
|
|
11
|
+
console.error("Generating Helm lexicon (static types)...");
|
|
12
|
+
const result = await generate({ verbose });
|
|
13
|
+
writeGeneratedFiles(result, pkgDir);
|
|
14
|
+
console.error(`Generated ${result.resources} resources, ${result.properties} property types`);
|
|
15
|
+
}
|
|
16
|
+
main().catch((err) => {
|
|
17
|
+
console.error("Generation failed:", err);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helm generation pipeline — static type generation.
|
|
3
|
+
*
|
|
4
|
+
* Unlike AWS/K8s, Helm types are defined statically (no upstream schema to
|
|
5
|
+
* fetch). This module generates lexicon-helm.json, index.d.ts, and index.ts
|
|
6
|
+
* directly from the known Helm resource/property type definitions.
|
|
7
|
+
*/
|
|
8
|
+
import { writeGeneratedArtifacts, } from "@intentius/chant/codegen/generate";
|
|
9
|
+
const HELM_TYPES = [
|
|
10
|
+
{
|
|
11
|
+
resourceType: "Helm::Chart",
|
|
12
|
+
exportName: "Chart",
|
|
13
|
+
kind: "resource",
|
|
14
|
+
description: "Chart.yaml metadata — defines the chart identity, version, and type.",
|
|
15
|
+
props: {
|
|
16
|
+
apiVersion: { type: "string", description: "Chart API version (v2)", required: true },
|
|
17
|
+
name: { type: "string", description: "Chart name", required: true },
|
|
18
|
+
version: { type: "string", description: "Chart version (SemVer)", required: true },
|
|
19
|
+
appVersion: { type: "string", description: "Version of the app deployed by this chart" },
|
|
20
|
+
description: { type: "string", description: "A single-sentence description of this chart" },
|
|
21
|
+
type: { type: "string", description: "Chart type: application or library" },
|
|
22
|
+
keywords: { type: "string[]", description: "Keywords for chart search" },
|
|
23
|
+
home: { type: "string", description: "URL of the project home page" },
|
|
24
|
+
icon: { type: "string", description: "URL to an SVG or PNG image for the chart" },
|
|
25
|
+
deprecated: { type: "boolean", description: "Whether this chart is deprecated" },
|
|
26
|
+
sources: { type: "string[]", description: "URLs to source code for this chart" },
|
|
27
|
+
maintainers: { type: "HelmMaintainerProps[]", description: "List of chart maintainers" },
|
|
28
|
+
annotations: { type: "Record<string, string>", description: "Arbitrary key-value annotations" },
|
|
29
|
+
kubeVersion: { type: "string", description: "SemVer range of compatible Kubernetes versions" },
|
|
30
|
+
condition: { type: "string", description: "YAML path for chart enablement (subcharts)" },
|
|
31
|
+
tags: { type: "string", description: "Tags for grouping charts for enabling/disabling" },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
resourceType: "Helm::Values",
|
|
36
|
+
exportName: "Values",
|
|
37
|
+
kind: "resource",
|
|
38
|
+
description: "Typed values definition — emits values.yaml and values.schema.json.",
|
|
39
|
+
props: {},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
resourceType: "Helm::Test",
|
|
43
|
+
exportName: "HelmTest",
|
|
44
|
+
kind: "resource",
|
|
45
|
+
description: "Helm test pod — annotated with helm.sh/hook: test.",
|
|
46
|
+
props: {
|
|
47
|
+
resource: { type: "object", description: "K8s Pod resource to use as the test" },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
resourceType: "Helm::Notes",
|
|
52
|
+
exportName: "HelmNotes",
|
|
53
|
+
kind: "resource",
|
|
54
|
+
description: "NOTES.txt template content — displayed after helm install.",
|
|
55
|
+
props: {
|
|
56
|
+
content: { type: "string", description: "NOTES.txt content (may contain Go template expressions)", required: true },
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
resourceType: "Helm::Hook",
|
|
61
|
+
exportName: "HelmHook",
|
|
62
|
+
kind: "property",
|
|
63
|
+
description: "Lifecycle hook annotation — wraps a K8s resource with helm.sh/hook annotations.",
|
|
64
|
+
props: {
|
|
65
|
+
hook: { type: "string", description: "Hook type: pre-install, post-install, pre-upgrade, post-upgrade, pre-delete, post-delete, pre-rollback, post-rollback, test", required: true },
|
|
66
|
+
weight: { type: "number", description: "Hook execution order weight" },
|
|
67
|
+
deletePolicy: { type: "string", description: "Hook delete policy: before-hook-creation, hook-succeeded, hook-failed" },
|
|
68
|
+
resource: { type: "object", description: "K8s resource to annotate with the hook", required: true },
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
resourceType: "Helm::Dependency",
|
|
73
|
+
exportName: "HelmDependency",
|
|
74
|
+
kind: "property",
|
|
75
|
+
description: "Chart dependency entry for Chart.yaml dependencies.",
|
|
76
|
+
props: {
|
|
77
|
+
name: { type: "string", description: "Dependency chart name", required: true },
|
|
78
|
+
version: { type: "string", description: "Dependency version range (SemVer)", required: true },
|
|
79
|
+
repository: { type: "string", description: "Repository URL", required: true },
|
|
80
|
+
condition: { type: "string", description: "YAML path that enables/disables this dependency" },
|
|
81
|
+
tags: { type: "string[]", description: "Tags for grouping dependencies" },
|
|
82
|
+
enabled: { type: "boolean", description: "Whether this dependency is enabled" },
|
|
83
|
+
importValues: { type: "unknown[]", description: "Values to import from dependency (import-values in Chart.yaml)" },
|
|
84
|
+
alias: { type: "string", description: "Alias for the dependency" },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
resourceType: "Helm::Maintainer",
|
|
89
|
+
exportName: "HelmMaintainer",
|
|
90
|
+
kind: "property",
|
|
91
|
+
description: "Chart maintainer entry for Chart.yaml maintainers.",
|
|
92
|
+
props: {
|
|
93
|
+
name: { type: "string", description: "Maintainer name", required: true },
|
|
94
|
+
email: { type: "string", description: "Maintainer email" },
|
|
95
|
+
url: { type: "string", description: "Maintainer URL" },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
resourceType: "Helm::CRD",
|
|
100
|
+
exportName: "HelmCRD",
|
|
101
|
+
kind: "resource",
|
|
102
|
+
description: "Custom Resource Definition — placed in the crds/ directory.",
|
|
103
|
+
props: {
|
|
104
|
+
content: { type: "string", description: "CRD YAML content", required: true },
|
|
105
|
+
filename: { type: "string", description: "CRD filename (e.g. mycrd.yaml)" },
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
// ── Generate artifacts ──────────────────────────────────
|
|
110
|
+
function generateLexiconJSON() {
|
|
111
|
+
const registry = {};
|
|
112
|
+
for (const t of HELM_TYPES) {
|
|
113
|
+
registry[t.exportName] = {
|
|
114
|
+
resourceType: t.resourceType,
|
|
115
|
+
kind: t.kind,
|
|
116
|
+
description: t.description,
|
|
117
|
+
props: t.props,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return JSON.stringify(registry, null, 2);
|
|
121
|
+
}
|
|
122
|
+
function generateTypesDTS() {
|
|
123
|
+
const lines = [
|
|
124
|
+
'// Auto-generated Helm lexicon type declarations',
|
|
125
|
+
'// Do not edit manually',
|
|
126
|
+
'',
|
|
127
|
+
'/** Minimal Declarable interface for generated types. */',
|
|
128
|
+
'interface Declarable { [key: string]: unknown; }',
|
|
129
|
+
'',
|
|
130
|
+
];
|
|
131
|
+
for (const t of HELM_TYPES) {
|
|
132
|
+
const className = t.exportName;
|
|
133
|
+
const propsInterface = `${className}Props`;
|
|
134
|
+
// Generate props interface
|
|
135
|
+
lines.push(`/** ${t.description} */`);
|
|
136
|
+
lines.push(`export interface ${propsInterface} {`);
|
|
137
|
+
for (const [key, prop] of Object.entries(t.props)) {
|
|
138
|
+
const optional = prop.required ? "" : "?";
|
|
139
|
+
lines.push(` /** ${prop.description} */`);
|
|
140
|
+
lines.push(` ${key}${optional}: ${prop.type};`);
|
|
141
|
+
}
|
|
142
|
+
if (Object.keys(t.props).length === 0) {
|
|
143
|
+
lines.push(" [key: string]: unknown;");
|
|
144
|
+
}
|
|
145
|
+
lines.push("}");
|
|
146
|
+
lines.push("");
|
|
147
|
+
// Generate class declaration
|
|
148
|
+
lines.push(`/** ${t.description} */`);
|
|
149
|
+
lines.push(`export declare const ${className}: new (props: ${propsInterface}) => Declarable;`);
|
|
150
|
+
lines.push("");
|
|
151
|
+
}
|
|
152
|
+
return lines.join("\n");
|
|
153
|
+
}
|
|
154
|
+
function generateRuntimeIndex() {
|
|
155
|
+
const lines = [
|
|
156
|
+
'// Auto-generated Helm lexicon runtime index',
|
|
157
|
+
'// Do not edit manually',
|
|
158
|
+
'',
|
|
159
|
+
'import { createResource, createProperty } from "@intentius/chant/runtime";',
|
|
160
|
+
'',
|
|
161
|
+
];
|
|
162
|
+
for (const t of HELM_TYPES) {
|
|
163
|
+
if (t.kind === "resource") {
|
|
164
|
+
lines.push(`export const ${t.exportName} = createResource("${t.resourceType}", "helm", {});`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
lines.push(`export const ${t.exportName} = createProperty("${t.resourceType}", "helm");`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
lines.push("");
|
|
171
|
+
return lines.join("\n");
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Run the Helm generation pipeline.
|
|
175
|
+
* Since Helm types are static, this doesn't fetch anything.
|
|
176
|
+
*/
|
|
177
|
+
export async function generate(opts = {}) {
|
|
178
|
+
const log = opts.verbose
|
|
179
|
+
? (msg) => console.error(msg)
|
|
180
|
+
: (_msg) => { };
|
|
181
|
+
log("Generating Helm lexicon (static types)...");
|
|
182
|
+
const lexiconJSON = generateLexiconJSON();
|
|
183
|
+
const typesDTS = generateTypesDTS();
|
|
184
|
+
const indexTS = generateRuntimeIndex();
|
|
185
|
+
const resourceCount = HELM_TYPES.filter((t) => t.kind === "resource").length;
|
|
186
|
+
const propertyCount = HELM_TYPES.filter((t) => t.kind === "property").length;
|
|
187
|
+
log(`Generated ${resourceCount} resources, ${propertyCount} property types`);
|
|
188
|
+
return {
|
|
189
|
+
lexiconJSON,
|
|
190
|
+
typesDTS,
|
|
191
|
+
indexTS,
|
|
192
|
+
resources: resourceCount,
|
|
193
|
+
properties: propertyCount,
|
|
194
|
+
enums: 0,
|
|
195
|
+
warnings: [],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Write generated artifacts to disk.
|
|
200
|
+
*/
|
|
201
|
+
export function writeGeneratedFiles(result, baseDir) {
|
|
202
|
+
writeGeneratedArtifacts({
|
|
203
|
+
baseDir,
|
|
204
|
+
files: {
|
|
205
|
+
"lexicon-helm.json": result.lexiconJSON,
|
|
206
|
+
"index.d.ts": result.typesDTS,
|
|
207
|
+
"index.ts": result.indexTS,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helm naming strategy — trivial mapping since Helm types are few and static.
|
|
3
|
+
*
|
|
4
|
+
* Maps Helm::{TypeName} → TypeName (e.g. Helm::Chart → Chart).
|
|
5
|
+
*/
|
|
6
|
+
import { NamingStrategy as CoreNamingStrategy, } from "@intentius/chant/codegen/naming";
|
|
7
|
+
const helmNamingConfig = {
|
|
8
|
+
priorityNames: {
|
|
9
|
+
"Helm::Chart": "Chart",
|
|
10
|
+
"Helm::Values": "Values",
|
|
11
|
+
"Helm::Test": "HelmTest",
|
|
12
|
+
"Helm::Notes": "HelmNotes",
|
|
13
|
+
"Helm::Hook": "HelmHook",
|
|
14
|
+
"Helm::Dependency": "HelmDependency",
|
|
15
|
+
},
|
|
16
|
+
priorityAliases: {},
|
|
17
|
+
priorityPropertyAliases: {},
|
|
18
|
+
serviceAbbreviations: {},
|
|
19
|
+
shortName: (typeName) => typeName.split("::").pop() ?? typeName,
|
|
20
|
+
serviceName: (_typeName) => "Helm",
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Helm naming strategy.
|
|
24
|
+
*/
|
|
25
|
+
export class NamingStrategy extends CoreNamingStrategy {
|
|
26
|
+
constructor(inputs) {
|
|
27
|
+
super(inputs, helmNamingConfig);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helm lexicon packaging — delegates to core packagePipeline
|
|
3
|
+
* with Helm-specific manifest building and skill collection.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import { join, dirname } from "path";
|
|
7
|
+
import { moduleSourceDir } from "@intentius/chant/module-source";
|
|
8
|
+
import { helmPlugin } from "../plugin.js";
|
|
9
|
+
import { packagePipeline, collectSkills, } from "@intentius/chant/codegen/package";
|
|
10
|
+
import { generate } from "./generate.js";
|
|
11
|
+
const pkgDir = dirname(moduleSourceDir(import.meta.url));
|
|
12
|
+
/**
|
|
13
|
+
* Package the Helm lexicon into a distributable BundleSpec.
|
|
14
|
+
*/
|
|
15
|
+
export async function packageLexicon(opts = {}) {
|
|
16
|
+
const pkgJson = JSON.parse(readFileSync(join(pkgDir, "..", "package.json"), "utf-8"));
|
|
17
|
+
return packagePipeline({
|
|
18
|
+
generate: (genOpts) => generate({ verbose: genOpts.verbose, force: genOpts.force }),
|
|
19
|
+
buildManifest: (_genResult) => {
|
|
20
|
+
return {
|
|
21
|
+
name: "helm",
|
|
22
|
+
version: pkgJson.version ?? "0.0.0",
|
|
23
|
+
chantVersion: ">=0.1.0",
|
|
24
|
+
namespace: "Helm",
|
|
25
|
+
// Derived from the plugin's own registration (chant #1067) — this
|
|
26
|
+
// used to be a hand-maintained array of 7 entries, independent of
|
|
27
|
+
// ../plugin.ts's real registration (which has grown to 22) and
|
|
28
|
+
// never updated to match. That drift is exactly why an
|
|
29
|
+
// independent second source of truth for the same data is
|
|
30
|
+
// dangerous: the generated intrinsics doc page (chant #1062) was
|
|
31
|
+
// silently reporting "7 intrinsic functions" for a lexicon that
|
|
32
|
+
// ships 22.
|
|
33
|
+
intrinsics: helmPlugin.intrinsics?.() ?? [],
|
|
34
|
+
pseudoParameters: {},
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
srcDir: pkgDir,
|
|
38
|
+
collectSkills: () => {
|
|
39
|
+
const skillDefs = helmPlugin.skills?.() ?? [];
|
|
40
|
+
return collectSkills(skillDefs);
|
|
41
|
+
},
|
|
42
|
+
version: pkgJson.version ?? "0.0.0",
|
|
43
|
+
}, opts);
|
|
44
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A TypeScript type for a chart's values when the chart ships no
|
|
3
|
+
* `values.schema.json`, inferred from its `values.yaml` defaults.
|
|
4
|
+
*
|
|
5
|
+
* Every member is optional, since an override sets any subset. An object's
|
|
6
|
+
* keys are the ones `values.yaml` lists and no others, so a misspelled key
|
|
7
|
+
* fails to typecheck. A default that says nothing about its type becomes
|
|
8
|
+
* `unknown`: a `null` or empty value (`key:` or `key: ~`) is `unknown`, an
|
|
9
|
+
* empty object (`podAnnotations: {}`) a map of `unknown`, an empty list an
|
|
10
|
+
* array of `unknown`. An empty string is still a string. Keys are quoted
|
|
11
|
+
* when they are not identifiers and never renamed.
|
|
12
|
+
*/
|
|
13
|
+
import { tsPropertyKey } from "@intentius/chant/codegen/json-schema-to-ts";
|
|
14
|
+
/** The type expression for a parsed `values.yaml` document. */
|
|
15
|
+
export function inferValuesType(values) {
|
|
16
|
+
if (values === null || values === undefined)
|
|
17
|
+
return "Record<string, unknown>";
|
|
18
|
+
return render(shape(values), "");
|
|
19
|
+
}
|
|
20
|
+
function shape(value) {
|
|
21
|
+
if (value === null || value === undefined)
|
|
22
|
+
return { kind: "unknown" };
|
|
23
|
+
if (typeof value === "string")
|
|
24
|
+
return { kind: "scalar", type: "string" };
|
|
25
|
+
if (typeof value === "number")
|
|
26
|
+
return { kind: "scalar", type: "number" };
|
|
27
|
+
if (typeof value === "boolean")
|
|
28
|
+
return { kind: "scalar", type: "boolean" };
|
|
29
|
+
if (value instanceof Date)
|
|
30
|
+
return { kind: "scalar", type: "string" };
|
|
31
|
+
if (Array.isArray(value)) {
|
|
32
|
+
if (value.length === 0)
|
|
33
|
+
return { kind: "array", element: { kind: "unknown" } };
|
|
34
|
+
return { kind: "array", element: value.map(shape).reduce(merge) };
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === "object") {
|
|
37
|
+
const entries = Object.entries(value);
|
|
38
|
+
if (entries.length === 0)
|
|
39
|
+
return { kind: "map" };
|
|
40
|
+
return { kind: "object", members: new Map(entries.map(([k, v]) => [k, shape(v)])) };
|
|
41
|
+
}
|
|
42
|
+
return { kind: "unknown" };
|
|
43
|
+
}
|
|
44
|
+
/** Combine the shapes of two list elements: objects merge their keys, anything else unions. */
|
|
45
|
+
function merge(a, b) {
|
|
46
|
+
if (a.kind === "unknown" || b.kind === "unknown")
|
|
47
|
+
return { kind: "unknown" };
|
|
48
|
+
if (a.kind === "object" && b.kind === "object") {
|
|
49
|
+
const members = new Map(a.members);
|
|
50
|
+
for (const [k, s] of b.members)
|
|
51
|
+
members.set(k, members.has(k) ? merge(members.get(k), s) : s);
|
|
52
|
+
return { kind: "object", members };
|
|
53
|
+
}
|
|
54
|
+
if (a.kind === "scalar" && b.kind === "scalar" && a.type === b.type)
|
|
55
|
+
return a;
|
|
56
|
+
if (a.kind === "map" && b.kind === "map")
|
|
57
|
+
return a;
|
|
58
|
+
if (a.kind === "array" && b.kind === "array")
|
|
59
|
+
return { kind: "array", element: merge(a.element, b.element) };
|
|
60
|
+
const shapes = [...(a.kind === "union" ? a.shapes : [a]), ...(b.kind === "union" ? b.shapes : [b])];
|
|
61
|
+
return { kind: "union", shapes };
|
|
62
|
+
}
|
|
63
|
+
function render(s, indent) {
|
|
64
|
+
switch (s.kind) {
|
|
65
|
+
case "unknown":
|
|
66
|
+
return "unknown";
|
|
67
|
+
case "scalar":
|
|
68
|
+
return s.type;
|
|
69
|
+
case "map":
|
|
70
|
+
return "Record<string, unknown>";
|
|
71
|
+
case "array": {
|
|
72
|
+
const element = render(s.element, indent);
|
|
73
|
+
return /^[A-Za-z]+$/.test(element) ? `${element}[]` : `Array<${element}>`;
|
|
74
|
+
}
|
|
75
|
+
case "union":
|
|
76
|
+
return [...new Set(s.shapes.map((x) => render(x, indent)))].join(" | ");
|
|
77
|
+
case "object": {
|
|
78
|
+
const inner = indent + " ";
|
|
79
|
+
const lines = ["{"];
|
|
80
|
+
for (const [key, member] of s.members) {
|
|
81
|
+
lines.push(`${inner}${tsPropertyKey(key)}?: ${render(member, inner)};`);
|
|
82
|
+
}
|
|
83
|
+
lines.push(`${indent}}`);
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|