feat: bootstrap portable Node runtimes
CI / Client installer (macos-latest) (push) Canceled after 0s
CI / Client installer (ubuntu-latest) (push) Canceled after 0s
CI / Client installer (windows-latest) (push) Canceled after 0s
CI / test (22) (push) Canceled after 0s
CI / test (24) (push) Canceled after 0s
CI / release-smoke (push) Canceled after 0s

This commit is contained in:
2026-08-18 13:53:54 +00:00
parent 2e275b684d
commit dd189c2acb
22 changed files with 900 additions and 96 deletions
+27
View File
@@ -1,4 +1,5 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import test from "node:test";
@@ -19,6 +20,10 @@ test("customer distribution is a generic single-login gateway bridge", () => {
assert.match(installer, /onboarding\.firstRun=true.*简短示例.*不要自动执行/s);
assert.match(installer, /Windows(PowerShell)/);
assert.match(installer, /Invoke-WebRequest[\s\S]*Get-FileHash[\s\S]*ConvertFrom-Json/);
assert.match(installer, /系统无需预装 Node\.js/);
assert.match(installer, /阿里云精确版本.*npmmirror 精确版本.*Node 官方精确版本/s);
assert.match(installer, /BOOTSTRAP_WINDOWS_URL|Windows 安装引导/);
assert.match(installer, /Linux\/macOS 安装引导/);
assert.match(installer, /dingtalk-approval-bridge\.cmd/);
assert.match(installer, /显示 302 或启动后立即退出[\s\S]*type: \"stdio\"[\s\S]*doctor --json/);
assert.match(installer, /sourceStatus[\s\S]*dingtalk\.status[\s\S]*xft\.status[\s\S]*都必须是 `ok`/);
@@ -28,6 +33,25 @@ test("customer distribution is a generic single-login gateway bridge", () => {
assert.doesNotMatch(installer, /通过当前 Messenger/);
assert.doesNotMatch(installer, /DWS_CLI_PATH|dingtalk-workspace-cli|APP_MODE=local/);
const runtime = JSON.parse(readFileSync("distribution/node-runtime.json", "utf8"));
assert.equal(runtime.version, "22.23.2");
assert.deepEqual(Object.keys(runtime.platforms).sort(), [
"darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", "win32-arm64", "win32-x64",
]);
assert.equal(runtime.sourceBases.length, 3);
assert.ok(runtime.sourceBases.every((value: string) => value.includes(`/v${runtime.version}/`) && !value.includes("latest")));
assert.ok(Object.values(runtime.platforms).every((value) => /^[a-f0-9]{64}$/.test((value as { sha256: string }).sha256)));
const windowsBootstrap = readFileSync("distribution/bootstrap-windows.template.ps1", "utf8");
const posixBootstrap = readFileSync("distribution/bootstrap-posix.template.sh", "utf8");
assert.match(windowsBootstrap, /OSArchitecture[\s\S]*Save-DingDownload[\s\S]*Expand-Archive/);
assert.match(windowsBootstrap, /SHA-256 不匹配,停止安装/);
assert.match(posixBootstrap, /^#!\/bin\/sh\nset -eu/);
assert.match(posixBootstrap, /uname -s[\s\S]*uname -m[\s\S]*tar -xzf/);
assert.match(posixBootstrap, /SHA-256 不匹配,停止安装/);
assert.equal(spawnSync("sh", ["-n", "distribution/bootstrap-posix.template.sh"]).status, 0);
assert.doesNotMatch(`${windowsBootstrap}\n${posixBootstrap}`, /latest-v|npm install -g/);
const skill = readFileSync("skills/dingtalk-approval/SKILL.md", "utf8");
const createApproval = readFileSync("skills/dingtalk-approval/references/create-approval.md", "utf8");
assert.match(skill, /read `references\/create-approval\.md` completely/);
@@ -42,6 +66,9 @@ test("customer distribution is a generic single-login gateway bridge", () => {
assert.match(builder, /schemaVersion: 3,\s+version,/s);
assert.match(builder, /signData\(null, manifestBytes/);
assert.match(builder, /dingtalk-approval-client/);
assert.match(builder, /bootstrap-windows\.template\.ps1/);
assert.match(builder, /bootstrap-posix\.template\.sh/);
assert.match(builder, /validateNodeRuntime/);
assert.match(builder, /cpSync\(resolve\("skills\/dingtalk-approval"\).*recursive: true/s);
assert.doesNotMatch(builder, /codexPlugin|codex-plugin|\.codex-plugin|openai\.yaml/);
assert.doesNotMatch(builder, /dingtalk-workspace-cli|DWS_URL|DWS_SHA256/);