241 lines
7.8 KiB
JavaScript
241 lines
7.8 KiB
JavaScript
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" });
|
|
|
|
import { createRequire } from "node:module";
|
|
const require = createRequire(import.meta.url);
|
|
const conventionalChangelog =
|
|
require("conventional-changelog-conventionalcommits").default ||
|
|
require("conventional-changelog-conventionalcommits");
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Corrected function to get the latest changelog entry from CHANGELOG.md
|
|
const getLatestChangelog = async () => {
|
|
try {
|
|
const changelogContent = await fs.readFile(
|
|
path.resolve(__dirname, "../CHANGELOG.md"),
|
|
"utf8"
|
|
);
|
|
|
|
// Regex to capture the content of the latest release.
|
|
// It looks for a line starting with "## [" (the start of a release heading)
|
|
// and captures everything until the next "## [" or the end of the file.
|
|
// The `m` flag makes `^` and `$` match start/end of lines, not just string.
|
|
// The `s` flag allows `.` to match newlines.
|
|
const latestEntryMatch = changelogContent.match(
|
|
/^(## \[.*?\] - .*?\n[\s\S]*?)(?=^## \[|\Z)/m
|
|
);
|
|
|
|
if (latestEntryMatch && latestEntryMatch[1]) {
|
|
// Split the matched content into lines
|
|
const lines = latestEntryMatch[1].split("\n");
|
|
|
|
// Find the index of the first non-heading line
|
|
// The first line is usually the heading (e.g., "## [1.0.0] - 2024-01-01")
|
|
// So we start checking from the second line (index 1).
|
|
let firstContentLineIndex = -1;
|
|
for (let i = 1; i < lines.length; i++) {
|
|
if (lines[i].trim() !== "") {
|
|
// Check for non-empty and non-whitespace lines
|
|
firstContentLineIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (firstContentLineIndex !== -1) {
|
|
// Join the lines from the first content line onwards and trim any leading/trailing whitespace
|
|
return lines.slice(firstContentLineIndex).join("\n").trim();
|
|
}
|
|
return ""; // No content found after heading
|
|
} else {
|
|
console.warn(
|
|
"Could not find the latest changelog entry in CHANGELOG.md. Ensure it's formatted correctly."
|
|
);
|
|
return "No changelog notes available."; // Default message if not found
|
|
}
|
|
} catch (err) {
|
|
console.error("Error reading or parsing CHANGELOG.md:", err);
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
const releaseNotes = await getLatestChangelog();
|
|
|
|
// 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: `Release ${fullVersion}`,
|
|
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;
|
|
};
|
|
|
|
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); // fix this later and just update the readme.
|
|
} catch (err) {
|
|
console.error(err);
|
|
process.exit(1);
|
|
}
|
|
})();
|