updater fixed

This commit is contained in:
NuklearRabbit
2026-07-28 00:03:33 +02:00
parent f990c26014
commit d4d77c827a
18 changed files with 451 additions and 71 deletions
+60 -27
View File
@@ -11,6 +11,14 @@ const configuredUserData =
path.join(app.getPath("appData"), "forgeflow");
app.setPath("userData", path.resolve(configuredUserData));
function safeRepositoryPart(value, label) {
const text = String(value || "").trim();
if (!/^[a-zA-Z0-9_.-]+$/.test(text)) {
throw new Error(`${label} contains unsupported characters.`);
}
return text;
}
async function api(baseUrl, token, pathname, options = {}) {
const response = await fetch(`${baseUrl}/api/v1${pathname}`, {
...options,
@@ -28,10 +36,11 @@ async function api(baseUrl, token, pathname, options = {}) {
} catch {
data = text;
}
if (!response.ok)
if (!response.ok) {
throw new Error(
`Gitea returned HTTP ${response.status}: ${data?.message || text || response.statusText}`,
);
}
return data;
}
@@ -40,16 +49,32 @@ app.whenReady().then(async () => {
const manifest = JSON.parse(
await fs.readFile(path.join(root, "package.json"), "utf8"),
);
const config = JSON.parse(
await fs.readFile(
path.join(configuredUserData, "forgeflow-config.json"),
"utf8",
),
);
const configPath = path.join(configuredUserData, "forgeflow-config.json");
const config = JSON.parse(await fs.readFile(configPath, "utf8"));
if (!config?.gitea?.encryptedToken) {
throw new Error(
`No encrypted Gitea token was found in ${configPath}. Sign in to Gitea once from ForgeFlow first.`,
);
}
const token = safeStorage.decryptString(
Buffer.from(config.gitea.encryptedToken, "base64"),
);
const baseUrl = String(config.gitea.baseUrl).replace(/\/+$/, "");
const baseUrl = String(config.gitea.baseUrl || "").replace(/\/+$/, "");
if (!/^https?:\/\//i.test(baseUrl)) {
throw new Error("The configured Gitea base URL is invalid.");
}
const owner = safeRepositoryPart(
process.env.FORGEFLOW_RELEASE_OWNER || config.updates?.owner || "Jens",
"Release repository owner",
);
const repo = safeRepositoryPart(
process.env.FORGEFLOW_RELEASE_REPO || config.updates?.repo || "ForgeFlow",
"Release repository name",
);
const branch = safeRepositoryPart(
process.env.FORGEFLOW_RELEASE_BRANCH || config.updates?.branch || "main",
"Release branch",
);
const version = manifest.version;
const tag = `v${version}`;
const commit = execFileSync("git", ["rev-parse", "HEAD"], {
@@ -58,13 +83,16 @@ app.whenReady().then(async () => {
}).trim();
const remote = execFileSync(
"git",
["ls-remote", "origin", "refs/heads/main"],
["ls-remote", "origin", `refs/heads/${branch}`],
{ cwd: root, encoding: "utf8" },
)
.trim()
.split(/\s+/)[0];
if (commit !== remote)
throw new Error("Local HEAD is not the published origin/main commit.");
if (commit !== remote) {
throw new Error(
`Local HEAD is not the published origin/${branch} commit. Push the exact source before publishing binaries.`,
);
}
const notesPath = path.join(root, "docs", `RELEASE_NOTES_${version}.md`);
const body = await fs.readFile(notesPath, "utf8");
let release;
@@ -72,22 +100,27 @@ app.whenReady().then(async () => {
release = await api(
baseUrl,
token,
`/repos/Jens/ForgeFlow/releases/tags/${encodeURIComponent(tag)}`,
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/tags/${encodeURIComponent(tag)}`,
);
} catch (error) {
if (!/HTTP 404/.test(error.message)) throw error;
release = await api(baseUrl, token, "/repos/Jens/ForgeFlow/releases", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
tag_name: tag,
target_commitish: commit,
name: `ForgeFlow ${version}`,
body,
draft: false,
prerelease: false,
}),
});
release = await api(
baseUrl,
token,
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
tag_name: tag,
target_commitish: commit,
name: `ForgeFlow ${version}`,
body,
draft: false,
prerelease: false,
}),
},
);
}
const binaries = [
@@ -115,7 +148,7 @@ app.whenReady().then(async () => {
await api(
baseUrl,
token,
`/repos/Jens/ForgeFlow/releases/${release.id}/assets/${existing.id}`,
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/${release.id}/assets/${existing.id}`,
{ method: "DELETE" },
);
}
@@ -124,7 +157,7 @@ app.whenReady().then(async () => {
const uploaded = await api(
baseUrl,
token,
`/repos/Jens/ForgeFlow/releases/${release.id}/assets?name=${encodeURIComponent(name)}`,
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/${release.id}/assets?name=${encodeURIComponent(name)}`,
{
method: "POST",
body: form,
@@ -139,7 +172,7 @@ app.whenReady().then(async () => {
}
}
console.log(
`PASS ForgeFlow ${version} binary release published for ${commit.slice(0, 7)}`,
`PASS ForgeFlow ${version} binary release published to ${owner}/${repo} for ${commit.slice(0, 7)}`,
);
app.exit(0);
} catch (error) {
+21 -2
View File
@@ -72,6 +72,7 @@ const required = [
"docs/RELEASE_NOTES_0.8.8.md",
"docs/RELEASE_NOTES_0.8.9.md",
"docs/RELEASE_NOTES_0.9.0.md",
"docs/RELEASE_NOTES_0.9.1.md",
"docs/UPDATING.md",
"docs/DIAGNOSTICS.md",
"docs/DEPLOYMENT_SETUP.md",
@@ -110,9 +111,9 @@ for (const file of required) await access(path.join(root, file));
const packageJson = JSON.parse(
await readFile(path.join(root, "package.json"), "utf8"),
);
if (packageJson.version !== "0.9.0")
if (packageJson.version !== "0.9.1")
throw new Error(
`Expected package version 0.9.0, got ${packageJson.version}.`,
`Expected package version 0.9.1, got ${packageJson.version}.`,
);
const sourceManifest = await readFile(
path.join(root, "SOURCE_MANIFEST.txt"),
@@ -362,6 +363,24 @@ for (const phrase of [
]) {
if (!release090.includes(phrase)) throw new Error(`0.9.0 release notes are missing: ${phrase}`);
}
const release091 = await readFile(path.join(root, "docs/RELEASE_NOTES_0.9.1.md"), "utf8");
for (const phrase of [
"browser_download_url",
"cross-origin",
"manual installer",
"in-app updates",
]) {
if (!release091.includes(phrase)) throw new Error(`0.9.1 release notes are missing: ${phrase}`);
}
const giteaUpdateSource = await readFile(path.join(root, "src/main/gitea-service.cjs"), "utf8");
for (const phrase of [
"browser_download_url",
"insecure cross-origin",
"downloadReleaseAsset",
]) {
if (!giteaUpdateSource.includes(phrase)) throw new Error(`0.9.1 updater repair is missing: ${phrase}`);
}
const gitSource = await readFile(
path.join(root, "src/main/git-service.cjs"),
"utf8",