@intentius/chant-lexicon-k8s 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/composites/index.d.ts +2 -0
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/operator-stack.d.ts +183 -0
- package/dist/composites/operator-stack.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +35 -35
- package/dist/lint/post-synth/argo-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/crd-schema-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/k8s-helpers.d.ts +28 -5
- package/dist/lint/post-synth/k8s-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8005.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8006.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8041.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8042.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8101.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8102.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8103.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8104.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8105.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8201.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8202.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8203.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8204.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8205.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8207.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8208.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8209.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8301.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8302.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8303.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8304.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8305.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8306.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8401.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8402.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8403.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8404.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8405.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8406.d.ts.map +1 -1
- package/dist/lint/post-synth/wk8407.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/op/builders.d.ts +69 -0
- package/dist/op/builders.d.ts.map +1 -0
- package/dist/rules/argo-helpers.ts +2 -6
- package/dist/rules/crd-schema-helpers.ts +4 -6
- package/dist/rules/k8s-helpers.ts +32 -23
- package/dist/rules/wk8005.ts +27 -32
- package/dist/rules/wk8006.ts +33 -38
- package/dist/rules/wk8041.ts +19 -24
- package/dist/rules/wk8042.ts +17 -22
- package/dist/rules/wk8101.ts +29 -34
- package/dist/rules/wk8102.ts +13 -18
- package/dist/rules/wk8103.ts +17 -22
- package/dist/rules/wk8104.ts +30 -35
- package/dist/rules/wk8105.ts +16 -21
- package/dist/rules/wk8201.ts +26 -31
- package/dist/rules/wk8202.ts +17 -22
- package/dist/rules/wk8203.ts +17 -22
- package/dist/rules/wk8204.ts +46 -51
- package/dist/rules/wk8205.ts +27 -32
- package/dist/rules/wk8207.ts +14 -19
- package/dist/rules/wk8208.ts +14 -19
- package/dist/rules/wk8209.ts +14 -19
- package/dist/rules/wk8301.ts +25 -30
- package/dist/rules/wk8302.ts +19 -24
- package/dist/rules/wk8303.ts +23 -26
- package/dist/rules/wk8304.ts +35 -40
- package/dist/rules/wk8305.ts +39 -42
- package/dist/rules/wk8306.ts +20 -25
- package/dist/rules/wk8401.ts +59 -64
- package/dist/rules/wk8402.ts +13 -18
- package/dist/rules/wk8403.ts +32 -37
- package/dist/rules/wk8404.ts +15 -21
- package/dist/rules/wk8405.ts +15 -18
- package/dist/rules/wk8406.ts +29 -34
- package/dist/rules/wk8407.ts +17 -22
- package/dist/serializer.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/composites/composites.test.ts +283 -0
- package/src/composites/index.ts +4 -0
- package/src/composites/operator-stack.ts +385 -0
- package/src/index.ts +11 -0
- package/src/lint/post-synth/argo-helpers.ts +2 -6
- package/src/lint/post-synth/crd-schema-helpers.ts +4 -6
- package/src/lint/post-synth/k8s-helpers.test.ts +26 -9
- package/src/lint/post-synth/k8s-helpers.ts +32 -23
- package/src/lint/post-synth/wk8005.ts +27 -32
- package/src/lint/post-synth/wk8006.ts +33 -38
- package/src/lint/post-synth/wk8041.ts +19 -24
- package/src/lint/post-synth/wk8042.ts +17 -22
- package/src/lint/post-synth/wk8101.ts +29 -34
- package/src/lint/post-synth/wk8102.ts +13 -18
- package/src/lint/post-synth/wk8103.ts +17 -22
- package/src/lint/post-synth/wk8104.ts +30 -35
- package/src/lint/post-synth/wk8105.ts +16 -21
- package/src/lint/post-synth/wk8201.ts +26 -31
- package/src/lint/post-synth/wk8202.ts +17 -22
- package/src/lint/post-synth/wk8203.ts +17 -22
- package/src/lint/post-synth/wk8204.ts +46 -51
- package/src/lint/post-synth/wk8205.ts +27 -32
- package/src/lint/post-synth/wk8207.ts +14 -19
- package/src/lint/post-synth/wk8208.ts +14 -19
- package/src/lint/post-synth/wk8209.ts +14 -19
- package/src/lint/post-synth/wk8301.ts +25 -30
- package/src/lint/post-synth/wk8302.ts +19 -24
- package/src/lint/post-synth/wk8303.ts +23 -26
- package/src/lint/post-synth/wk8304.ts +35 -40
- package/src/lint/post-synth/wk8305.ts +39 -42
- package/src/lint/post-synth/wk8306.ts +20 -25
- package/src/lint/post-synth/wk8401.ts +59 -64
- package/src/lint/post-synth/wk8402.ts +13 -18
- package/src/lint/post-synth/wk8403.ts +32 -37
- package/src/lint/post-synth/wk8404.ts +15 -21
- package/src/lint/post-synth/wk8405.ts +15 -18
- package/src/lint/post-synth/wk8406.ts +29 -34
- package/src/lint/post-synth/wk8407.ts +17 -22
- package/src/op/builders.test.ts +82 -0
- package/src/op/builders.ts +95 -0
- package/src/serializer.ts +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wk8404.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/wk8404.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"wk8404.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/wk8404.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAU9G,eAAO,MAAM,MAAM,EAAE,cA4BpB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wk8405.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/wk8405.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAc9G,eAAO,MAAM,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"wk8405.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/wk8405.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAc9G,eAAO,MAAM,MAAM,EAAE,cA8BpB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wk8406.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/wk8406.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAG9G,eAAO,MAAM,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"wk8406.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/wk8406.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAG9G,eAAO,MAAM,MAAM,EAAE,cAyCpB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wk8407.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/wk8407.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAM9G;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAS9D;AAED,eAAO,MAAM,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"wk8407.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/wk8407.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAM9G;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAS9D;AAED,eAAO,MAAM,MAAM,EAAE,cA8BpB,CAAC"}
|
package/dist/manifest.json
CHANGED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed step-builder wrappers for this lexicon's activities (chant #1288
|
|
3
|
+
* Stage 2 — "regenerate the step builders as fully typed wrappers"). Core
|
|
4
|
+
* cannot import a lexicon's `*Args` types (lexicons depend on core, not the
|
|
5
|
+
* other way around — see `packages/core/src/op/activity-contract.ts`'s module
|
|
6
|
+
* doc for the same layering constraint on Stage 1's contracts), so a fully
|
|
7
|
+
* typed `kubectlApply`/`waitForReady`/`ensureSecret` has to live beside the
|
|
8
|
+
* `*Args` interface it derives from: here, next to `./activities/kubectl.ts`
|
|
9
|
+
* etc. `opts`'s type in each wrapper below IS the activity's own `*Args`
|
|
10
|
+
* interface (via `Omit`/`WithStepRefs`) — never a hand-restated mirror — so
|
|
11
|
+
* `kubectlApply`/`waitForReady`/`ensureSecret`'s full option surface (the
|
|
12
|
+
* dozen-plus cross-field options on `kubectlApply` that Stage 1's zod
|
|
13
|
+
* contracts deliberately skipped rather than duplicate) is authoring-time
|
|
14
|
+
* typed for free, and stays in sync with the implementation by construction.
|
|
15
|
+
*
|
|
16
|
+
* Every field also accepts a {@link StepOutputRef} in its place
|
|
17
|
+
* ({@link WithStepRefs}, chant #1950) — a later step's args can consume an
|
|
18
|
+
* earlier step's declared output.
|
|
19
|
+
*
|
|
20
|
+
* `core`'s own `kubectlApply`/`waitForReady`/`ensureSecret` (in
|
|
21
|
+
* `@intentius/chant/op`, re-exported from `@intentius/chant-lexicon-temporal`
|
|
22
|
+
* for lexicon-agnostic single-import convenience) are UNCHANGED and produce
|
|
23
|
+
* byte-identical `ActivityStep` output for the same inputs — these are purely
|
|
24
|
+
* additive. Deliberately not swapped into the temporal barrel: that would
|
|
25
|
+
* make `@intentius/chant-lexicon-temporal` depend on this package (and on
|
|
26
|
+
* helm, for `helmInstall`) at runtime, which is exactly the "a worker image
|
|
27
|
+
* needs no kubectl binary" / "temporal stays product-agnostic" property the
|
|
28
|
+
* `op/activities/index.ts` module docs across this repo call out on purpose
|
|
29
|
+
* (activities were moved OUT of temporal into per-product lexicons for this
|
|
30
|
+
* reason; the step-builder layer shouldn't reintroduce the coupling from the
|
|
31
|
+
* other direction). An author who wants the typed surface imports it from
|
|
32
|
+
* here — `@intentius/chant-lexicon-k8s` — which a project using `kubectlApply`
|
|
33
|
+
* already depends on; existing `@intentius/chant-lexicon-temporal` imports
|
|
34
|
+
* keep working exactly as before, opting in only if the import is changed.
|
|
35
|
+
*/
|
|
36
|
+
import { type ActivityStep, type NamedActivityStep, type WithStepRefs } from "@intentius/chant/op";
|
|
37
|
+
import type { KubectlApplyArgs } from "./activities/kubectl.js";
|
|
38
|
+
import type { WaitForReadyArgs } from "./activities/wait-for-ready.js";
|
|
39
|
+
import type { EnsureSecretActivityArgs } from "./activities/ensure-secret.js";
|
|
40
|
+
/** Extra opts every wrapper below accepts alongside its activity's own fields. */
|
|
41
|
+
type StepOpts = {
|
|
42
|
+
profile?: ActivityStep["profile"];
|
|
43
|
+
id?: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Run `kubectl apply -f <manifest>` — the fully typed twin of core's
|
|
47
|
+
* `kubectlApply` step-builder shortcut (chant #1288 Stage 2). `opts` is
|
|
48
|
+
* {@link KubectlApplyArgs} itself (minus the positional `manifest`) — every
|
|
49
|
+
* field `applyManifest` accepts, including `deleteMode`, `force`,
|
|
50
|
+
* `fieldManager`, `documents`, is authoring-time typed here, not just the
|
|
51
|
+
* subset a hand-written zod mirror would bother restating. Defaults to the
|
|
52
|
+
* `longInfra` profile (override via `opts.profile`).
|
|
53
|
+
*/
|
|
54
|
+
export declare const kubectlApply: (manifest: string, opts?: WithStepRefs<Omit<KubectlApplyArgs, "manifest">> & StepOpts) => NamedActivityStep;
|
|
55
|
+
/**
|
|
56
|
+
* Poll a Kubernetes resource until it reports ready, driven by a data-only
|
|
57
|
+
* readiness spec (CRD-aware; #365) — the fully typed twin of core's
|
|
58
|
+
* `waitForReady`. `opts` is {@link WaitForReadyArgs} itself, minus the
|
|
59
|
+
* positional `kind`/`name`. Defaults to the `k8sWait` profile.
|
|
60
|
+
*/
|
|
61
|
+
export declare const waitForReady: (kind: string, name: string, opts?: WithStepRefs<Omit<WaitForReadyArgs, "kind" | "name">> & StepOpts) => NamedActivityStep;
|
|
62
|
+
/**
|
|
63
|
+
* Ensure a `generated-once` secret exists in the target store (#1829) — the
|
|
64
|
+
* fully typed twin of core's `ensureSecret`. `opts` is
|
|
65
|
+
* {@link EnsureSecretActivityArgs} itself, minus the positional `name`/`keys`.
|
|
66
|
+
*/
|
|
67
|
+
export declare const ensureSecret: (name: string, keys: string[], opts?: WithStepRefs<Omit<EnsureSecretActivityArgs, "name" | "keys">> & StepOpts) => NamedActivityStep;
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=builders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAE3E,kFAAkF;AAClF,KAAK,QAAQ,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,EAChB,OAAO,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC,GAAG,QAAQ,KACjE,iBAGF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,QAAQ,KACtE,iBAGF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,MAAM,MAAM,EAAE,EACd,OAAO,YAAY,CAAC,IAAI,CAAC,wBAAwB,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,QAAQ,KAC9E,iBAGF,CAAC"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Excluded from check auto-discovery by the "helper" filename filter.
|
|
7
7
|
*/
|
|
8
8
|
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
9
|
-
import {
|
|
9
|
+
import { docsToManifests, type K8sManifest } from "./k8s-helpers";
|
|
10
10
|
|
|
11
11
|
/** The always-present, in-cluster Argo destination. */
|
|
12
12
|
export const IN_CLUSTER_SERVER = "https://kubernetes.default.svc";
|
|
@@ -17,11 +17,7 @@ export const CLUSTER_SECRET_TYPE_LABEL = "argocd.argoproj.io/secret-type";
|
|
|
17
17
|
|
|
18
18
|
/** All manifests across every lexicon output. */
|
|
19
19
|
export function allManifests(ctx: PostSynthContext): K8sManifest[] {
|
|
20
|
-
|
|
21
|
-
for (const [, output] of ctx.outputs) {
|
|
22
|
-
manifests.push(...parseK8sManifests(getPrimaryOutput(output)));
|
|
23
|
-
}
|
|
24
|
-
return manifests;
|
|
20
|
+
return docsToManifests(ctx);
|
|
25
21
|
}
|
|
26
22
|
|
|
27
23
|
/** Manifests of a given Argo kind. */
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { createRequire } from "module";
|
|
15
15
|
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
16
16
|
import type { CrdFieldSchema } from "../../spec/parse";
|
|
17
|
-
import {
|
|
17
|
+
import { docsToManifests, type K8sManifest } from "./k8s-helpers";
|
|
18
18
|
|
|
19
19
|
export type { CrdFieldSchema };
|
|
20
20
|
|
|
@@ -70,11 +70,9 @@ export function specSchemaFor(manifest: K8sManifest): CrdFieldSchema | undefined
|
|
|
70
70
|
/** Every manifest in the build that has a shipped spec schema, with that schema. */
|
|
71
71
|
export function customResources(ctx: PostSynthContext): Array<{ manifest: K8sManifest; schema: CrdFieldSchema }> {
|
|
72
72
|
const out: Array<{ manifest: K8sManifest; schema: CrdFieldSchema }> = [];
|
|
73
|
-
for (const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (schema) out.push({ manifest, schema });
|
|
77
|
-
}
|
|
73
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
74
|
+
const schema = specSchemaFor(manifest);
|
|
75
|
+
if (schema) out.push({ manifest, schema });
|
|
78
76
|
}
|
|
79
77
|
return out;
|
|
80
78
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared helpers for Kubernetes post-synthesis lint rules.
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
5
|
-
* extraction logic that handles all common workload types.
|
|
4
|
+
* Provides the `ctx.docs` → `K8sManifest[]` bridge every check reads through,
|
|
5
|
+
* and container extraction logic that handles all common workload types.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { parseOutputDocs, type PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
9
9
|
export { getPrimaryOutput, getAdditionalFiles } from "@intentius/chant/lint/post-synth";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -45,27 +45,36 @@ export interface K8sContainer {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* Every parsed Kubernetes manifest across all lexicon outputs, via `ctx.docs`
|
|
49
|
+
* (chant #975) — the shared, parse-once view every k8s post-synth check
|
|
50
|
+
* reads, in place of the old per-check `parseK8sManifests(getPrimaryOutput(output))`
|
|
51
|
+
* splitter (chant #976). Two documents are dropped rather than passed through
|
|
52
|
+
* as a "manifest":
|
|
53
|
+
*
|
|
54
|
+
* - `d.error` — a document `parseOutputDocs` could not make sense of.
|
|
55
|
+
* - `d.file` — a document that came from a `SerializerResult.files` sidecar
|
|
56
|
+
* (a committed-encrypted secret's ciphertext, in this lexicon) rather than
|
|
57
|
+
* a lexicon's primary output. `parseK8sManifests` only ever saw
|
|
58
|
+
* `getPrimaryOutput(output)`, so a sidecar was invisible to every general
|
|
59
|
+
* manifest-consuming check — see `getAdditionalFiles`'s doc comment on why
|
|
60
|
+
* that separation is deliberate. Only `sops-helpers.ts`'s
|
|
61
|
+
* `resolveEncryptedSecretClaims` (WK8504/WK8503's producer join) reads
|
|
62
|
+
* `.files` explicitly; this bridge must keep it that way.
|
|
63
|
+
*
|
|
64
|
+
* `ctx.docs` is populated by every context chant itself builds (`chant
|
|
65
|
+
* build`, `@intentius/chant-test-utils`'s `createPostSynthContext` /
|
|
66
|
+
* `makePostSynthCtx*`) — see `PostSynthContext.docs`'s doc comment for why
|
|
67
|
+
* it is still optional at the type level. A handful of this lexicon's own
|
|
68
|
+
* hand-rolled `PostSynthContext` test literals (`post-synth.test.ts`'s
|
|
69
|
+
* `makeCtx`) predate that field and never set it; for those, fall back to
|
|
70
|
+
* parsing `ctx.outputs` directly with the same `parseOutputDocs` primitive,
|
|
71
|
+
* rather than silently returning no manifests.
|
|
50
72
|
*/
|
|
51
|
-
export function
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const trimmed = doc.trim();
|
|
57
|
-
if (trimmed === "" || trimmed === "---") continue;
|
|
58
|
-
try {
|
|
59
|
-
const parsed = parseYAML(trimmed);
|
|
60
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
61
|
-
manifests.push(parsed as K8sManifest);
|
|
62
|
-
}
|
|
63
|
-
} catch {
|
|
64
|
-
// Skip unparseable documents
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return manifests;
|
|
73
|
+
export function docsToManifests(ctx: PostSynthContext): K8sManifest[] {
|
|
74
|
+
const docs = ctx.docs ?? parseOutputDocs(ctx.outputs);
|
|
75
|
+
return docs
|
|
76
|
+
.filter((d) => !d.error && !d.file)
|
|
77
|
+
.map((d) => d.value as K8sManifest);
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
/**
|
package/dist/rules/wk8005.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
10
|
-
import {
|
|
10
|
+
import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
|
|
11
11
|
|
|
12
12
|
const SENSITIVE_NAME_PATTERN = /password|token|key|secret/i;
|
|
13
13
|
|
|
@@ -18,37 +18,32 @@ export const wk8005: PostSynthCheck = {
|
|
|
18
18
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
19
19
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
20
20
|
|
|
21
|
-
for (const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has hardcoded value for sensitive env var "${envVar.name}" — use secretKeyRef instead`,
|
|
48
|
-
entity: resourceName,
|
|
49
|
-
lexicon: "k8s",
|
|
50
|
-
});
|
|
51
|
-
}
|
|
21
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
22
|
+
if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
|
|
23
|
+
|
|
24
|
+
const containers = extractContainers(manifest);
|
|
25
|
+
const resourceName = manifest.metadata?.name ?? manifest.kind;
|
|
26
|
+
|
|
27
|
+
for (const container of containers) {
|
|
28
|
+
if (!Array.isArray(container.env)) continue;
|
|
29
|
+
|
|
30
|
+
for (const envVar of container.env) {
|
|
31
|
+
if (
|
|
32
|
+
typeof envVar.name === "string" &&
|
|
33
|
+
SENSITIVE_NAME_PATTERN.test(envVar.name) &&
|
|
34
|
+
envVar.value !== undefined &&
|
|
35
|
+
envVar.value !== null &&
|
|
36
|
+
typeof envVar.value === "string" &&
|
|
37
|
+
envVar.value !== "" &&
|
|
38
|
+
!envVar.valueFrom
|
|
39
|
+
) {
|
|
40
|
+
diagnostics.push({
|
|
41
|
+
checkId: "WK8005",
|
|
42
|
+
severity: "error",
|
|
43
|
+
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has hardcoded value for sensitive env var "${envVar.name}" — use secretKeyRef instead`,
|
|
44
|
+
entity: resourceName,
|
|
45
|
+
lexicon: "k8s",
|
|
46
|
+
});
|
|
52
47
|
}
|
|
53
48
|
}
|
|
54
49
|
}
|
package/dist/rules/wk8006.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
9
|
-
import {
|
|
9
|
+
import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
|
|
10
10
|
|
|
11
11
|
export const wk8006: PostSynthCheck = {
|
|
12
12
|
id: "WK8006",
|
|
@@ -15,49 +15,44 @@ export const wk8006: PostSynthCheck = {
|
|
|
15
15
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
16
16
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
17
17
|
|
|
18
|
-
for (const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
18
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
19
|
+
if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
|
|
20
|
+
|
|
21
|
+
const containers = extractContainers(manifest);
|
|
22
|
+
const resourceName = manifest.metadata?.name ?? manifest.kind;
|
|
23
|
+
|
|
24
|
+
for (const container of containers) {
|
|
25
|
+
const image = container.image;
|
|
26
|
+
if (typeof image !== "string" || image === "") continue;
|
|
27
|
+
|
|
28
|
+
// Split off digest (@sha256:...) — those are pinned and fine
|
|
29
|
+
if (image.includes("@")) continue;
|
|
30
|
+
|
|
31
|
+
// Extract the tag portion (after the last colon that isn't part of a port)
|
|
32
|
+
// Images can be: name, name:tag, registry:port/name, registry:port/name:tag
|
|
33
|
+
const slashIndex = image.lastIndexOf("/");
|
|
34
|
+
const afterSlash = slashIndex >= 0 ? image.slice(slashIndex + 1) : image;
|
|
35
|
+
const colonIndex = afterSlash.lastIndexOf(":");
|
|
36
|
+
|
|
37
|
+
if (colonIndex === -1) {
|
|
38
|
+
// No tag at all
|
|
39
|
+
diagnostics.push({
|
|
40
|
+
checkId: "WK8006",
|
|
41
|
+
severity: "warning",
|
|
42
|
+
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" uses untagged image "${image}" — specify an explicit version tag`,
|
|
43
|
+
entity: resourceName,
|
|
44
|
+
lexicon: "k8s",
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
const tag = afterSlash.slice(colonIndex + 1);
|
|
48
|
+
if (tag === "latest") {
|
|
43
49
|
diagnostics.push({
|
|
44
50
|
checkId: "WK8006",
|
|
45
51
|
severity: "warning",
|
|
46
|
-
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" uses
|
|
52
|
+
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" uses :latest tag on image "${image}" — use a specific version tag`,
|
|
47
53
|
entity: resourceName,
|
|
48
54
|
lexicon: "k8s",
|
|
49
55
|
});
|
|
50
|
-
} else {
|
|
51
|
-
const tag = afterSlash.slice(colonIndex + 1);
|
|
52
|
-
if (tag === "latest") {
|
|
53
|
-
diagnostics.push({
|
|
54
|
-
checkId: "WK8006",
|
|
55
|
-
severity: "warning",
|
|
56
|
-
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" uses :latest tag on image "${image}" — use a specific version tag`,
|
|
57
|
-
entity: resourceName,
|
|
58
|
-
lexicon: "k8s",
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
56
|
}
|
|
62
57
|
}
|
|
63
58
|
}
|
package/dist/rules/wk8041.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
10
|
-
import {
|
|
10
|
+
import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Known API key patterns and their descriptions.
|
|
@@ -35,33 +35,28 @@ export const wk8041: PostSynthCheck = {
|
|
|
35
35
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
36
36
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
37
37
|
|
|
38
|
-
for (const
|
|
39
|
-
|
|
40
|
-
const manifests = parseK8sManifests(yaml);
|
|
38
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
39
|
+
if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
const containers = extractContainers(manifest);
|
|
42
|
+
const resourceName = manifest.metadata?.name ?? manifest.kind;
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
for (const container of containers) {
|
|
45
|
+
if (!Array.isArray(container.env)) continue;
|
|
47
46
|
|
|
48
|
-
for (const
|
|
49
|
-
if (
|
|
47
|
+
for (const envVar of container.env) {
|
|
48
|
+
if (typeof envVar.value !== "string" || envVar.value === "") continue;
|
|
50
49
|
|
|
51
|
-
for (const
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
lexicon: "k8s",
|
|
62
|
-
});
|
|
63
|
-
break; // One match per env var is sufficient
|
|
64
|
-
}
|
|
50
|
+
for (const { pattern, label } of API_KEY_PATTERNS) {
|
|
51
|
+
if (pattern.test(envVar.value)) {
|
|
52
|
+
diagnostics.push({
|
|
53
|
+
checkId: "WK8041",
|
|
54
|
+
severity: "error",
|
|
55
|
+
message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has a ${label} hardcoded in env var "${envVar.name ?? "(unnamed)"}" — use a Secret reference instead`,
|
|
56
|
+
entity: resourceName,
|
|
57
|
+
lexicon: "k8s",
|
|
58
|
+
});
|
|
59
|
+
break; // One match per env var is sufficient
|
|
65
60
|
}
|
|
66
61
|
}
|
|
67
62
|
}
|
package/dist/rules/wk8042.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
10
|
-
import {
|
|
10
|
+
import { docsToManifests } from "./k8s-helpers";
|
|
11
11
|
|
|
12
12
|
const PRIVATE_KEY_PATTERN = /-----BEGIN\s+[\w\s]*PRIVATE KEY-----/;
|
|
13
13
|
|
|
@@ -18,27 +18,22 @@ export const wk8042: PostSynthCheck = {
|
|
|
18
18
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
19
19
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
20
20
|
|
|
21
|
-
for (const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
message: `ConfigMap "${resourceName}" contains a private key in data field "${key}" — use a Secret resource instead`,
|
|
38
|
-
entity: resourceName,
|
|
39
|
-
lexicon: "k8s",
|
|
40
|
-
});
|
|
41
|
-
}
|
|
21
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
22
|
+
if (manifest.kind !== "ConfigMap") continue;
|
|
23
|
+
|
|
24
|
+
const resourceName = manifest.metadata?.name ?? "ConfigMap";
|
|
25
|
+
const data = manifest.data;
|
|
26
|
+
if (typeof data !== "object" || data === null) continue;
|
|
27
|
+
|
|
28
|
+
for (const [key, value] of Object.entries(data)) {
|
|
29
|
+
if (typeof value === "string" && PRIVATE_KEY_PATTERN.test(value)) {
|
|
30
|
+
diagnostics.push({
|
|
31
|
+
checkId: "WK8042",
|
|
32
|
+
severity: "error",
|
|
33
|
+
message: `ConfigMap "${resourceName}" contains a private key in data field "${key}" — use a Secret resource instead`,
|
|
34
|
+
entity: resourceName,
|
|
35
|
+
lexicon: "k8s",
|
|
36
|
+
});
|
|
42
37
|
}
|
|
43
38
|
}
|
|
44
39
|
}
|
package/dist/rules/wk8101.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
10
|
-
import {
|
|
10
|
+
import { docsToManifests } from "./k8s-helpers";
|
|
11
11
|
|
|
12
12
|
export const wk8101: PostSynthCheck = {
|
|
13
13
|
id: "WK8101",
|
|
@@ -16,46 +16,41 @@ export const wk8101: PostSynthCheck = {
|
|
|
16
16
|
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
17
17
|
const diagnostics: PostSynthDiagnostic[] = [];
|
|
18
18
|
|
|
19
|
-
for (const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
for (const manifest of docsToManifests(ctx)) {
|
|
20
|
+
if (manifest.kind !== "Deployment") continue;
|
|
21
|
+
|
|
22
|
+
const resourceName = manifest.metadata?.name ?? "Deployment";
|
|
23
|
+
const spec = manifest.spec;
|
|
24
|
+
if (!spec) continue;
|
|
25
|
+
|
|
26
|
+
const selector = spec.selector as Record<string, unknown> | undefined;
|
|
27
|
+
const matchLabels = selector?.matchLabels as Record<string, string> | undefined;
|
|
28
|
+
if (!matchLabels || typeof matchLabels !== "object") continue;
|
|
29
|
+
|
|
30
|
+
const template = spec.template as Record<string, unknown> | undefined;
|
|
31
|
+
const templateMetadata = template?.metadata as Record<string, unknown> | undefined;
|
|
32
|
+
const templateLabels = templateMetadata?.labels as Record<string, string> | undefined;
|
|
33
|
+
|
|
34
|
+
if (!templateLabels || typeof templateLabels !== "object") {
|
|
35
|
+
diagnostics.push({
|
|
36
|
+
checkId: "WK8101",
|
|
37
|
+
severity: "error",
|
|
38
|
+
message: `Deployment "${resourceName}" has spec.selector.matchLabels but no spec.template.metadata.labels`,
|
|
39
|
+
entity: resourceName,
|
|
40
|
+
lexicon: "k8s",
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
for (const [key, value] of Object.entries(matchLabels)) {
|
|
46
|
+
if (templateLabels[key] !== value) {
|
|
39
47
|
diagnostics.push({
|
|
40
48
|
checkId: "WK8101",
|
|
41
49
|
severity: "error",
|
|
42
|
-
message: `Deployment "${resourceName}"
|
|
50
|
+
message: `Deployment "${resourceName}" selector label "${key}=${value}" does not match template label "${key}=${templateLabels[key] ?? '(missing)'}"`,
|
|
43
51
|
entity: resourceName,
|
|
44
52
|
lexicon: "k8s",
|
|
45
53
|
});
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (const [key, value] of Object.entries(matchLabels)) {
|
|
50
|
-
if (templateLabels[key] !== value) {
|
|
51
|
-
diagnostics.push({
|
|
52
|
-
checkId: "WK8101",
|
|
53
|
-
severity: "error",
|
|
54
|
-
message: `Deployment "${resourceName}" selector label "${key}=${value}" does not match template label "${key}=${templateLabels[key] ?? '(missing)'}"`,
|
|
55
|
-
entity: resourceName,
|
|
56
|
-
lexicon: "k8s",
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
54
|
}
|
|
60
55
|
}
|
|
61
56
|
}
|