-
-
Notifications
You must be signed in to change notification settings - Fork 513
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(installer): manually mount and scan a DMG file when installing f…
…or the .app copy the .app file discovered from the DMG into the users Applications directory
- Loading branch information
1 parent
9a2f36c
commit 7ea5af8
Showing
4 changed files
with
116 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,32 @@ | ||
import opn from 'opn'; | ||
import { spawn } from 'child_process'; | ||
import fs from 'fs-promise'; | ||
import path from 'path'; | ||
|
||
export default async (filePath) => { | ||
await opn(filePath, { wait: false }); | ||
import { getMountedImages, mountImage, unmountImage } from '../../util/hdiutil'; | ||
import moveApp from '../../util/move-app'; | ||
|
||
export default async (filePath, installSpinner) => { | ||
const mounts = await getMountedImages(); | ||
let targetMount = mounts.find(mount => mount.imagePath === filePath); | ||
|
||
if (!targetMount) { | ||
targetMount = await mountImage(filePath); | ||
} | ||
|
||
try { | ||
const volumePath = path.resolve('/Volumes', targetMount.mountPath); | ||
const appName = (await fs.readdir(volumePath)).find(file => file.endsWith('.app')); | ||
if (!appName) { | ||
// eslint-disable-next-line no-throw-literal | ||
throw 'Failed to find .app file in DMG'; | ||
} | ||
const appPath = path.resolve(volumePath, appName); | ||
const targetApplicationPath = `/Applications/${path.basename(appPath)}`; | ||
|
||
await moveApp(appPath, targetApplicationPath, installSpinner, true); | ||
|
||
spawn('open', ['-R', targetApplicationPath], { detached: true }); | ||
} finally { | ||
await unmountImage(targetMount); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { spawnPromise } from 'spawn-rx'; | ||
import debug from 'debug'; | ||
|
||
const d = debug('electron-forge:hdiutil'); | ||
|
||
export const getMountedImages = async () => { | ||
const output = await spawnPromise('hdiutil', ['info']); | ||
const mounts = output.split(/====\n/g); | ||
mounts.shift(); | ||
|
||
const mountObjects = []; | ||
|
||
for (const mount of mounts) { | ||
try { | ||
const mountPath = /\/Volumes\/(.+)\n/g.exec(mount)[1]; | ||
const imagePath = /image-path +: +(.+)\n/g.exec(mount)[1]; | ||
mountObjects.push({ mountPath, imagePath }); | ||
} catch (err) { | ||
// Ignore | ||
} | ||
} | ||
|
||
d('identified active mounts', mountObjects); | ||
return mountObjects; | ||
}; | ||
|
||
export const mountImage = async (filePath) => { | ||
d('mounting image:', filePath); | ||
const output = await spawnPromise('hdiutil', ['attach', '-noautoopen', '-nobrowse', '-noverify', filePath]); | ||
const mountPath = /\/Volumes\/(.+)\n/g.exec(output)[1]; | ||
d('mounted at:', mountPath); | ||
|
||
return { | ||
mountPath, | ||
iamgePath: filePath, | ||
}; | ||
}; | ||
|
||
export const unmountImage = async (mount) => { | ||
d('unmounting current mount:', mount); | ||
await spawnPromise('hdiutil', ['unmount', '-force', `/Volumes/${mount.mountPath}`]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import fs from 'fs-promise'; | ||
import inquirer from 'inquirer'; | ||
import path from 'path'; | ||
import pify from 'pify'; | ||
import sudo from 'sudo-prompt'; | ||
import { exec } from 'child_process'; | ||
|
||
export default async (appPath, targetApplicationPath, spinner, copyInstead = false) => { | ||
let writeAccess = true; | ||
try { | ||
await fs.access('/Applications', fs.W_OK); | ||
} catch (err) { | ||
writeAccess = false; | ||
} | ||
|
||
if (await fs.exists(targetApplicationPath)) { | ||
spinner.stop(); | ||
const { confirm } = await inquirer.createPromptModule()({ | ||
type: 'confirm', | ||
name: 'confirm', | ||
message: `The application "${path.basename(targetApplicationPath)}" appears to already exist in /Applications. Do you want to replace it?`, | ||
}); | ||
if (!confirm) { | ||
// eslint-disable-next-line no-throw-literal | ||
throw 'Installation stopped by user'; | ||
} else { | ||
spinner.start(); | ||
await fs.remove(targetApplicationPath); | ||
} | ||
} | ||
|
||
const moveCommand = `${copyInstead ? 'cp -r' : 'mv'} "${appPath}" "${targetApplicationPath}"`; | ||
if (writeAccess) { | ||
await pify(exec)(moveCommand); | ||
} else { | ||
await pify(sudo.exec)(moveCommand, { | ||
name: 'Electron Forge', | ||
}); | ||
} | ||
}; |