diff --git a/scripts/runInstaller.mjs b/scripts/runInstaller.mjs index a74d01fa..0d474649 100644 --- a/scripts/runInstaller.mjs +++ b/scripts/runInstaller.mjs @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { execFileSync } from "child_process"; +import { execFileSync, execSync } from "child_process"; import { createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { dirname, join } from "path"; import { Readable } from "stream"; @@ -24,9 +24,10 @@ import { finished } from "stream/promises"; import { fileURLToPath } from "url"; const BASE_URL = "https://github.com/Vencord/Installer/releases/latest/download/"; +const INSTALLER_PATH_DARWIN = "VencordInstaller.app/Contents/MacOS/VencordInstaller"; -const DIST_DIR = join(dirname(fileURLToPath(import.meta.url)), ".."); -const FILE_DIR = join(DIST_DIR, "dist", "Installer"); +const BASE_DIR = join(dirname(fileURLToPath(import.meta.url)), ".."); +const FILE_DIR = join(BASE_DIR, "dist", "Installer"); const ETAG_FILE = join(FILE_DIR, "etag.txt"); function getFilename() { @@ -34,8 +35,7 @@ function getFilename() { case "win32": return "VencordInstaller.exe"; case "darwin": - // return "VencordInstaller.MacOS.zip"; - throw new Error("PR Mac support if you want it. Or use a better OS that doesn't suck"); + return "VencordInstaller.MacOS.zip"; case "linux": return "VencordInstaller-" + (process.env.WAYLAND_DISPLAY ? "wayland" : "x11"); default: @@ -49,8 +49,14 @@ async function ensureBinary() { mkdirSync(FILE_DIR, { recursive: true }); - const installerFile = join(FILE_DIR, filename); - const etag = existsSync(installerFile) && existsSync(ETAG_FILE) ? readFileSync(ETAG_FILE, "utf-8") : null; + const downloadName = join(FILE_DIR, filename); + const outputFile = process.platform === "darwin" + ? join(FILE_DIR, "VencordInstaller") + : downloadName; + + const etag = existsSync(outputFile) && existsSync(ETAG_FILE) + ? readFileSync(ETAG_FILE, "utf-8") + : null; const res = await fetch(BASE_URL + filename, { headers: { @@ -58,40 +64,63 @@ async function ensureBinary() { "If-None-Match": etag } }); + if (res.status === 304) { console.log("Up to date, not redownloading!"); - return installerFile; + return outputFile; } - - if (!res.ok) { + if (!res.ok) throw new Error(`Failed to download installer: ${res.status} ${res.statusText}`); + + writeFileSync(ETAG_FILE, res.headers.get("etag")); + + if (process.platform === "darwin") { + console.log("Unzipping..."); + const zip = new Uint8Array(await res.arrayBuffer()); + + const ff = await import("fflate"); + const bytes = ff.unzipSync(zip, { + filter: f => f.name === INSTALLER_PATH_DARWIN + })[INSTALLER_PATH_DARWIN]; + + writeFileSync(outputFile, bytes, { mode: 0o755 }); + + console.log("Overriding security policy for installer binary (this is required to run it)"); + console.log("xattr might error, that's okay"); + + const logAndRun = cmd => { + console.log("Running", cmd); + try { + execSync(cmd); + } catch { } + }; + logAndRun(`sudo spctl --add '${outputFile}' --label "Vencord Installer"`); + logAndRun(`sudo xattr -d com.apple.quarantine '${outputFile}'`); + } else { + // WHY DOES NODE FETCH RETURN A WEB STREAM OH MY GOD + const body = Readable.fromWeb(res.body); + await finished(body.pipe(createWriteStream(outputFile, { + mode: 0o755, + autoClose: true + }))); } - const newEtag = res.headers.get("etag"); - writeFileSync(ETAG_FILE, newEtag); - - // WHY DOES NODE FETCH RETURN A WEB STREAM OH MY GOD - const body = Readable.fromWeb(res.body); - await finished(body.pipe(createWriteStream(installerFile, { - mode: 0o755, - autoClose: true - }))); - console.log("Finished downloading!"); - return installerFile; + return outputFile; } -console.log("Now running Installer..."); const installerBin = await ensureBinary(); +console.log("Now running Installer..."); + execFileSync(installerBin, { stdio: "inherit", env: { ...process.env, - VENCORD_USER_DATA_DIR: DIST_DIR, + VENCORD_USER_DATA_DIR: BASE_DIR, VENCORD_DEV_INSTALL: "1" } });