返回 DeepSeek-Reasonix
publish-homebrew-cask.mjs
根目录 / scripts / publish-homebrew-cask.mjs
1 import { readFileSync } from "node:fs";
2 import path from "node:path";
3 import { pathToFileURL } from "node:url";
4
5 const VERSION_RE = /version "(\d+)\.(\d+)\.(\d+)"/;
6
7 function compareVersion(left, right) {
8 const a = left.map(Number); const b = right.map(Number);
9 for (let i = 0; i < 3; i += 1) if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1;
10 return 0;
11 }
12
13 export function decideCaskUpdate(existing, candidate) {
14 if (existing === candidate) return "reuse";
15 const current = existing.match(VERSION_RE);
16 const next = candidate.match(VERSION_RE);
17 if (!next) throw new Error("candidate cask has no canonical version");
18 if (current && compareVersion(next.slice(1), current.slice(1)) < 0) throw new Error("refusing to move Homebrew cask backwards");
19 if (current && compareVersion(next.slice(1), current.slice(1)) === 0) throw new Error("existing Homebrew version has conflicting content");
20 return "publish";
21 }
22
23 async function request(url, token, options = {}) {
24 const response = await fetch(url, {
25 ...options,
26 headers: { Accept: "application/vnd.github+json", Authorization: `Bearer ${token}`, "Content-Type": "application/json", "X-GitHub-Api-Version": "2022-11-28", ...options.headers },
27 });
28 if (!response.ok) throw new Error(`GitHub API ${response.status}: ${await response.text()}`);
29 return response.json();
30 }
31
32 async function publish(file) {
33 const token = process.env.HOMEBREW_TAP_TOKEN;
34 if (!token) throw new Error("HOMEBREW_TAP_TOKEN is required");
35 const candidate = readFileSync(file, "utf8");
36 const api = "https://api.github.com/repos/esengine/homebrew-reasonix/contents/Casks/reasonix.rb";
37 const current = await request(api, token);
38 const existing = Buffer.from(current.content, "base64").toString("utf8");
39 if (decideCaskUpdate(existing, candidate) === "reuse") return;
40 await request(api, token, {
41 method: "PUT",
42 body: JSON.stringify({
43 message: `chore: update Reasonix to ${candidate.match(VERSION_RE).slice(1).join(".")}`,
44 content: Buffer.from(candidate).toString("base64"),
45 sha: current.sha,
46 branch: "main",
47 committer: { name: "reasonix", email: "reasonix@deepseek.com" },
48 }),
49 });
50 }
51
52 if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
53 if (!process.argv[2]) throw new Error("usage: publish-homebrew-cask.mjs CASK_FILE");
54 await publish(process.argv[2]);
55 }
56
56 lines Plain Text