Add portable Windows desktop downloads and release uploads
This commit is contained in:
23
scripts/assemble-desktop-release.mjs
Normal file
23
scripts/assemble-desktop-release.mjs
Normal file
@@ -0,0 +1,23 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { createHash } from "node:crypto";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export function assembleDesktopRelease(root) {
|
||||
const directory = path.join(root, ".desktop-release");
|
||||
const manifestPath = path.join(directory, "manifest.json");
|
||||
if (!fs.existsSync(manifestPath)) return;
|
||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
||||
if (!/^MineGeo-[\d.]+-windows-x64\.exe$/.test(manifest.name)) throw new Error("Invalid desktop release name");
|
||||
if (!Number.isInteger(manifest.parts) || manifest.parts < 1 || manifest.parts > 100) throw new Error("Invalid desktop release parts");
|
||||
const binary = Buffer.concat(Array.from({ length: manifest.parts }, (_, i) => fs.readFileSync(path.join(directory, `part-${i}`))));
|
||||
if (binary.length !== manifest.bytes || createHash("sha256").update(binary).digest("hex") !== manifest.sha256) throw new Error("Desktop release checksum mismatch");
|
||||
const destination = path.join(root, "public", "desktop");
|
||||
fs.mkdirSync(destination, { recursive: true });
|
||||
fs.writeFileSync(path.join(destination, manifest.name), binary);
|
||||
console.log(`Verified and assembled desktop release: ${manifest.name}`);
|
||||
}
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
assembleDesktopRelease(path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."));
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import os from "node:os";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { unzipSync } from "fflate";
|
||||
import { createHash } from "node:crypto";
|
||||
|
||||
const defaultRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
@@ -52,7 +53,7 @@ export function deploymentFiles(root = defaultRoot) {
|
||||
if (relative === "package.json") {
|
||||
const pkg = JSON.parse(data);
|
||||
pkg.scripts = {
|
||||
prebuild: "node scripts/clean-build.mjs",
|
||||
prebuild: "node scripts/assemble-desktop-release.mjs && node scripts/clean-build.mjs",
|
||||
build: "next build",
|
||||
start: "next start",
|
||||
};
|
||||
@@ -89,6 +90,15 @@ export function deploymentFiles(root = defaultRoot) {
|
||||
throw new Error(
|
||||
`Private Git server reference found in ${relative}. Remove it before deploying.`,
|
||||
);
|
||||
if (relative.startsWith("public/desktop/") && relative.endsWith(".exe") && data.length > 10 * 1024 * 1024) {
|
||||
const chunkSize = 6 * 1024 * 1024;
|
||||
const parts = Math.ceil(data.length / chunkSize);
|
||||
for (let index = 0; index < parts; index++) {
|
||||
files.push({ file: `.desktop-release/part-${index}`, data: data.subarray(index * chunkSize, (index + 1) * chunkSize).toString("base64"), encoding: "base64" });
|
||||
}
|
||||
files.push({ file: ".desktop-release/manifest.json", data: Buffer.from(JSON.stringify({ name: path.basename(relative), parts, bytes: data.length, sha256: createHash("sha256").update(data).digest("hex") })).toString("base64"), encoding: "base64" });
|
||||
return;
|
||||
}
|
||||
files.push({
|
||||
file: relative,
|
||||
data: data.toString("base64"),
|
||||
@@ -124,6 +134,7 @@ export function deploymentFiles(root = defaultRoot) {
|
||||
"postcss.config.mjs",
|
||||
"vercel.json",
|
||||
"scripts/clean-build.mjs",
|
||||
"scripts/assemble-desktop-release.mjs",
|
||||
]) {
|
||||
if (fs.existsSync(path.join(root, file))) add(path.join(root, file), file);
|
||||
}
|
||||
@@ -220,7 +231,36 @@ export async function deploy(
|
||||
);
|
||||
return data;
|
||||
}
|
||||
let deployment = await api("/v13/deployments", payload);
|
||||
const files = [];
|
||||
for (const file of payload.files) {
|
||||
const data = Buffer.from(file.data || "", "base64");
|
||||
if (file.encoding !== "base64" || data.length <= 10 * 1024 * 1024) {
|
||||
files.push(file);
|
||||
continue;
|
||||
}
|
||||
const sha = createHash("sha1").update(data).digest("hex");
|
||||
const response = await request(
|
||||
`https://api.vercel.com/v2/files?teamId=${encodeURIComponent(team)}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/octet-stream",
|
||||
"Content-Length": String(data.length),
|
||||
"x-vercel-digest": sha,
|
||||
},
|
||||
body: data,
|
||||
signal: AbortSignal.timeout(300000),
|
||||
},
|
||||
);
|
||||
if (!response.ok)
|
||||
throw new Error(
|
||||
`Vercel file upload failed (${response.status}): ${file.file}`,
|
||||
);
|
||||
files.push({ file: file.file, sha, size: data.length });
|
||||
log(`Uploaded release: ${file.file}`);
|
||||
}
|
||||
let deployment = await api("/v13/deployments", { ...payload, files });
|
||||
if (!deployment.id) throw new Error("Vercel did not return a deployment ID.");
|
||||
log(
|
||||
`Uploaded ${payload.files.length} site files in one request. Waiting for Vercel…`,
|
||||
|
||||
Reference in New Issue
Block a user