Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bundle Fuse Installer with Release #357

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion public/electron-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -18,6 +19,7 @@ let goTo = getRedirectPath(process.argv);
global.destroyStream = () => {};

const daemon = new DaemonProcess();
const fuseInstaller = new FuseInstallerProcess();

const enableDevDaemon = process.env.DEV_DAEMON === 'true';

Expand Down Expand Up @@ -72,7 +74,7 @@ app.on('open-url', (event, data) => {
event.preventDefault();

goTo = getRedirectPath([data]);

fuseInstaller.start(isDev);
gpuente marked this conversation as resolved.
Show resolved Hide resolved
if (mainWindow) {
const fileUrl = url.format({
hash: goTo,
Expand Down
22 changes: 22 additions & 0 deletions public/electron/fuse/askpass.osascript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env osascript -l JavaScript
gpuente marked this conversation as resolved.
Show resolved Hide resolved

/* 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)
}
101 changes: 101 additions & 0 deletions public/electron/fuse/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const path = require('path');
gpuente marked this conversation as resolved.
Show resolved Hide resolved
const chalk = require('chalk');
const get = require('lodash/get');
const { spawn, exec } = require('child_process');

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) {
if (this.handlers[key]) {
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')) {
// eslint-disable-next-line no-console
console.log(chalk.blue('OsxFuse Kernel Installed'));
this.loadFuseKernel();
}
});

this.childProcess.stderr.on('data', (data) => {
// eslint-disable-next-line no-console
console.error(chalk.red(data));
this.callHandlers('error');
});

this.callHandlers('pending');
}

loadFuseKernel() {
gpuente marked this conversation as resolved.
Show resolved Hide resolved
// eslint-disable-next-line no-console
console.log(chalk.blue('Loading OsxFuse Kernel'));
exec('/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse', (err, stdout) => {
if (err) {
// eslint-disable-next-line no-console
console.error(chalk.red(err.message));
this.callHandlers('error');
return;
}

// eslint-disable-next-line no-console
console.log(chalk.blue(stdout));
// eslint-disable-next-line no-console
console.log(chalk.blue('Loading OsxFuse Kernel was successful'));
this.callHandlers('success');
});
}

stop() {
if (this.childProcess) {
this.childProcess.kill();
this.childProcess = null;
}
}
}

module.exports = FuseInstallerProcess;
69 changes: 69 additions & 0 deletions scripts/download-fuse-pkg.js
Original file line number Diff line number Diff line change
@@ -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();