ymal: compose repeated node lane templates

This commit is contained in:
root
2026-07-08 06:34:23 +02:00
parent 0cea74367a
commit d083e88bb9
13 changed files with 1232 additions and 1585 deletions
+31 -4
View File
@@ -9,6 +9,7 @@ import type { RenderedCliResult } from "./output";
import { shQuote } from "./platform-infra-public-service";
import { fingerprintSecretValues, readEnvSourceFile, requiredEnvValue } from "./secrets";
import { transHostProxyEnvSummary } from "./trans-host-proxy";
import { materializeYamlComposition } from "./yaml-composition";
const configPath = "config/platform-infra/host-proxy.yaml";
@@ -161,7 +162,7 @@ function parseOptions(args: string[]): HostProxyOptions {
}
function readHostProxyConfig(): HostProxyConfig {
const root = record(Bun.YAML.parse(readFileSync(rootPath(configPath), "utf8")), configPath);
const root = record(materializeYamlComposition(Bun.YAML.parse(readFileSync(rootPath(configPath), "utf8")) as unknown, { label: configPath }).value, configPath);
if (root.kind !== "platform-infra-host-proxy") throw new Error(`${configPath}.kind must be platform-infra-host-proxy`);
const defaultsRaw = record(root.defaults, `${configPath}.defaults`);
const server = parseServer(record(root.server, `${configPath}.server`), `${configPath}.server`);
@@ -172,7 +173,7 @@ function readHostProxyConfig(): HostProxyConfig {
}));
const targets: Record<string, HostProxyTarget> = {};
for (const [id, value] of Object.entries(record(root.targets, `${configPath}.targets`))) {
targets[id] = parseTarget(id, record(value, `${configPath}.targets.${id}`), sources);
targets[id] = parseTarget(id, record(value, `${configPath}.targets.${id}`), sources, root);
}
return {
defaults: { targetId: stringField(defaultsRaw, "targetId", `${configPath}.defaults`) },
@@ -315,7 +316,7 @@ function podAccessSpec(raw: Record<string, unknown>, label: string): HostProxyPo
return { enabled, listenHost, listenPort, proxyUrl };
}
function parseTarget(id: string, raw: Record<string, unknown>, sources: Record<string, HostProxySource>): HostProxyTarget {
function parseTarget(id: string, raw: Record<string, unknown>, sources: Record<string, HostProxySource>, root: Record<string, unknown>): HostProxyTarget {
const sourceRef = stringField(raw, "sourceRef", `${configPath}.targets.${id}`);
if (!sourceRef.startsWith("sources.")) throw new Error(`${configPath}.targets.${id}.sourceRef must use sources.<id>`);
const sourceId = sourceRef.slice("sources.".length);
@@ -324,7 +325,9 @@ function parseTarget(id: string, raw: Record<string, unknown>, sources: Record<s
const envRaw = record(raw.env, `${configPath}.targets.${id}.env`);
const filesRaw = record(raw.files, `${configPath}.targets.${id}.files`);
const applyRaw = record(raw.apply, `${configPath}.targets.${id}.apply`);
const noProxy = stringArrayField(envRaw, "noProxy", `${configPath}.targets.${id}.env`);
const noProxy = envRaw.noProxy === undefined
? noProxyFromRefs(envRaw, root, `${configPath}.targets.${id}.env`)
: stringArrayField(envRaw, "noProxy", `${configPath}.targets.${id}.env`);
if (!noProxy.includes("hyueapi.com") || !noProxy.includes(".hyueapi.com")) {
throw new Error(`${configPath}.targets.${id}.env.noProxy must preserve hyueapi.com and .hyueapi.com`);
}
@@ -359,6 +362,30 @@ function parseTarget(id: string, raw: Record<string, unknown>, sources: Record<s
return target;
}
function noProxyFromRefs(envRaw: Record<string, unknown>, root: Record<string, unknown>, label: string): string[] {
const refs = record(envRaw.noProxyRefs, `${label}.noProxyRefs`);
const values: string[] = [];
for (const key of ["common", "extras"] as const) {
const ref = stringField(refs, key, `${label}.noProxyRefs`);
values.push(...stringArrayAtPath(root, ref, `${label}.noProxyRefs.${key}`));
}
return [...new Set(values)];
}
function stringArrayAtPath(root: unknown, ref: string, label: string): string[] {
let current: unknown = root;
for (const segment of ref.split(".")) {
if (!/^[A-Za-z0-9_-]+$/u.test(segment) || typeof current !== "object" || current === null || Array.isArray(current)) {
throw new Error(`${label} references invalid path ${ref}`);
}
current = (current as Record<string, unknown>)[segment];
}
if (!Array.isArray(current) || !current.every((item) => typeof item === "string" && item.length > 0)) {
throw new Error(`${label} must reference a string array`);
}
return [...current];
}
function resolveTarget(config: HostProxyConfig, targetId: string): HostProxyTarget {
const target = config.targets[targetId];
if (target === undefined) throw new Error(`${configPath}.targets.${targetId} is not defined`);