Files
codex-market-app/scripts/publish-codex-installer.js
T

127 lines
4.4 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
// publish-codex-installer.js —— 发布 Codex 安装器资源到自建 gitea。
//
// 用法:
// node scripts/publish-codex-installer.js /Users/mac/Downloads/Codex-Windows-x64.msix
// node scripts/publish-codex-installer.js /Users/mac/Downloads/ChatGPT.dmg
// token 可设置 GIT_TOKEN,或写入 ~/.tokendun-git-token。
"use strict";
const fs = require("fs");
const os = require("os");
const path = require("path");
const crypto = require("crypto");
const { execFileSync, spawnSync } = require("child_process");
const OWNER = "mhgmh";
const REPO = "codex-market-app";
const TAG = "codex-installers";
const MSIX_NAME = "Codex-Windows-x64.msix";
const DMG_NAME = "ChatGPT.dmg";
const ALLOWED_NAMES = new Set([MSIX_NAME, DMG_NAME]);
const API = `https://up.tokendun.cc/api/v1/repos/${OWNER}/${REPO}`;
const DL = `https://update.tokendun.cc/${OWNER}/${REPO}/releases/download/${TAG}`;
const installerPath = path.resolve(process.argv[2] || "/Users/mac/Downloads/Codex-Windows-x64.msix");
const installerName = path.basename(installerPath);
if (!ALLOWED_NAMES.has(installerName)) {
console.error(`安装包文件名必须是 ${Array.from(ALLOWED_NAMES).join(" 或 ")},当前是 ${installerName}`);
process.exit(1);
}
if (!fs.existsSync(installerPath)) {
console.error(`安装包不存在:${installerPath}`);
process.exit(1);
}
let token = process.env.GIT_TOKEN || "";
if (!token) {
try { token = fs.readFileSync(path.join(os.homedir(), ".tokendun-git-token"), "utf8").trim(); } catch {}
}
if (!token) {
console.error("缺少 gitea token:设置 GIT_TOKEN 或写入 ~/.tokendun-git-token");
process.exit(1);
}
function hashFile(file) {
return new Promise((resolve, reject) => {
const h = crypto.createHash("sha256");
fs.createReadStream(file)
.on("data", (chunk) => h.update(chunk))
.on("error", reject)
.on("end", () => resolve(h.digest("hex")));
});
}
function api(method, urlPath, body) {
const args = ["-s", "-X", method, "-H", `Authorization: token ${token}`, `${API}${urlPath}`];
if (body !== undefined) {
args.push("-H", "Content-Type: application/json", "-d", JSON.stringify(body));
}
return execFileSync("curl", args, { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
}
function apiJson(method, urlPath, body) {
const out = api(method, urlPath, body);
try { return JSON.parse(out); } catch { return null; }
}
function upload(rel, file, name) {
const result = spawnSync("curl", [
"--progress-bar",
"-o", "/dev/null",
"-w", "%{http_code}",
"-X", "POST",
"-H", `Authorization: token ${token}`,
"-F", `attachment=@${file};type=application/octet-stream`,
`${API}/releases/${rel.id}/assets?name=${encodeURIComponent(name)}`,
], { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"], maxBuffer: 64 * 1024 * 1024 });
if (result.error) throw result.error;
const code = String(result.stdout || "").trim();
console.log(`上传 ${name} → HTTP ${code}`);
if (code !== "201") {
console.error(`上传失败:${name}`);
process.exit(1);
}
}
(async () => {
const stat = fs.statSync(installerPath);
console.log(`准备发布 ${installerName}:${(stat.size / 1048576).toFixed(2)}MB`);
const sha256 = await hashFile(installerPath);
const shaPath = path.join(os.tmpdir(), `${installerName}.sha256`);
fs.writeFileSync(shaPath, `${sha256} ${installerName}\n`);
console.log(`sha256=${sha256}`);
let rel = apiJson("GET", `/releases/tags/${encodeURIComponent(TAG)}`);
if (!rel || !rel.id) {
console.log(`创建 release ${TAG} …`);
rel = apiJson("POST", "/releases", {
tag_name: TAG,
target_commitish: "main",
name: "Codex 安装器资源",
body: "TD工具箱内置安装器使用的远程安装包资源。",
draft: false,
prerelease: false,
});
}
if (!rel || !rel.id) {
console.error(`无法创建/获取 release ${TAG}`);
process.exit(1);
}
const existing = new Map((rel.assets || []).map((a) => [a.name, a.id]));
for (const name of [installerName, `${installerName}.sha256`]) {
if (existing.has(name)) {
api("DELETE", `/releases/${rel.id}/assets/${existing.get(name)}`);
console.log(`删除旧 asset: ${name}`);
}
}
upload(rel, installerPath, installerName);
upload(rel, shaPath, `${installerName}.sha256`);
console.log("\n发布完成:");
console.log(` ${DL}/${installerName}`);
console.log(` ${DL}/${installerName}.sha256`);
})();