发布候选:整合桌面端界面与发布稳定化里程碑
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { access, readFile } from "node:fs/promises";
|
||||
import { basename, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const frontendDir = fileURLToPath(new URL("../", import.meta.url));
|
||||
const packageInfo = JSON.parse(await readFile(resolve(frontendDir, "package.json"), "utf8"));
|
||||
const failures = [];
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const optionValue = (name) => {
|
||||
const index = args.indexOf(name);
|
||||
return index >= 0 ? args[index + 1] : undefined;
|
||||
};
|
||||
|
||||
const feedPath = resolve(
|
||||
frontendDir,
|
||||
optionValue("--feed") || process.env.DESKTOP_UPDATE_FEED || "src-tauri/target/release/bundle/latest.json",
|
||||
);
|
||||
const artifactDir = optionValue("--artifacts-dir") || process.env.DESKTOP_UPDATE_ARTIFACTS_DIR;
|
||||
const expectedBaseUrl = optionValue("--base-url") || process.env.DESKTOP_UPDATE_BASE_URL;
|
||||
|
||||
const fail = (message) => failures.push(message);
|
||||
const assert = (condition, message) => {
|
||||
if (!condition) fail(message);
|
||||
};
|
||||
|
||||
const assertFileExists = async (path, description) => {
|
||||
try {
|
||||
await access(path);
|
||||
} catch {
|
||||
fail(`${description} does not exist: ${path}`);
|
||||
}
|
||||
};
|
||||
|
||||
let feed;
|
||||
try {
|
||||
feed = JSON.parse(await readFile(feedPath, "utf8"));
|
||||
} catch (error) {
|
||||
fail(`Cannot read desktop update feed ${feedPath}: ${error.message}`);
|
||||
}
|
||||
|
||||
if (feed) {
|
||||
const normalizedFeedVersion = String(feed.version || "").replace(/^v/, "");
|
||||
const platforms = feed.platforms || {};
|
||||
const darwinArm = platforms["darwin-aarch64"];
|
||||
const darwinIntel = platforms["darwin-x86_64"];
|
||||
|
||||
assert(normalizedFeedVersion === packageInfo.version, `latest.json version must match package version ${packageInfo.version}.`);
|
||||
assert(Boolean(feed.pub_date || feed.pubDate), "latest.json must include a publication date.");
|
||||
assert(Boolean(darwinArm), "latest.json must include darwin-aarch64.");
|
||||
assert(Boolean(darwinIntel), "latest.json must include darwin-x86_64.");
|
||||
|
||||
const entries = [
|
||||
["darwin-aarch64", darwinArm],
|
||||
["darwin-x86_64", darwinIntel],
|
||||
];
|
||||
|
||||
for (const [platform, entry] of entries) {
|
||||
const rawUrl = entry?.url;
|
||||
const signature = entry?.signature;
|
||||
assert(typeof signature === "string" && signature.length > 80, `${platform} must include an updater signature.`);
|
||||
assert(typeof rawUrl === "string" && rawUrl.length > 0, `${platform} must include an artifact URL.`);
|
||||
if (!rawUrl) continue;
|
||||
|
||||
let url;
|
||||
try {
|
||||
url = new URL(rawUrl);
|
||||
} catch {
|
||||
fail(`${platform} artifact URL is invalid: ${rawUrl}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(url.protocol === "https:", `${platform} artifact URL must use HTTPS.`);
|
||||
assert(!/[?&]token=/i.test(url.search), `${platform} artifact URL must not contain token query parameters.`);
|
||||
assert(!url.pathname.endsWith("/latest.json"), `${platform} artifact URL must point to an immutable artifact, not latest.json.`);
|
||||
assert(url.pathname.includes(packageInfo.version), `${platform} artifact URL must include version ${packageInfo.version}.`);
|
||||
if (expectedBaseUrl) {
|
||||
assert(rawUrl.startsWith(expectedBaseUrl), `${platform} artifact URL must start with ${expectedBaseUrl}.`);
|
||||
}
|
||||
|
||||
if (artifactDir) {
|
||||
const artifactPath = resolve(artifactDir, basename(url.pathname));
|
||||
await assertFileExists(artifactPath, `${platform} updater artifact`);
|
||||
await assertFileExists(`${artifactPath}.sig`, `${platform} updater artifact signature`);
|
||||
}
|
||||
}
|
||||
|
||||
if (darwinArm?.url && darwinIntel?.url) {
|
||||
assert(darwinArm.url === darwinIntel.url, "Universal macOS latest.json must point both darwin architectures at the same artifact.");
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error(`Desktop update feed check failed:\n${failures.map((item) => ` - ${item}`).join("\n")}`);
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
console.log("Desktop update feed check passed.");
|
||||
}
|
||||
Reference in New Issue
Block a user