From aec4151a5d3a67c804687d693c47df40047ecc63 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 1 Jul 2026 14:05:43 +0000 Subject: [PATCH] fix: preserve PK01 Caddy host blocks --- scripts/src/pk01-caddy.test.ts | 91 ++++++++++++++++++++++++++++++++++ scripts/src/pk01-caddy.ts | 13 ++++- 2 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 scripts/src/pk01-caddy.test.ts diff --git a/scripts/src/pk01-caddy.test.ts b/scripts/src/pk01-caddy.test.ts new file mode 100644 index 00000000..d1c9c2e8 --- /dev/null +++ b/scripts/src/pk01-caddy.test.ts @@ -0,0 +1,91 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { test } from "bun:test"; + +import { pk01CaddyMergeManagedBlocksPython } from "./pk01-caddy"; + +function mergeCaddyfiles(current: string, desired: string, legacyDomains: readonly string[] = []): { merged: string; summary: Record } { + const dir = mkdtempSync(join(tmpdir(), "pk01-caddy-")); + try { + const currentPath = join(dir, "current.Caddyfile"); + const desiredPath = join(dir, "desired.Caddyfile"); + const mergedPath = join(dir, "merged.Caddyfile"); + writeFileSync(currentPath, current, "utf8"); + writeFileSync(desiredPath, desired, "utf8"); + const result = spawnSync("python3", ["-", desiredPath, currentPath, mergedPath, ...legacyDomains], { + encoding: "utf8", + input: pk01CaddyMergeManagedBlocksPython(), + }); + assert.equal(result.status, 0, result.stderr || result.stdout); + return { + merged: readFileSync(mergedPath, "utf8"), + summary: JSON.parse(result.stdout) as Record, + }; + } finally { + rmSync(dir, { recursive: true, force: true }); + } +} + +test("PK01 Caddy merge preserves monitor root nested managed block", () => { + const current = `{ + email ops@pikapython.com +} + +pikapython.com, www.pikapython.com { + reverse_proxy 127.0.0.1:18888 +} + +api2.pikapython.com { + reverse_proxy 127.0.0.1:18080 +} + +# BEGIN unidesk managed langbot +langbot.pikapython.com { + reverse_proxy 127.0.0.1:22097 +} +# END unidesk managed langbot + +# BEGIN unidesk managed sub2api-jd01 +api2.pikapython.com { + reverse_proxy 127.0.0.1:22080 +} +# END unidesk managed sub2api-jd01 + +monitor.pikapython.com { + # BEGIN unidesk managed hwlab-web-probe-sentinel-active-root + handle { + reverse_proxy 127.0.0.1:22098 + } + # END unidesk managed hwlab-web-probe-sentinel-active-root +} +`; + const desired = `{ + email ops@pikapython.com +} + +pikapython.com, www.pikapython.com { + reverse_proxy 127.0.0.1:18888 +} + +# BEGIN unidesk managed sub2api-jd01 +api2.pikapython.com { + reverse_proxy 127.0.0.1:22087 +} +# END unidesk managed sub2api-jd01 +`; + + const { merged, summary } = mergeCaddyfiles(current, desired, ["api2.pikapython.com"]); + + assert.equal(summary.baseSource, "current"); + assert.match(merged, /monitor\.pikapython\.com \{/u); + assert.match(merged, /hwlab-web-probe-sentinel-active-root/u); + assert.match(merged, /reverse_proxy 127\.0\.0\.1:22098/u); + assert.match(merged, /# BEGIN unidesk managed langbot/u); + assert.match(merged, /# BEGIN unidesk managed sub2api-jd01[\s\S]*127\.0\.0\.1:22087/u); + assert.doesNotMatch(merged, /127\.0\.0\.1:22080/u); + assert.doesNotMatch(merged, /127\.0\.0\.1:18080/u); +}); diff --git a/scripts/src/pk01-caddy.ts b/scripts/src/pk01-caddy.ts index 06038b56..6c0c9078 100644 --- a/scripts/src/pk01-caddy.ts +++ b/scripts/src/pk01-caddy.ts @@ -367,7 +367,10 @@ for source in (current, desired): order.append(name) blocks[name] = match.group(0).rstrip() + "\n" -base = pattern.sub("", desired) +# Preserve host-owned site blocks and nested managed blocks from the active +# Caddyfile; service-specific applies should only upsert top-level blocks. +base_source = "current" if current.strip() else "desired" +base = pattern.sub("", current if base_source == "current" else desired) for domain in legacy_domains: base = remove_site_blocks(base, domain) base = base.rstrip() @@ -376,7 +379,13 @@ next_text = base if managed: next_text = next_text + "\n\n" + managed merged_path.write_text(next_text.rstrip() + "\n", encoding="utf-8") -print(json.dumps({"currentManagedBlocks": len(pattern.findall(current)), "desiredManagedBlocks": len(pattern.findall(desired)), "mergedManagedBlocks": len(blocks)}))`; +print(json.dumps({ + "baseSource": base_source, + "legacyDomains": legacy_domains, + "currentManagedBlocks": len(pattern.findall(current)), + "desiredManagedBlocks": len(pattern.findall(desired)), + "mergedManagedBlocks": len(blocks), +}))`; } function managedBlockPattern(): RegExp {