返回 DeepSeek-TUI-2026
verify-release-assets.js
根目录 / npm / deepseek-tui / scripts / verify-release-assets.js
1 const https = require("https");
2 const http = require("http");
3 const {
4 allAssetNames,
5 allReleaseAssetNames,
6 checksumManifestUrl,
7 releaseAssetUrl,
8 } = require("./artifacts");
9
10 const pkg = require("../package.json");
11
12 function resolveBinaryVersion() {
13 const configuredVersion =
14 process.env.DEEPSEEK_TUI_VERSION ||
15 process.env.DEEPSEEK_VERSION ||
16 pkg.deepseekBinaryVersion ||
17 pkg.version;
18 return String(configuredVersion).trim();
19 }
20
21 function resolveRepo() {
22 return process.env.DEEPSEEK_TUI_GITHUB_REPO || process.env.DEEPSEEK_GITHUB_REPO || "Hmbown/DeepSeek-TUI";
23 }
24
25 function requestStatus(url, method = "HEAD", redirects = 0) {
26 if (redirects > 10) {
27 throw new Error(`Too many redirects while checking ${url}`);
28 }
29 const client = url.startsWith("https:") ? https : http;
30 return new Promise((resolve, reject) => {
31 const req = client.request(
32 url,
33 {
34 method,
35 headers: {
36 "User-Agent": "deepseek-tui-npm-release-check",
37 },
38 },
39 (res) => {
40 const status = res.statusCode || 0;
41 const location = res.headers.location;
42 res.resume();
43 if (status >= 300 && status < 400 && location) {
44 const next = new URL(location, url).toString();
45 resolve(requestStatus(next, method, redirects + 1));
46 return;
47 }
48 resolve(status);
49 },
50 );
51 req.on("error", reject);
52 req.end();
53 });
54 }
55
56 async function verifyAsset(url, label) {
57 let status = await requestStatus(url, "HEAD");
58 if (status === 403 || status === 405) {
59 status = await requestStatus(url, "GET");
60 }
61 if (status < 200 || status >= 400) {
62 throw new Error(`${label} returned HTTP ${status} (${url})`);
63 }
64 }
65
66 async function downloadText(url) {
67 const client = url.startsWith("https:") ? https : http;
68 return new Promise((resolve, reject) => {
69 client
70 .get(
71 url,
72 {
73 headers: {
74 "User-Agent": "deepseek-tui-npm-release-check",
75 },
76 },
77 (res) => {
78 const status = res.statusCode || 0;
79 if (status >= 300 && status < 400 && res.headers.location) {
80 const next = new URL(res.headers.location, url).toString();
81 resolve(downloadText(next));
82 return;
83 }
84 if (status !== 200) {
85 reject(new Error(`Request failed with status ${status}: ${url}`));
86 res.resume();
87 return;
88 }
89 const chunks = [];
90 res.setEncoding("utf8");
91 res.on("data", (chunk) => chunks.push(chunk));
92 res.on("end", () => resolve(chunks.join("")));
93 },
94 )
95 .on("error", reject);
96 });
97 }
98
99 function parseChecksumManifest(text) {
100 const checksums = new Map();
101 for (const line of text.split(/\r?\n/)) {
102 const trimmed = line.trim();
103 if (!trimmed) {
104 continue;
105 }
106 const match = trimmed.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
107 if (!match) {
108 throw new Error(`Invalid checksum manifest line: ${trimmed}`);
109 }
110 checksums.set(match[2], match[1].toLowerCase());
111 }
112 return checksums;
113 }
114
115 async function run() {
116 const version = resolveBinaryVersion();
117 const repo = resolveRepo();
118 const assets = allReleaseAssetNames();
119
120 console.log(`Verifying ${assets.length} release assets for ${repo}@v${version}...`);
121 for (const asset of assets) {
122 const url = releaseAssetUrl(asset, version, repo);
123 await verifyAsset(url, asset);
124 console.log(` ok ${asset}`);
125 }
126 const checksums = parseChecksumManifest(
127 await downloadText(checksumManifestUrl(version, repo)),
128 );
129 for (const asset of allAssetNames()) {
130 if (!checksums.has(asset)) {
131 throw new Error(`Checksum manifest is missing ${asset}`);
132 }
133 }
134 console.log("Release assets verified.");
135 }
136
137 run().catch((error) => {
138 console.error("Release asset verification failed:", error.message);
139 process.exit(1);
140 });
141
141 lines JAVASCRIPT