diff --git a/config/platform-infra/langbot.yaml b/config/platform-infra/langbot.yaml index 1de3ed23..568c0e68 100644 --- a/config/platform-infra/langbot.yaml +++ b/config/platform-infra/langbot.yaml @@ -7,6 +7,9 @@ metadata: relatedIssues: - 297 +defaults: + targetId: G14 + image: repository: rockchin/langbot tag: v4.10.1 diff --git a/config/platform-infra/n8n.yaml b/config/platform-infra/n8n.yaml index f29356f7..feb334ea 100644 --- a/config/platform-infra/n8n.yaml +++ b/config/platform-infra/n8n.yaml @@ -7,6 +7,9 @@ metadata: relatedIssues: - 300 +defaults: + targetId: G14 + image: repository: n8nio/n8n tag: "1.123.55" diff --git a/scripts/src/platform-infra-langbot.ts b/scripts/src/platform-infra-langbot.ts index d8e27f71..55824a77 100644 --- a/scripts/src/platform-infra-langbot.ts +++ b/scripts/src/platform-infra-langbot.ts @@ -53,6 +53,7 @@ interface LangBotConfig { version: number; kind: "platform-infra-langbot"; metadata: { id: string; owner: string; relatedIssues: number[] }; + defaults: { targetId: string }; image: { repository: string; tag: string; pullPolicy: "Always" | "IfNotPresent" | "Never" }; dependencyImages: { postgresClient: string }; targets: LangBotTarget[]; @@ -127,7 +128,7 @@ interface PublicExposure { } interface CommonOptions { - targetId: string; + targetId: string | null; full: boolean; raw: boolean; } @@ -197,7 +198,7 @@ export async function runLangBotCommand(config: UniDeskConfig, args: string[]): } function parseCommonOptions(args: string[]): CommonOptions { - let targetId = "G14"; + let targetId: string | null = null; let full = false; let raw = false; for (let index = 0; index < args.length; index += 1) { @@ -216,7 +217,7 @@ function parseCommonOptions(args: string[]): CommonOptions { throw new Error(`unsupported langbot option: ${arg}`); } } - if (!/^[A-Za-z0-9._-]+$/u.test(targetId)) throw new Error("--target must be a simple target id"); + if (targetId !== null && !/^[A-Za-z0-9._-]+$/u.test(targetId)) throw new Error("--target must be a simple target id"); return { targetId, full, raw }; } @@ -226,7 +227,8 @@ function parseApplyOptions(args: string[]): ApplyOptions { return arg !== "--confirm" && arg !== "--dry-run" && arg !== "--wait" && previous !== "--component" && previous !== "--lines" && previous !== "--path"; }); const common = parseCommonOptions(commonArgs); - if (args.some((arg) => !["--target", "--confirm", "--dry-run", "--wait", common.targetId].includes(arg) && !arg.startsWith("--target="))) { + const allowedArgs = ["--target", "--confirm", "--dry-run", "--wait", ...(common.targetId === null ? [] : [common.targetId])]; + if (args.some((arg) => !allowedArgs.includes(arg) && !arg.startsWith("--target="))) { for (let index = 0; index < args.length; index += 1) { const arg = args[index]; if (arg === "--target") { @@ -298,6 +300,7 @@ function readLangBotConfig(): LangBotConfig { const kind = stringField(root, "kind", ""); if (kind !== "platform-infra-langbot") throw new Error("config/platform-infra/langbot.yaml.kind must be platform-infra-langbot"); const metadata = objectField(root, "metadata", ""); + const defaults = objectField(root, "defaults", ""); const image = objectField(root, "image", ""); const dependencyImages = objectField(root, "dependencyImages", ""); const runtime = objectField(root, "runtime", ""); @@ -316,6 +319,9 @@ function readLangBotConfig(): LangBotConfig { owner: stringField(metadata, "owner", "metadata"), relatedIssues: numberArrayField(metadata, "relatedIssues", "metadata"), }, + defaults: { + targetId: stringField(defaults, "targetId", "defaults"), + }, image: { repository: stringField(image, "repository", "image"), tag: stringField(image, "tag", "image"), @@ -372,6 +378,7 @@ function readLangBotConfig(): LangBotConfig { if (!isImageReference(`${config.image.repository}:${config.image.tag}`)) throw new Error("config/platform-infra/langbot.yaml.image must render a valid image reference"); if (!isImageReference(config.dependencyImages.postgresClient)) throw new Error("config/platform-infra/langbot.yaml.dependencyImages.postgresClient must be an image reference"); if (config.targets.length === 0) throw new Error("config/platform-infra/langbot.yaml.targets must not be empty"); + assertKnownEnabledTarget(config.targets, config.defaults.targetId, "defaults.targetId"); return config; } @@ -422,9 +429,16 @@ function parseTarget(record: Record, index: number): LangBotTar }; } -function resolveTarget(langbot: LangBotConfig, targetId: string): LangBotTarget { - const target = langbot.targets.find((item) => item.id.toLowerCase() === targetId.toLowerCase()); - if (target === undefined) throw new Error(`unknown LangBot target ${targetId}; known targets: ${langbot.targets.map((item) => item.id).join(", ")}`); +function assertKnownEnabledTarget(targets: LangBotTarget[], targetId: string, path: string): void { + const target = targets.find((item) => item.id.toLowerCase() === targetId.toLowerCase()); + if (target === undefined) throw new Error(`config/platform-infra/langbot.yaml.${path} references unknown target ${targetId}; known targets: ${targets.map((item) => item.id).join(", ")}`); + if (!target.enabled) throw new Error(`config/platform-infra/langbot.yaml.${path} references disabled target ${target.id}`); +} + +function resolveTarget(langbot: LangBotConfig, targetId: string | null): LangBotTarget { + const resolvedTargetId = targetId ?? langbot.defaults.targetId; + const target = langbot.targets.find((item) => item.id.toLowerCase() === resolvedTargetId.toLowerCase()); + if (target === undefined) throw new Error(`unknown LangBot target ${resolvedTargetId}; known targets: ${langbot.targets.map((item) => item.id).join(", ")}`); if (!target.enabled) throw new Error(`LangBot target ${target.id} is disabled in config/platform-infra/langbot.yaml`); return target; } @@ -1000,7 +1014,7 @@ function prepareSecretMaterial(langbot: LangBotConfig): SecretMaterial { export function readLangBotRuntimeConfig(): Record { const langbot = readLangBotConfig(); - const target = resolveTarget(langbot, "G14"); + const target = resolveTarget(langbot, null); return { publicBaseUrl: target.publicExposure.publicBaseUrl, expectedNamespace: target.namespace, diff --git a/scripts/src/platform-infra-n8n.ts b/scripts/src/platform-infra-n8n.ts index 68a1d792..9d054969 100644 --- a/scripts/src/platform-infra-n8n.ts +++ b/scripts/src/platform-infra-n8n.ts @@ -53,6 +53,7 @@ interface N8nConfig { version: number; kind: "platform-infra-n8n"; metadata: { id: string; owner: string; relatedIssues: number[] }; + defaults: { targetId: string }; image: { repository: string; tag: string; pullPolicy: "Always" | "IfNotPresent" | "Never" }; dependencyImages: { postgresClient: string }; targets: N8nTarget[]; @@ -96,7 +97,7 @@ interface N8nTarget extends PublicServiceTarget { } interface CommonOptions { - targetId: string; + targetId: string | null; full: boolean; raw: boolean; } @@ -158,7 +159,7 @@ export async function runN8nCommand(config: UniDeskConfig, args: string[]): Prom } function parseCommonOptions(args: string[]): CommonOptions { - let targetId = "G14"; + let targetId: string | null = null; let full = false; let raw = false; for (let index = 0; index < args.length; index += 1) { @@ -177,7 +178,7 @@ function parseCommonOptions(args: string[]): CommonOptions { throw new Error(`unsupported n8n option: ${arg}`); } } - if (!/^[A-Za-z0-9._-]+$/u.test(targetId)) throw new Error("--target must be a simple target id"); + if (targetId !== null && !/^[A-Za-z0-9._-]+$/u.test(targetId)) throw new Error("--target must be a simple target id"); return { targetId, full, raw }; } @@ -876,6 +877,7 @@ function readN8nConfig(): N8nConfig { const kind = stringField(root, "kind", ""); if (kind !== "platform-infra-n8n") throw new Error(`${configLabel}.kind must be platform-infra-n8n`); const metadata = objectField(root, "metadata", ""); + const defaults = objectField(root, "defaults", ""); const image = objectField(root, "image", ""); const dependencyImages = objectField(root, "dependencyImages", ""); const runtime = objectField(root, "runtime", ""); @@ -893,6 +895,9 @@ function readN8nConfig(): N8nConfig { owner: stringField(metadata, "owner", "metadata"), relatedIssues: numberArrayField(metadata, "relatedIssues", "metadata"), }, + defaults: { + targetId: stringField(defaults, "targetId", "defaults"), + }, image: { repository: stringField(image, "repository", "image"), tag: stringField(image, "tag", "image"), @@ -944,6 +949,7 @@ function readN8nConfig(): N8nConfig { if (!isImageReference(`${config.image.repository}:${config.image.tag}`)) throw new Error(`${configLabel}.image must render a valid image reference`); if (!isImageReference(config.dependencyImages.postgresClient)) throw new Error(`${configLabel}.dependencyImages.postgresClient must be an image reference`); if (config.targets.length === 0) throw new Error(`${configLabel}.targets must not be empty`); + assertKnownEnabledTarget(config.targets, config.defaults.targetId, "defaults.targetId"); return config; } @@ -994,9 +1000,16 @@ function parseTarget(record: Record, index: number): N8nTarget }; } -function resolveTarget(n8n: N8nConfig, targetId: string): N8nTarget { - const target = n8n.targets.find((item) => item.id.toLowerCase() === targetId.toLowerCase()); - if (target === undefined) throw new Error(`unknown n8n target ${targetId}; known targets: ${n8n.targets.map((item) => item.id).join(", ")}`); +function assertKnownEnabledTarget(targets: N8nTarget[], targetId: string, path: string): void { + const target = targets.find((item) => item.id.toLowerCase() === targetId.toLowerCase()); + if (target === undefined) throw new Error(`${configLabel}.${path} references unknown target ${targetId}; known targets: ${targets.map((item) => item.id).join(", ")}`); + if (!target.enabled) throw new Error(`${configLabel}.${path} references disabled target ${target.id}`); +} + +function resolveTarget(n8n: N8nConfig, targetId: string | null): N8nTarget { + const resolvedTargetId = targetId ?? n8n.defaults.targetId; + const target = n8n.targets.find((item) => item.id.toLowerCase() === resolvedTargetId.toLowerCase()); + if (target === undefined) throw new Error(`unknown n8n target ${resolvedTargetId}; known targets: ${n8n.targets.map((item) => item.id).join(", ")}`); if (!target.enabled) throw new Error(`n8n target ${target.id} is disabled in ${configLabel}`); return target; }