feat(cicd): add warning-only feature config validation

This commit is contained in:
Codex
2026-07-14 04:46:01 +02:00
parent 7eebc513b2
commit 90e5ad8e89
38 changed files with 1160 additions and 493 deletions
@@ -11,7 +11,7 @@ afterEach(() => {
while (roots.length > 0) rmSync(roots.pop()!, { recursive: true, force: true });
});
test("patches YAML workloads across ordinary, multi-document, and Kubernetes List files", () => {
test("patches fixed Kafka transport settings without architecture capability env", () => {
const root = mkdtempSync(join(tmpdir(), "runtime-gitops-postprocess-"));
roots.push(root);
const runtimeDir = join(root, "runtime");
@@ -32,23 +32,25 @@ test("patches YAML workloads across ordinary, multi-document, and Kubernetes Lis
expect(result.stderr).toContain('"codeAgentRuntimeChanged":true');
const api = Bun.YAML.parse(readFileSync(join(runtimeDir, "cloud-api.yaml"), "utf8")) as any;
expect(authorityValues(api)).toEqual(["false", "false", "false", "true", "true", "true"]);
expect(envValues(api)).toMatchObject({
HWLAB_KAFKA_ENABLED: "true",
HWLAB_KAFKA_AGENTRUN_EVENT_CONSUME_ENABLED: "true",
HWLAB_KAFKA_PROJECTOR_GROUP_ID: "hwlab-projector",
HWLAB_KAFKA_HWLAB_EVENT_GROUP_ID: "hwlab-events",
});
expect(architectureEnvNames(api)).toEqual([]);
const multiDocs = readFileSync(join(runtimeDir, "cloud-web.yaml"), "utf8").split(/^---$/mu).map((document) => Bun.YAML.parse(document) as any);
expect(multiDocs[0].kind).toBe("ConfigMap");
expect(envValues(multiDocs[1])).toMatchObject({
HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED: "false",
HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_ENABLED: "false",
HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED: "true",
});
expect(envValues(multiDocs[1])).toEqual({ EXISTING: "preserved" });
const list = Bun.YAML.parse(readFileSync(join(runtimeDir, "list.yaml"), "utf8")) as any;
expect(list.kind).toBe("List");
expect(authorityValues(list.items[0])).toEqual(["false", "false", "false", "true", "true", "true"]);
expect(architectureEnvNames(list.items[0])).toEqual([]);
const jsonText = readFileSync(join(runtimeDir, "json-workload.yaml"), "utf8");
expect(jsonText.startsWith("{\n")).toBe(true);
expect(authorityValues(JSON.parse(jsonText))).toEqual(["false", "false", "false", "true", "true", "true"]);
expect(architectureEnvNames(JSON.parse(jsonText))).toEqual([]);
const afterFirstRun = snapshot(runtimeDir);
const second = spawnSync(process.execPath, [script], {
@@ -68,16 +70,11 @@ function overlay(runtimePath: string) {
enabled: true,
kafkaEventBridge: {
enabled: true,
directPublishConsumerGroupId: "hwlab-direct",
transactionalProjectorConsumerGroupId: "hwlab-projector",
features: {
directPublish: false,
liveKafkaSse: false,
kafkaRefreshReplay: false,
transactionalProjector: true,
projectionOutboxRelay: true,
projectionRealtime: true,
},
hwlabEventConsumerGroupId: "hwlab-events",
transactionalProjector: { heartbeatIntervalMs: 2000 },
projectionOutboxRelay: { intervalMs: 250, batchSize: 100, leaseMs: 30000, sendTimeoutMs: 10000, retryBackoffMs: 1000 },
projectionRealtime: { outboxTailBatchSize: 100, sseHeartbeatMs: 1000, sseDrainTimeoutMs: 5000 },
},
},
};
@@ -96,16 +93,16 @@ function envValues(workload: any) {
return Object.fromEntries(workload.spec.template.spec.containers[0].env.map((item: any) => [item.name, item.value]));
}
function authorityValues(workload: any) {
const env = envValues(workload);
return [
env.HWLAB_KAFKA_DIRECT_PUBLISH_ENABLED,
env.HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED,
env.HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_ENABLED,
env.HWLAB_KAFKA_TRANSACTIONAL_PROJECTOR_ENABLED,
env.HWLAB_KAFKA_PROJECTION_OUTBOX_RELAY_ENABLED,
env.HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED,
];
function architectureEnvNames(workload: any) {
const forbidden = new Set([
"HWLAB_KAFKA_DIRECT_PUBLISH_ENABLED",
"HWLAB_WORKBENCH_LIVE_KAFKA_SSE_ENABLED",
"HWLAB_WORKBENCH_KAFKA_REFRESH_REPLAY_ENABLED",
"HWLAB_KAFKA_TRANSACTIONAL_PROJECTOR_ENABLED",
"HWLAB_KAFKA_PROJECTION_OUTBOX_RELAY_ENABLED",
"HWLAB_WORKBENCH_PROJECTION_REALTIME_ENABLED",
]);
return workload.spec.template.spec.containers[0].env.map((item: any) => item.name).filter((name: string) => forbidden.has(name));
}
function snapshot(runtimeDir: string) {