fix: prefer native domestic installer downloads
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
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:
@@ -10,7 +10,7 @@ import { basename, delimiter, dirname, join } from "node:path";
|
||||
import test from "node:test";
|
||||
import {
|
||||
acquireClientInstallLock, cleanupLegacyInstallations, clientLauncherBodies, clientPaths, releaseKeyId,
|
||||
nodeProxyArgs, npmChildEnvironment, npmFailureMessage, runClientInstaller, runNpm, verifySignedManifest,
|
||||
downloadWithCurl, nodeProxyArgs, npmChildEnvironment, npmFailureMessage, runClientInstaller, runNpm, verifySignedManifest,
|
||||
type ClientReleaseManifest,
|
||||
} from "../src/client-installer.js";
|
||||
|
||||
@@ -130,6 +130,125 @@ test("runNpm passes the isolated proxy environment to the real child process", (
|
||||
}
|
||||
});
|
||||
|
||||
test("native curl downloader honors the proxy environment and never fails silently", async () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "ding-client-curl-"));
|
||||
try {
|
||||
const fakeBin = join(root, "bin");
|
||||
const curl = join(fakeBin, "curl.exe");
|
||||
const log = join(root, "curl.log");
|
||||
mkdirSync(fakeBin, { recursive: true });
|
||||
writeFileSync(curl, `#!/bin/sh
|
||||
target=''
|
||||
url=''
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--output) target="$2"; shift 2 ;;
|
||||
https://*) url="$1"; shift ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
printf '%s|%s\n' "$url" "${'${HTTPS_PROXY:-}'}" >> "$DING_CURL_LOG"
|
||||
case "$url" in
|
||||
*/ok) printf 'native-payload' > "$target"; printf '200'; exit 0 ;;
|
||||
*/missing) printf '404'; exit 22 ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
`);
|
||||
chmodSync(curl, 0o700);
|
||||
const env = {
|
||||
PATH: `${fakeBin}${delimiter}${process.env.PATH ?? ""}`,
|
||||
HTTPS_PROXY: "http://192.168.199.62:7890",
|
||||
DING_CURL_LOG: log,
|
||||
};
|
||||
assert.equal((await downloadWithCurl(new URL("https://release.example.test/ok"), env, "win32")).toString(), "native-payload");
|
||||
assert.match(readFileSync(log, "utf8"), /https:\/\/release\.example\.test\/ok\|http:\/\/192\.168\.199\.62:7890/);
|
||||
await assert.rejects(downloadWithCurl(new URL("https://release.example.test/missing"), env, "win32"), /curl 下载失败.*HTTP 404/);
|
||||
await assert.rejects(downloadWithCurl(new URL("https://release.example.test/silent"), env, "win32"), /curl 下载失败.*curl 退出码 1/);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("installer uses native curl end to end in a proxied Windows-style environment", async () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "ding-client-curl-install-"));
|
||||
try {
|
||||
const appRoot = join(root, "dingtalk-approval");
|
||||
const fakeBin = join(root, "bin");
|
||||
const fixtureDir = join(root, "downloads");
|
||||
const curlLog = join(root, "curl.log");
|
||||
mkdirSync(fakeBin, { recursive: true });
|
||||
mkdirSync(fixtureDir, { recursive: true });
|
||||
createFakeNpm(fakeBin);
|
||||
|
||||
const keys = generateKeyPairSync("ed25519");
|
||||
const publicPem = Buffer.from(keys.publicKey.export({ type: "spki", format: "pem" }));
|
||||
const keyId = releaseKeyId(publicPem);
|
||||
const bodies = {
|
||||
installer: Buffer.from("installer"), publicKey: publicPem, client: Buffer.from("fake-client-tgz"),
|
||||
skill: Buffer.from("---\nname: dingtalk-approval\ndescription: Test\n---\n"),
|
||||
};
|
||||
const manifest = manifestFixture(keyId, publicPem, bodies);
|
||||
const manifestBytes = Buffer.from(`${JSON.stringify(manifest, null, 2)}\n`);
|
||||
const signature = signatureEnvelope(keyId, sign(null, manifestBytes, keys.privateKey));
|
||||
writeFileSync(join(fixtureDir, "manifest.json"), manifestBytes);
|
||||
writeFileSync(join(fixtureDir, "manifest.sig"), signature);
|
||||
for (const [name, body] of Object.entries(bodies)) writeFileSync(join(fixtureDir, name), body);
|
||||
|
||||
const curlBody = `#!/bin/sh
|
||||
target=''
|
||||
url=''
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--output) target="$2"; shift 2 ;;
|
||||
https://*) url="$1"; shift ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
printf '%s|%s\n' "$url" "${'${HTTPS_PROXY:-}'}" >> "$DING_CURL_LOG"
|
||||
name="${'${url##*/}'}"
|
||||
if [ -f "$DING_CURL_FIXTURE_DIR/$name" ]; then
|
||||
cp "$DING_CURL_FIXTURE_DIR/$name" "$target"
|
||||
printf '200'
|
||||
exit 0
|
||||
fi
|
||||
printf '404'
|
||||
exit 22
|
||||
`;
|
||||
for (const name of ["curl", "curl.exe"]) {
|
||||
writeFileSync(join(fakeBin, name), curlBody);
|
||||
chmodSync(join(fakeBin, name), 0o700);
|
||||
}
|
||||
const publicPath = join(root, "release-public.pem");
|
||||
writeFileSync(publicPath, publicPem, { mode: 0o600 });
|
||||
const env = {
|
||||
PATH: `${fakeBin}${delimiter}${process.env.PATH ?? ""}`,
|
||||
DINGTALK_APPROVAL_CLIENT_HOME: appRoot,
|
||||
npm_execpath: process.platform === "win32"
|
||||
? join(fakeBin, "node_modules", "npm", "bin", "npm-cli.js") : join(fakeBin, "fake-npm.mjs"),
|
||||
HTTPS_PROXY: "http://192.168.199.62:7890",
|
||||
NODE_OPTIONS: "--trace-warnings --use-env-proxy",
|
||||
DING_CURL_FIXTURE_DIR: fixtureDir,
|
||||
DING_CURL_LOG: curlLog,
|
||||
};
|
||||
const output: string[] = [];
|
||||
const progress: string[] = [];
|
||||
const code = await runClientInstaller([
|
||||
"install", "--manifest-url", "https://release.example.test/manifest.json",
|
||||
"--public-key", publicPath, "--app-root", appRoot, "--json",
|
||||
], {
|
||||
env, platform: process.platform, home: join(root, "home"), fetchFn: fetch,
|
||||
stdout: (value) => { output.push(value); }, stderr: (value) => { progress.push(value); },
|
||||
});
|
||||
assert.equal(code, 0, progress.join(""));
|
||||
assert.equal(JSON.parse(output.at(-1)!).status, "installed");
|
||||
const calls = readFileSync(curlLog, "utf8");
|
||||
assert.match(calls, /manifest\.json\|http:\/\/192\.168\.199\.62:7890/);
|
||||
assert.match(calls, /client\|http:\/\/192\.168\.199\.62:7890/);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("signed manifest verification rejects tampering and a foreign key", () => {
|
||||
const trusted = generateKeyPairSync("ed25519");
|
||||
const foreign = generateKeyPairSync("ed25519");
|
||||
|
||||
@@ -25,7 +25,8 @@ test("customer distribution is a generic single-login gateway bridge", () => {
|
||||
assert.match(installer, /安装长时间无最终 JSON[\s\S]*残留 staging/s);
|
||||
assert.match(installer, /系统无需预装 Node\.js/);
|
||||
assert.match(installer, /阿里云精确版本.*npmmirror 精确版本.*Node 官方精确版本/s);
|
||||
assert.match(installer, /国内备用发布源[\s\S]*主发布源.*国内 Gitea 备用源/s);
|
||||
assert.match(installer, /备用发布源[\s\S]*优先使用国内 Gitea 静态发布源/s);
|
||||
assert.match(installer, /优先使用系统 `curl`[\s\S]*Node `fetch` 仅在系统没有 `curl` 时兜底/s);
|
||||
assert.match(installer, /不要设置全局 `NODE_OPTIONS=--use-env-proxy`[\s\S]*不要手工预装 `node_modules`/s);
|
||||
assert.match(installer, /BOOTSTRAP_WINDOWS_URL|Windows 安装引导/);
|
||||
assert.match(installer, /Linux\/macOS 安装引导/);
|
||||
@@ -82,6 +83,9 @@ test("customer distribution is a generic single-login gateway bridge", () => {
|
||||
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/);
|
||||
|
||||
const deploy = readFileSync("deploy/install.sh", "utf8");
|
||||
assert.match(deploy, /--download-base-url "\$\{gitea_release_base_url\}"[\s\S]*--download-mirror-base-url "\$\{public_download_base_url\}"/s);
|
||||
});
|
||||
|
||||
test("public install page explicitly declares UTF-8 for browser rendering", () => {
|
||||
|
||||
Reference in New Issue
Block a user