#!/usr/bin/env node // publish-full.js —— 发布完整安装包到自建 gitea。 // 把 dist/ 下的 dmg/zip/blockmap/latest-*.yml 上传到: // - latest release(electron-updater 自动更新目标,文件名固定,覆盖上传) // - v release(版本存档,供手动下载) "use strict"; const fs = require("fs"); const os = require("os"); const path = require("path"); const { execFileSync } = require("child_process"); const ROOT = path.join(__dirname, ".."); const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8")); const version = pkg.version; 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); } const OWNER = "mhgmh", REPO = "codex-market-app"; const API = `https://up.tokendun.cc/api/v1/repos/${OWNER}/${REPO}`; const DIST = path.join(ROOT, "dist"); // 要上传的产物:dmg/zip/blockmap + latest-mac.yml const files = fs.readdirSync(DIST).filter((f) => /\.(dmg|zip)(\.blockmap)?$/.test(f) || /^latest.*\.yml$/.test(f) ); if (!files.length) { console.error("dist/ 下没有可上传的产物"); process.exit(1); } console.log(`待上传 ${files.length} 个文件:\n ` + files.join("\n ")); 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) { try { return JSON.parse(api(method, urlPath, body)); } catch { return null; } } // 确保 release 存在(按 tag 取,缺则建) function ensureRelease(tag, name, prerelease) { 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, body: `TD工具箱 ${version} 完整安装包`, draft: false, prerelease: !!prerelease, }); } if (!rel || !rel.id) { console.error(`无法创建/获取 release ${tag}`); process.exit(1); } return rel; } function uploadTo(rel, tag) { // 删同名旧 asset,再上传 const existing = new Map((rel.assets || []).map((a) => [a.name, a.id])); for (const name of files) { if (existing.has(name)) { api("DELETE", `/releases/${rel.id}/assets/${existing.get(name)}`); console.log(` [${tag}] 删除旧 asset: ${name}`); } } for (const name of files) { const file = path.join(DIST, name); const code = execFileSync("curl", ["-s", "-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", maxBuffer: 64 * 1024 * 1024 }); console.log(` [${tag}] 上传 ${name} → HTTP ${code}`); if (code !== "201") { console.error(` 上传 ${name} 失败`); process.exit(1); } } } // 1) latest release —— electron-updater 自动更新目标 const latest = ensureRelease("latest", "自动更新通道", true); uploadTo(latest, "latest"); // 2) v release —— 版本存档 const verRel = ensureRelease(`v${version}`, `TD工具箱 ${version}`, false); uploadTo(verRel, `v${version}`); console.log(`\n✅ 完整包 v${version} 已发布到 latest 与 v${version} 两个 release。`); console.log(` 自动更新地址:https://update.tokendun.cc/${OWNER}/${REPO}/releases/download/latest`);