const version = process.argv[2]; if (!version) { console.error("Version not passed to create-gitea-release.js"); process.exit(1); } import fs from "fs-extra"; import path from "path"; import { spawnSync } from "child_process"; import { fileURLToPath } from "url"; import fetch from "node-fetch"; import dotenv from "dotenv"; dotenv.config({ path: "./.env" }); // Resolve the directory of the current script const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Absolute path to BUILD_NUMBER const buildNumberPath = path.resolve(__dirname, "../BUILD_NUMBER"); // Load build number from BUILD_NUMBER file let buildNumber = "0"; try { const rawBuild = fs.readFileSync(buildNumberPath, "utf8"); console.log("Raw build", rawBuild); buildNumber = rawBuild.trim(); } catch (e) { console.log(e); console.warn("BUILD_NUMBER file not found, defaulting to 0"); } const fullVersion = `${version}.${buildNumber}`; const { GITEA_URL, GITEA_USERNAME, GITEA_REPO, GITEA_TOKEN } = process.env; if (!GITEA_URL || !GITEA_USERNAME || !GITEA_REPO || !GITEA_TOKEN) { console.error("Missing required environment variables"); process.exit(1); } // Step 1: Generate or update CHANGELOG.md console.log("Generating CHANGELOG.md..."); const result = spawnSync( "npx", [ "conventional-changelog", "-p", "conventionalcommits", "-i", "CHANGELOG.md", "-s", "-r", "0", ], { stdio: "inherit", shell: true } ); if (result.status !== 0) { console.error("Failed to generate changelog"); process.exit(1); } // Step 2: Read changelog content for current version const changelog = await fs.readFile("CHANGELOG.md", "utf8"); const regex = new RegExp(`## \\[${version}\\][\\s\\S]*?(?=## \\[|$)`, "m"); const releaseNotes = changelog.match(regex)?.[0] || changelog; console.log(`Release notes for v${version} saved.`); // Step 3: Create or update Gitea release const createOrUpdateRelease = async () => { const tagName = `v${version}`; const apiBase = `https://${GITEA_URL}/api/v1/repos/${GITEA_USERNAME}/${GITEA_REPO}`; const existing = await fetch(`${apiBase}/releases/tags/${tagName}`, { headers: { Authorization: `token ${GITEA_TOKEN}` }, }); let release; if (existing.ok) { const existingRelease = await existing.json(); console.log(`Release ${tagName} already exists. Updating it.`); const updateResponse = await fetch( `${apiBase}/releases/${existingRelease.id}`, { method: "PATCH", headers: { Authorization: `token ${GITEA_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: tagName, body: releaseNotes, draft: false, prerelease: true, }), } ); if (!updateResponse.ok) { const errorText = await updateResponse.text(); throw new Error( `Failed to update release: ${updateResponse.status} - ${errorText}` ); } release = await updateResponse.json(); console.log("Release updated:", release.html_url || release.url); } else if (existing.status === 404) { const createResponse = await fetch(`${apiBase}/releases`, { method: "POST", headers: { Authorization: `token ${GITEA_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ tag_name: tagName, name: tagName, body: releaseNotes, draft: false, prerelease: true, }), }); if (!createResponse.ok) { const errorText = await createResponse.text(); throw new Error( `Failed to create release: ${createResponse.status} - ${errorText}` ); } release = await createResponse.json(); console.log("Release created:", release.html_url || release.url); } else { const errorText = await existing.text(); throw new Error( `Failed to check release: ${existing.status} - ${errorText}` ); } return release; }; // Step 4: Upload asset const uploadAsset = async (release) => { const apiUrl = `https://${GITEA_URL}/api/v1/repos/${GITEA_USERNAME}/${GITEA_REPO}/releases/assets?tag=${release.tag_name}`; const filePath = `releases/release-${fullVersion}.zip`; if (!(await fs.pathExists(filePath))) { console.warn(`Zip file not found: ${filePath}. Skipping asset upload.`); return; } const FormData = (await import("form-data")).default; const form = new FormData(); form.append("name", `release-${fullVersion}.zip`); form.append("attachment", fs.createReadStream(filePath)); const response = await fetch(apiUrl, { method: "POST", headers: { Authorization: `token ${GITEA_TOKEN}`, ...form.getHeaders(), }, body: form, }); if (!response.ok) { const errorText = await response.text(); throw new Error( `Failed to upload asset: ${response.status} - ${errorText}` ); } const asset = await response.json(); console.log("Asset uploaded:", asset.browser_download_url || asset.url); }; // Run everything (async () => { try { const release = await createOrUpdateRelease(); await uploadAsset(release); } catch (err) { console.error(err); process.exit(1); } })();