Files
q-edit/scripts/gitea-release.mjs
2025-09-06 17:24:09 -04:00

133 lines
3.8 KiB
JavaScript
Executable File

#!/usr/bin/env node
// Create a Gitea release and upload an asset.
// Usage:
// node scripts/gitea-release.mjs --tag v1.1.1 --name "Q-Edit v1.1.1" \
// --notes docs/RELEASE_NOTES_v1.1.1.md --asset q-edit_dist.zip
// Env:
// GITEA_TOKEN (required)
// GITEA_BASE (optional, default parsed from git remote origin; fallback https://gitea.qortal.link)
import { execSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
function parseArgs(argv) {
const out = {};
for (let i = 2; i < argv.length; i++) {
const a = argv[i];
if (a === "--tag") {
out.tag = argv[++i];
} else if (a === "--name") {
out.name = argv[++i];
} else if (a === "--notes") {
out.notes = argv[++i];
} else if (a === "--asset") {
out.asset = argv[++i];
}
}
return out;
}
function getOrigin() {
try {
const s = execSync("git config --get remote.origin.url", { encoding: "utf8" }).trim();
return s;
} catch {
return "";
}
}
function deriveRepo(originUrl) {
try {
const u = new URL(originUrl);
const parts = u.pathname
.replace(/\.git$/, "")
.split("/")
.filter(Boolean);
const owner = parts[0];
const repo = parts[1];
const base = `${u.protocol}//${u.host}`;
return { base, owner, repo };
} catch {
return { base: "https://gitea.qortal.link", owner: "", repo: "" };
}
}
async function main() {
const { tag, name, notes, asset } = parseArgs(process.argv);
if (!tag || !name) {
console.error("Usage: --tag vX.Y.Z --name 'Q-Edit vX.Y.Z' --notes <file> [--asset <file>]");
process.exit(1);
}
const token = process.env.GITEA_TOKEN;
if (!token) {
console.error("GITEA_TOKEN env var is required to create a release.");
process.exit(2);
}
const origin = getOrigin();
const parsed = deriveRepo(origin);
const base = process.env.GITEA_BASE || parsed.base || "https://gitea.qortal.link";
const owner = process.env.GITEA_OWNER || parsed.owner;
const repo = process.env.GITEA_REPO || parsed.repo;
if (!owner || !repo) {
console.error(
"Could not determine owner/repo from git remote. Set GITEA_OWNER and GITEA_REPO."
);
process.exit(3);
}
const body = notes ? fs.readFileSync(notes, "utf8") : "";
const createUrl = `${base}/api/v1/repos/${owner}/${repo}/releases`;
const createRes = await fetch(createUrl, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `token ${token}`,
},
body: JSON.stringify({
tag_name: tag,
name,
body,
draft: false,
prerelease: false,
}),
});
if (!createRes.ok) {
const t = await createRes.text();
console.error("Failed to create release:", createRes.status, t);
process.exit(4);
}
const rel = await createRes.json();
console.log("Created release:", rel.html_url || rel.url || rel.id);
if (asset && fs.existsSync(asset)) {
const st = fs.statSync(asset);
const assetUrl = `${base}/api/v1/repos/${owner}/${repo}/releases/${rel.id}/assets?name=${encodeURIComponent(path.basename(asset))}`;
const buf = fs.readFileSync(asset);
const blob = new Blob([buf], { type: "application/zip" });
const fd = new FormData();
fd.append("attachment", blob, path.basename(asset));
const upRes = await fetch(assetUrl, {
method: "POST",
headers: { Authorization: `token ${token}` },
body: fd,
});
if (!upRes.ok) {
const t = await upRes.text();
console.error("Failed to upload asset:", upRes.status, t);
process.exit(5);
}
console.log(`Uploaded asset ${path.basename(asset)} (${st.size} bytes).`);
} else if (asset) {
console.warn(`Asset not found: ${asset} (skipping upload)`);
}
}
main().catch((e) => {
console.error("gitea-release failed:", e);
process.exit(99);
});