ci: accept extensible capability metadata in container checks

The v1.6.2 container gate rejected `/api/capabilities` after the compatible connection-progress capability was added: it compared the entire object with the two service flags it owns. Check those flags independently, keeping optional SFU/NAT startup assertions while allowing compatible metadata extensions.

The standalone container acceptance script is also classified with other non-product checks so changing this gate does not select a new App version. This unblocks the already-approved v1.6.2 release; its reviewed Chinese and English notes remain unchanged.

Validation: focused release automation tests (4 passed); manual candidate CI checks the actual container recipe before integration.
This commit is contained in:
AAA authored and GitHub committed 2026-09-23 22:46:06 +08:00
1 parent 01821ae7ac
commit e22e3c2cf4
3 files changed
+11 -4

No files matched your search

+9 -2
View File
@@ -43,6 +43,13 @@ async function request(path, options) {
assert.ok(response.ok, `${path}: HTTP ${response.status}`); assert.ok(response.ok, `${path}: HTTP ${response.status}`);
return response; return response;
} }
async function expectServices(sfu, natPrediction) {
const capabilities = await (await request("/api/capabilities")).json();
// Capability metadata is extensible. This gate checks configured services,
// not the absence of other compatible capabilities.
assert.equal(capabilities.sfu, sfu);
assert.equal(capabilities.natPrediction, natPrediction);
}
async function binding(port) { async function binding(port) {
const socket = createSocket("udp4"); const socket = createSocket("udp4");
const packet = Buffer.alloc(20); const packet = Buffer.alloc(20);
@@ -79,7 +86,7 @@ try {
assert.match(compose("run", "--rm", "--no-deps", "piik", "--check-config"), /config=ok/); assert.match(compose("run", "--rm", "--no-deps", "piik", "--check-config"), /config=ok/);
compose("up", "-d", "--pull", "never"); compose("up", "-d", "--pull", "never");
await until(async () => assert.deepEqual(await (await request("/healthz")).json(), { status: "ok" }), "ready"); await until(async () => assert.deepEqual(await (await request("/healthz")).json(), { status: "ok" }), "ready");
assert.deepEqual(await (await request("/api/capabilities")).json(), { sfu: false, natPrediction: false }); await expectServices(false, false);
assert.equal((await (await request("/api/site-access")).json()).required, false); assert.equal((await (await request("/api/site-access")).json()).required, false);
const html = await (await request("/")).text(); const html = await (await request("/")).text();
const asset = html.match(/src="(\/assets\/index-[A-Za-z0-9_-]+\.js)"/)?.[1]; const asset = html.match(/src="(\/assets\/index-[A-Za-z0-9_-]+\.js)"/)?.[1];
@@ -101,7 +108,7 @@ try {
// Recreate with optional services on the same persistent room volume. // Recreate with optional services on the same persistent room volume.
writeFileSync(join(workspace, ".env"), `${sample}\nSFU_UDP_PORT=7882\nSFU_PUBLIC_IP=127.0.0.1\nNAT_PREDICTION_ENABLED=true\nPIIK_DEBUG=server\n`); writeFileSync(join(workspace, ".env"), `${sample}\nSFU_UDP_PORT=7882\nSFU_PUBLIC_IP=127.0.0.1\nNAT_PREDICTION_ENABLED=true\nPIIK_DEBUG=server\n`);
compose("up", "-d", "--force-recreate", "--pull", "never"); compose("up", "-d", "--force-recreate", "--pull", "never");
await until(async () => assert.deepEqual(await (await request("/api/capabilities")).json(), { sfu: true, natPrediction: true }), "optional services"); await until(() => expectServices(true, true), "optional services");
await request(`/api/rooms/${room.roomId}/access`, { await request(`/api/rooms/${room.roomId}/access`, {
method: "POST", method: "POST",
headers: { Origin: "https://share.example.com", Authorization: `Bearer ${room.hostToken}`, "Content-Type": "application/json" }, headers: { Origin: "https://share.example.com", Authorization: `Bearer ${room.hostToken}`, "Content-Type": "application/json" },
+1 -1
View File
@@ -20,7 +20,7 @@ const nonProductPaths = [
"cmd/**/*.md", "internal/**/*.md", "native/**/*.md", "src/**/*.md", "cmd/**/*.md", "internal/**/*.md", "native/**/*.md", "src/**/*.md",
".github/ISSUE_TEMPLATE/**", ".github/pull_request_template.md", ".github/workflows/website.yml", ".github/ISSUE_TEMPLATE/**", ".github/pull_request_template.md", ".github/workflows/website.yml",
"scripts/build-website.mjs", "scripts/update-website-hero.mjs", "scripts/check-website-film.js", "scripts/build-website.mjs", "scripts/update-website-hero.mjs", "scripts/check-website-film.js",
"scripts/check-docs.mjs", "scripts/markdown-slug.mjs", "scripts/check-project-state.*", "scripts/install-hooks.*", "scripts/check-docs.mjs", "scripts/check-container.mjs", "scripts/markdown-slug.mjs", "scripts/check-project-state.*", "scripts/install-hooks.*",
"scripts/required-project-paths.txt", "scripts/tsconfig.json", "scripts/required-project-paths.txt", "scripts/tsconfig.json",
"scripts/release-*.mjs", "scripts/publish-release.mjs", "scripts/publish-container.mjs", "scripts/mirror-release.mjs", "scripts/release-*.mjs", "scripts/publish-release.mjs", "scripts/publish-container.mjs", "scripts/mirror-release.mjs",
"scripts/*-gate.*", "scripts/*-probe.*", "scripts/browser-*", "scripts/encoded-*", "scripts/*-gate.*", "scripts/*-probe.*", "scripts/browser-*", "scripts/encoded-*",
+1 -1
View File
@@ -90,7 +90,7 @@ describe("release automation", () => {
expect(plan()).toMatchObject({ version: "v1.0.0", publish: true }); expect(plan()).toMatchObject({ version: "v1.0.0", publish: true });
for (const path of ["site/film/art.js", "site/docs/package.json", "site/docs/package-lock.json", for (const path of ["site/film/art.js", "site/docs/package.json", "site/docs/package-lock.json",
"site/docs/.vitepress/config.mjs", "docs/guide/troubleshooting.md", "docs/deployment.md", "README.zh-CN.md", "site/docs/.vitepress/config.mjs", "docs/guide/troubleshooting.md", "docs/deployment.md", "README.zh-CN.md",
"scripts/build-website.mjs", "scripts/check-docs.mjs", "scripts/markdown-slug.mjs", "scripts/release-notes.mjs", "scripts/build-website.mjs", "scripts/check-docs.mjs", "scripts/check-container.mjs", "scripts/markdown-slug.mjs", "scripts/release-notes.mjs",
"scripts/publish-container.mjs", "scripts/publish-container.mjs",
"scripts/app-gate-endpoint.ts", "scripts/client-gate-endpoint.ts", "scripts/app-gate-endpoint.ts", "scripts/client-gate-endpoint.ts",
"tests/room.test.ts", ".agents/skills/ponytail/SKILL.md", ".github/workflows/website.yml"]) { "tests/room.test.ts", ".agents/skills/ponytail/SKILL.md", ".github/workflows/website.yml"]) {