From 3d70d78b408ca7dbab720e7e1f55ac4d08756a1c Mon Sep 17 00:00:00 2001 From: Perfect Makanju Date: Wed, 18 Nov 2020 14:59:05 +0100 Subject: [PATCH] Bundle Fuse Installer with Release --- .github/workflows/release.yml | 5 +- package.json | 1 + public/electron-app.js | 2 + public/electron/fuse/askpass.osascript.js | 22 ++++++ public/electron/fuse/index.js | 85 +++++++++++++++++++++++ scripts/download-fuse-pkg.js | 69 ++++++++++++++++++ 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100755 public/electron/fuse/askpass.osascript.js create mode 100644 public/electron/fuse/index.js create mode 100644 scripts/download-fuse-pkg.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8083e542..34aa6916 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,6 +38,9 @@ jobs: run: yarn install --network-timeout 1000000 - name: Get daemon run: yarn download-daemon + - name: Get daemon + if: matrix.os == 'macos-latest' + run: yarn download-fuse - name: Linux app pack and release if: matrix.os == 'ubuntu-latest' run: yarn electron-pack --linux @@ -50,7 +53,7 @@ jobs: if: matrix.os == 'macos-latest' run: yarn electron-pack --mac - name: Windows app pack and release - # env: + # env: # CSC_LINK: ${{ secrets.WIN_CSC_LINK }} # CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }} if: matrix.os == 'windows-latest' diff --git a/package.json b/package.json index 4df4ec1d..6b478b57 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "build": "node scripts/build.js", "test": "node scripts/test.js", "download-daemon": "rimraf resources && node scripts/download-daemon.js", + "download-fuse": "rimraf resources/FuseInstaller && node scripts/download-fuse-pkg.js", "lint": "eslint ./ --ext .js --ext .jsx", "electron:dev": "concurrently \"cross-env BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"", "electron-pack": "electron-builder -c.extraMetadata.main=build/electron-app.js", diff --git a/public/electron-app.js b/public/electron-app.js index b5db8d8c..4e6184d6 100644 --- a/public/electron-app.js +++ b/public/electron-app.js @@ -7,6 +7,7 @@ const { app, Tray } = require('electron'); const isDev = require('electron-is-dev'); const DaemonProcess = require('./electron/daemon'); +const FuseInstallerProcess = require('./electron/fuse'); const registerEvents = require('./electron/events'); const createMainWindow = require('./electron/window/main'); const { getMenuOptions, trayIcon } = require('./electron/tray-menu'); @@ -17,6 +18,7 @@ let goTo = null; global.destroyStream = () => {}; const daemon = new DaemonProcess(); +const fuseInstaller = new FuseInstallerProcess(); const enableDevDaemon = process.env.DEV_DAEMON === 'true'; diff --git a/public/electron/fuse/askpass.osascript.js b/public/electron/fuse/askpass.osascript.js new file mode 100755 index 00000000..0d465175 --- /dev/null +++ b/public/electron/fuse/askpass.osascript.js @@ -0,0 +1,22 @@ +#!/usr/bin/env osascript -l JavaScript + +/* eslint-disable */ + +ObjC.import('stdlib') + +const app = Application.currentApplication() +app.includeStandardAdditions = true + +const result = app.displayDialog('Space wants to install OSX FUSE and needs privilege access. Enter your password to allow this.', { + defaultAnswer: '', + withIcon: 'note', + buttons: ['Cancel', 'Install OSX FUSE'], + defaultButton: 'Install OSX FUSE', + hiddenAnswer: true, +}) + +if (result.buttonReturned === 'Install OSX FUSE') { + result.textReturned +} else { + $.exit(255) +} diff --git a/public/electron/fuse/index.js b/public/electron/fuse/index.js new file mode 100644 index 00000000..61c9f1dd --- /dev/null +++ b/public/electron/fuse/index.js @@ -0,0 +1,85 @@ +const path = require('path'); +const chalk = require('chalk'); +const get = require('lodash/get'); +const { spawn } = require('child_process'); + +const installerName = 'Space FUSE Installer'; + +class FuseInstallerProcess { + constructor() { + this.childProcess = null; + this.handlers = { + ready: [], + failed: [], + pending: [], + }; + } + + on(key, handler) { + const accHandlers = get(this.handlers, key, []) || []; + + this.handlers = { + ...this.handlers, + [key]: [ + ...accHandlers, + handler, + ], + }; + } + + callHandlers(key, args) { + this.handlers[key].forEach((handler) => handler(args)); + } + + start(isDev) { + if (this.childProcess) return; + + if (process.platform !== 'darwin') { + return; + } + + let installerPath = path.join(process.resourcesPath, 'FuseInstaller.pkg'); + if (isDev) { + installerPath = path.join(__dirname, '../../../resources/FuseInstaller.pkg'); + } + + this.childProcess = spawn('sudo', ['-A', 'installer', '-pkg', installerPath, '-target', '/'], { + env: { + PATH: process.env.PATH, + SUDO_ASKPASS: path.join(__dirname, 'askpass.osascript.js'), + }, + }); + this.childProcess.stdout.on('data', (data) => { + const outputLog = data.toString().toLowerCase(); + + // eslint-disable-next-line no-console + console.log(chalk.blue(outputLog)); + + if (outputLog.includes('The install was successful') || outputLog.includes('The upgrade was successful')) { + this.callHandlers('success'); + } + }); + + this.childProcess.stderr.on('data', (data) => { + // eslint-disable-next-line no-console + console.error(chalk.red(data)); + this.callHandlers('error'); + }); + + this.childProcess.on('close', () => { + console.log(chalk.blue(`${installerName} finished`)); + this.stop(); + }); + + this.callHandlers('pending'); + } + + stop() { + if (this.childProcess) { + this.childProcess.kill(); + this.childProcess = null; + } + } +} + +module.exports = FuseInstallerProcess; diff --git a/scripts/download-fuse-pkg.js b/scripts/download-fuse-pkg.js new file mode 100644 index 00000000..005b13c2 --- /dev/null +++ b/scripts/download-fuse-pkg.js @@ -0,0 +1,69 @@ +/* eslint-disable no-console */ +const fs = require('fs-extra'); +const path = require('path'); +const axios = require('axios'); +const ProgressBar = require('progress'); + +async function getInstaller() { + const { cwd, platform } = process; + if (platform !== 'darwin') { + return + } + + const resourcesPath = path.resolve(cwd(), 'resources'); + + const fusePkgUrl = `https://space-fuse-asset.s3-us-west-2.amazonaws.com/FUSE+for+macOS+3.11.0.pkg`; + const { data, headers } = await axios({ + method: 'GET', + responseType: 'stream', + url: fusePkgUrl, + }).catch((error) => { + console.error(`\nError when trying to download the package: ${fusePkgUrl}`); + console.error(`Error : ${error.stack || error.message}`); + process.exit(1); + }); + + const totalLength = headers['content-length']; + + console.log(`Downloading Osx Fuse from ${fusePkgUrl}:`); + const progressBar = new ProgressBar(`File: FUSE+for+macOS+3.11.0.pkg [:bar] :percent :etas`, { + width: 40, + complete: '=', + incomplete: ' ', + renderThrottle: 1, + total: parseInt(totalLength, 10), + }); + + if (!fs.existsSync(resourcesPath)) { + fs.mkdirSync(resourcesPath); + } + // save to ./resources/ + const writer = fs.createWriteStream(path.join(resourcesPath, `FuseInstaller.pkg`), { mode: 0o755 }); + + data.on('data', (chunk) => ( + progressBar.tick(chunk.length) + )); + + data.on('error', (error) => { + data.destroy(); + writer.destroy(); + + console.error(`\nError when downloading the Fuse installer binary: ${error.stack || error.message}`); + process.exit(1); + }); + + writer.on('finish', async () => { + process.exit(0); + }); + + writer.on('error', (error) => { + writer.destroy(); + + console.error(`\nError when saving the Fuse installer: ${error.stack || error.message}`); + process.exit(1); + }); + + data.pipe(writer); +} + +getInstaller();