Merge remote-tracking branch 'origin/pr/1339' into work/v0.8.34

This commit is contained in:
Hunter Bown
2026-05-12 23:34:22 -05:00
2 changed files with 161 additions and 1 deletions
+52
View File
@@ -985,10 +985,57 @@ async function verifyChecksum(filePath, assetName, checksums) {
}
}
async function checksumMatches(filePath, assetName, checksums) {
const expected = checksums.get(assetName);
if (!expected) {
throw new NonRetryableError(`Checksum manifest is missing ${assetName}`);
}
const actual = await sha256File(filePath);
return actual === expected;
}
async function loadChecksums(version, repo, options = {}) {
return parseChecksumManifest(await downloadText(checksumManifestUrl(version, repo), options));
}
function existingBinaryCandidates(targetPath, assetName) {
const candidates = [targetPath];
const assetPath = path.join(path.dirname(targetPath), assetName);
if (assetPath !== targetPath) {
candidates.push(assetPath);
}
return candidates;
}
async function adoptExistingBinaryIfValid(targetPath, assetName, version, getChecksums, marker) {
const candidates = [];
for (const candidate of existingBinaryCandidates(targetPath, assetName)) {
if (await fileExists(candidate)) {
candidates.push(candidate);
}
}
if (candidates.length === 0) {
return false;
}
const checksums = await getChecksums();
for (const candidate of candidates) {
if (!(await checksumMatches(candidate, assetName, checksums))) {
continue;
}
preflightGlibc(candidate);
if (candidate !== targetPath) {
await rename(candidate, targetPath);
}
if (process.platform !== "win32") {
await chmod(targetPath, 0o755);
}
await writeFile(marker, String(version), "utf8");
return true;
}
return false;
}
async function ensureBinary(targetPath, assetName, version, repo, getChecksums, options = {}) {
const marker = `${targetPath}.version`;
const downloadIfNeeded =
@@ -1001,6 +1048,9 @@ async function ensureBinary(targetPath, assetName, version, repo, getChecksums,
return targetPath;
}
}
if (await adoptExistingBinaryIfValid(targetPath, assetName, version, getChecksums, marker)) {
return targetPath;
}
}
const checksums = await getChecksums();
const url = releaseAssetUrl(assetName, version, repo);
@@ -1077,9 +1127,11 @@ module.exports = {
run,
_internal: {
isOptionalInstall,
adoptExistingBinaryIfValid,
shouldIgnoreInstallFailure,
defaultTimeoutMs,
defaultStallMs,
ensureBinary,
maxAttempts,
withRetry,
},
+109 -1
View File
@@ -1,5 +1,7 @@
const assert = require("node:assert/strict");
const crypto = require("node:crypto");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const test = require("node:test");
@@ -7,7 +9,45 @@ const installScript = fs.readFileSync(
path.join(__dirname, "..", "scripts", "install.js"),
"utf8",
);
const { installFailureHint } = require("../scripts/install");
const { installFailureHint, _internal } = require("../scripts/install");
function sha256(content) {
return crypto.createHash("sha256").update(content).digest("hex");
}
async function makeTempDir(t) {
const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "deepseek-install-test-"));
t.after(() => fs.promises.rm(dir, { force: true, recursive: true }));
return dir;
}
async function exists(file) {
return fs.promises.access(file).then(
() => true,
() => false,
);
}
async function withoutForcedDownload(callback) {
const previousTui = process.env.DEEPSEEK_TUI_FORCE_DOWNLOAD;
const previousLegacy = process.env.DEEPSEEK_FORCE_DOWNLOAD;
delete process.env.DEEPSEEK_TUI_FORCE_DOWNLOAD;
delete process.env.DEEPSEEK_FORCE_DOWNLOAD;
try {
return await callback();
} finally {
if (previousTui === undefined) {
delete process.env.DEEPSEEK_TUI_FORCE_DOWNLOAD;
} else {
process.env.DEEPSEEK_TUI_FORCE_DOWNLOAD = previousTui;
}
if (previousLegacy === undefined) {
delete process.env.DEEPSEEK_FORCE_DOWNLOAD;
} else {
process.env.DEEPSEEK_FORCE_DOWNLOAD = previousLegacy;
}
}
}
test("install script checks Node support before loading helpers", () => {
const guardIndex = installScript.indexOf("assertSupportedNode();");
@@ -70,3 +110,71 @@ test("install failure hint checks configured release base when override is alrea
}
}
});
test("ensureBinary adopts a manually placed target binary after checksum validation", async (t) => {
const dir = await makeTempDir(t);
const target = path.join(dir, process.platform === "win32" ? "deepseek.exe" : "deepseek");
const assetName = process.platform === "win32" ? "deepseek-windows-x64.exe" : "deepseek-linux-x64";
const version = "0.8.25";
const content = Buffer.from("manual deepseek binary");
let checksumLoads = 0;
await fs.promises.writeFile(target, content, { mode: 0o600 });
await fs.promises.writeFile(`${target}.version`, "0.8.24", "utf8");
const result = await withoutForcedDownload(() =>
_internal.ensureBinary(target, assetName, version, "Hmbown/DeepSeek-TUI", async () => {
checksumLoads += 1;
return new Map([[assetName, sha256(content)]]);
}),
);
assert.equal(result, target);
assert.equal(checksumLoads, 1);
assert.equal(await fs.promises.readFile(`${target}.version`, "utf8"), version);
if (process.platform !== "win32") {
assert.notEqual((await fs.promises.stat(target)).mode & 0o111, 0);
}
});
test("ensureBinary adopts an official release-named binary placed in downloads", async (t) => {
const dir = await makeTempDir(t);
const target = path.join(dir, process.platform === "win32" ? "deepseek.exe" : "deepseek");
const assetName = process.platform === "win32" ? "deepseek-windows-x64.exe" : "deepseek-linux-x64";
const assetPath = path.join(dir, assetName);
const version = "0.8.25";
const content = Buffer.from("official release binary");
await fs.promises.writeFile(assetPath, content);
const result = await withoutForcedDownload(() =>
_internal.ensureBinary(target, assetName, version, "Hmbown/DeepSeek-TUI", async () =>
new Map([[assetName, sha256(content)]]),
),
);
assert.equal(result, target);
assert.equal(await exists(target), true);
assert.equal(await exists(assetPath), false);
assert.equal(await fs.promises.readFile(`${target}.version`, "utf8"), version);
});
test("manual binaries with mismatched checksums are not adopted", async (t) => {
const dir = await makeTempDir(t);
const target = path.join(dir, process.platform === "win32" ? "deepseek.exe" : "deepseek");
const assetName = process.platform === "win32" ? "deepseek-windows-x64.exe" : "deepseek-linux-x64";
const content = Buffer.from("wrong binary bytes");
await fs.promises.writeFile(target, content);
const adopted = await _internal.adoptExistingBinaryIfValid(
target,
assetName,
"0.8.25",
async () => new Map([[assetName, sha256(Buffer.from("different bytes"))]]),
`${target}.version`,
);
assert.equal(adopted, false);
assert.equal(await exists(`${target}.version`), false);
});