diff --git a/desktop/scripts/build-flipper-server-release.tsx b/desktop/scripts/build-flipper-server-release.tsx index 05256d4aa..acb23ab22 100644 --- a/desktop/scripts/build-flipper-server-release.tsx +++ b/desktop/scripts/build-flipper-server-release.tsx @@ -9,6 +9,9 @@ const dotenv = require('dotenv').config(); import path from 'path'; +import https from 'https'; +import os from 'os'; +import tar from 'tar'; import { buildBrowserBundle, buildFolder, @@ -35,6 +38,9 @@ import {need as pkgFetch} from 'pkg-fetch'; // This needs to be tested individually. As of 2022Q2, node17 is not supported. const SUPPORTED_NODE_PLATFORM = 'node16'; +// Node version below is only used for macOS AARCH64 builds as we download +// the binary directly from Node distribution site instead of relying on pkg-fetch. +const NODE_VERSION = 'v16.15.0'; enum BuildPlatform { LINUX = 'linux', @@ -461,6 +467,69 @@ function nodeArchFromBuildPlatform(platform: BuildPlatform): string { return 'x64'; } +/** + * Downloads a file located at the given URL and saves it to the destination path.. + * @param url - URL of the file to download. + * @param dest - Destination path for the downloaded file. + * @returns - A promise that resolves when the file is downloaded. + * If the file can't be downloaded, it rejects with an error. + */ +async function download(url: string, dest: string): Promise { + // First, check if the file already exists and remove it. + try { + await fs.access(dest, fs.constants.F_OK); + await fs.unlink(dest); + } catch (err) {} + + return new Promise((resolve, reject) => { + // Then, download the file and save it to the destination path. + const file: fs.WriteStream = fs.createWriteStream(dest); + https + .get(url, (response) => { + response.pipe(file); + file.on('finish', () => { + file.close(); + console.log(`✅ Download successful ${url}.`); + resolve(); + }); + }) + .on('error', (error: Error) => { + fs.unlink(dest); + reject(error); + }); + }); +} + +/** + * Unpacks a tarball and extracts the contents to a directory. + * @param source - Source tarball. + * @param dest - Destination directory for the extracted contents. + */ +async function unpack(source: string, destination: string) { + console.log(`⚙️ Extracting ${source}.`); + + try { + await fs.access(destination, fs.constants.F_OK); + await fs.rm(destination, {recursive: true, force: true}); + } catch (err) {} + + await fs.mkdir(destination); + + try { + await tar.x({ + file: source, + strip: 1, + cwd: destination, + }); + + console.log(`✅ Extraction completed.`); + } catch (error) { + console.error( + `⚙️ Error found whilst trying to extract '${source}'. Found: ${error}`, + ); + } +} + function nodePlatformFromBuildPlatform(platform: BuildPlatform): string { switch (platform) { case BuildPlatform.LINUX: @@ -476,14 +545,59 @@ function nodePlatformFromBuildPlatform(platform: BuildPlatform): string { } async function installNodeBinary(outputPath: string, platform: BuildPlatform) { - console.log(`⚙️ Downloading node version for ${platform} using pkg-fetch`); - const path = await pkgFetch({ - arch: nodeArchFromBuildPlatform(platform), - platform: nodePlatformFromBuildPlatform(platform), - nodeRange: SUPPORTED_NODE_PLATFORM, - }); - console.log(`⚙️ Copying node binary from ${path} to ${outputPath}`); - await fs.copyFile(path, outputPath); + /** + * Below is a temporary patch that doesn't use pkg-fetch to + * download a node binary for macOS arm64. + * This will be removed once there's a properly + * signed binary for macOS arm64 architecture. + */ + if (platform === BuildPlatform.MAC_AARCH64) { + const temporaryDirectory = os.tmpdir(); + const name = `node-${NODE_VERSION}-darwin-arm64`; + const downloadOutputPath = path.resolve( + temporaryDirectory, + `${name}.tar.gz`, + ); + const unpackedOutputPath = path.resolve(temporaryDirectory, name); + let nodePath = path.resolve(unpackedOutputPath, 'bin', 'node'); + console.log( + `⚙️ Downloading node version for ${platform} using temporary patch.`, + ); + + // Check local cache. + let cached = false; + try { + const cachePath = path.join(homedir(), '.node', name); + await fs.access(cachePath, fs.constants.F_OK); + console.log(`⚙️ Cached artifact found, skip download.`); + nodePath = path.resolve(cachePath, 'bin', 'node'); + cached = true; + } catch (err) {} + if (!cached) { + // Download node tarball from the distribution site. + await download( + `https://nodejs.org/dist/${NODE_VERSION}/${name}.tar.gz`, + downloadOutputPath, + ); + // Finally, unpack the tarball to a local folder i.e. outputPath. + await unpack(downloadOutputPath, unpackedOutputPath); + console.log(`✅ Node successfully downloaded and unpacked.`); + } + + console.log(`⚙️ Copying node binary from ${nodePath} to ${outputPath}`); + await fs.copyFile(nodePath, outputPath); + } else { + console.log(`⚙️ Downloading node version for ${platform} using pkg-fetch`); + const nodePath = await pkgFetch({ + arch: nodeArchFromBuildPlatform(platform), + platform: nodePlatformFromBuildPlatform(platform), + nodeRange: SUPPORTED_NODE_PLATFORM, + }); + + console.log(`⚙️ Copying node binary from ${nodePath} to ${outputPath}`); + await fs.copyFile(nodePath, outputPath); + } + // Set +x on the binary as copyFile doesn't maintain the bit. await fs.chmod(outputPath, 0o755); } diff --git a/desktop/scripts/package.json b/desktop/scripts/package.json index 8bf66ae2e..47f178a84 100644 --- a/desktop/scripts/package.json +++ b/desktop/scripts/package.json @@ -34,6 +34,7 @@ "pkg-fetch": "3.4.1", "promisify-child-process": "^4.1.0", "socket.io": "^4.5.0", + "tar": "6.1.15", "tmp": "^0.2.1", "uuid": "^8.3.2", "yargs": "^17.6.0" diff --git a/desktop/yarn.lock b/desktop/yarn.lock index 2fea644d7..279696b6b 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -11453,6 +11453,11 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -14526,6 +14531,18 @@ tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" +tar@6.1.15: + version "6.1.15" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69" + integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tar@^6.1.10, tar@^6.1.11: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"