mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 15:27:17 +00:00
refactor(CLI): move UNIX umount functionality to repository (#866)
- Move UNIX unmount functionality to the CLI module - Run `electron-mocha` for the CLI code - Make unmount command templates that take a `drivelist` object as input Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
parent
f80feb9edf
commit
4e3bdb7e22
105
lib/cli/unmount.js
Normal file
105
lib/cli/unmount.js
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2016 resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const Bluebird = require('bluebird');
|
||||
const childProcess = Bluebird.promisifyAll(require('child_process'));
|
||||
const os = require('os');
|
||||
|
||||
/**
|
||||
* @summary Unmount command templates
|
||||
* @namespace COMMAND_TEMPLATES
|
||||
* @private
|
||||
*
|
||||
* We make sure that the commands declared here exit
|
||||
* successfully even if the drive is not mounted.
|
||||
*/
|
||||
const COMMAND_TEMPLATES = {
|
||||
|
||||
/**
|
||||
* @property {String} darwin
|
||||
* @memberof COMMAND_TEMPLATES
|
||||
*/
|
||||
darwin: '/usr/sbin/diskutil unmountDisk force <%= device %>',
|
||||
|
||||
/**
|
||||
* @property {String} linux
|
||||
* @memberof COMMAND_TEMPLATES
|
||||
*
|
||||
* @description
|
||||
* If trying to unmount the raw device in Linux, we get:
|
||||
* > umount: /dev/sdN: not mounted
|
||||
* Therefore we use the ?* glob to make sure umount processes
|
||||
* the partitions of sdN independently (even if they contain multiple digits)
|
||||
* but not the raw device.
|
||||
* We also redirect stderr to /dev/null to ignore warnings
|
||||
* if a device is already unmounted.
|
||||
* Finally, we also wrap the command in a boolean expression
|
||||
* that always evaluates to true to ignore the return code,
|
||||
* which is non zero when a device was already unmounted.
|
||||
*/
|
||||
linux: 'umount <%= device %>?* 2>/dev/null || /bin/true'
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Get UNIX unmount command
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {String} operatingSystem - operating system slug
|
||||
* @param {Object} drive - drive object
|
||||
* @returns {String} command
|
||||
*
|
||||
* @example
|
||||
* const drivelist = require('drivelist');
|
||||
* const os = require('os');
|
||||
*
|
||||
* drivelist.list((drives) => {
|
||||
* const command = unmount.getUNIXUnmountCommand(os.platform(), drives[0]);
|
||||
* });
|
||||
*/
|
||||
exports.getUNIXUnmountCommand = (operatingSystem, drive) => {
|
||||
return _.template(COMMAND_TEMPLATES[operatingSystem])(drive);
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Unmount drive
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {Object} drive - drive object
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @example
|
||||
* const Bluebird = require('bluebird');
|
||||
* const drivelist = Bluebird.promisifyAll(require('drivelist'));
|
||||
*
|
||||
* drivelist.listAsync().each(unmount.unmountDrive);
|
||||
*/
|
||||
exports.unmountDrive = (drive) => {
|
||||
const platform = os.platform();
|
||||
|
||||
if (platform === 'win32') {
|
||||
const removedrive = Bluebird.promisifyAll(require('removedrive'));
|
||||
return removedrive.ejectAsync(drive.mountpoint);
|
||||
}
|
||||
|
||||
const command = exports.getUNIXUnmountCommand(platform, drive);
|
||||
return childProcess.execAsync(command);
|
||||
};
|
@ -20,9 +20,8 @@ const imageWrite = require('etcher-image-write');
|
||||
const imageStream = require('etcher-image-stream');
|
||||
const Bluebird = require('bluebird');
|
||||
const fs = Bluebird.promisifyAll(require('fs'));
|
||||
const umount = Bluebird.promisifyAll(require('umount'));
|
||||
const os = require('os');
|
||||
const isWindows = os.platform() === 'win32';
|
||||
const unmount = require('./unmount');
|
||||
|
||||
/**
|
||||
* @summary Write an image to a disk drive
|
||||
@ -56,7 +55,15 @@ const isWindows = os.platform() === 'win32';
|
||||
* });
|
||||
*/
|
||||
exports.writeImage = (imagePath, drive, options, onProgress) => {
|
||||
return umount.umountAsync(drive.device).then(() => {
|
||||
return Bluebird.try(() => {
|
||||
|
||||
// Unmounting a drive in Windows means we can't write to it anymore
|
||||
if (os.platform() === 'win32') {
|
||||
return;
|
||||
}
|
||||
|
||||
return unmount.unmountDrive(drive);
|
||||
}).then(() => {
|
||||
return Bluebird.props({
|
||||
image: imageStream.getFromFilePath(imagePath),
|
||||
driveFileDescriptor: fs.openAsync(drive.raw, 'rs+')
|
||||
@ -83,14 +90,6 @@ exports.writeImage = (imagePath, drive, options, onProgress) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWindows && drive.mountpoint) {
|
||||
|
||||
// The `can-ignore` annotation is EncloseJS (http://enclosejs.com) specific.
|
||||
const removedrive = Bluebird.promisifyAll(require('removedrive', 'can-ignore'));
|
||||
|
||||
return removedrive.ejectAsync(drive.mountpoint);
|
||||
}
|
||||
|
||||
return umount.umountAsync(drive.device);
|
||||
return unmount.unmountDrive(drive);
|
||||
});
|
||||
};
|
||||
|
12
npm-shrinkwrap.json
generated
12
npm-shrinkwrap.json
generated
@ -5146,18 +5146,6 @@
|
||||
"from": "umd@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz"
|
||||
},
|
||||
"umount": {
|
||||
"version": "1.1.5",
|
||||
"from": "umount@1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/umount/-/umount-1.1.5.tgz",
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "3.10.1",
|
||||
"from": "lodash@>=3.7.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unbzip2-stream": {
|
||||
"version": "1.0.10",
|
||||
"from": "unbzip2-stream@>=1.0.10 <2.0.0",
|
||||
|
@ -11,7 +11,7 @@
|
||||
"url": "git@github.com:resin-io/etcher.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run lint && electron-mocha --recursive --renderer tests/gui -R min",
|
||||
"test": "npm run lint && electron-mocha --recursive --renderer tests/gui -R min && electron-mocha --recursive tests/cli -R min",
|
||||
"sass": "node-sass ./lib/gui/scss/main.scss > build/css/main.css",
|
||||
"jslint": "eslint lib tests scripts bin versionist.conf.js",
|
||||
"scsslint": "scss-lint lib/gui/scss",
|
||||
@ -88,7 +88,6 @@
|
||||
"sudo-prompt": "^6.1.0",
|
||||
"tail": "^1.1.0",
|
||||
"trackjs": "^2.1.16",
|
||||
"umount": "^1.1.5",
|
||||
"username": "^2.1.0",
|
||||
"yargs": "^4.6.0"
|
||||
},
|
||||
|
64
tests/cli/unmount.spec.js
Normal file
64
tests/cli/unmount.spec.js
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2016 resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const m = require('mochainon');
|
||||
const unmount = require('../../lib/cli/unmount');
|
||||
|
||||
describe('CLI: Unmount', function() {
|
||||
|
||||
describe('.getUNIXUnmountCommand()', function() {
|
||||
|
||||
it('should return the correct command for OS X', function() {
|
||||
const command = unmount.getUNIXUnmountCommand('darwin', {
|
||||
device: '/dev/disk2',
|
||||
description: 'DataTraveler 2.0',
|
||||
size: 7823458304,
|
||||
mountpoints: [
|
||||
{
|
||||
path: '/Volumes/UNTITLED'
|
||||
}
|
||||
],
|
||||
raw: '/dev/rdisk2',
|
||||
protected: false,
|
||||
system: false
|
||||
});
|
||||
|
||||
m.chai.expect(command).to.equal('/usr/sbin/diskutil unmountDisk force /dev/disk2');
|
||||
});
|
||||
|
||||
it('should return the correct command for GNU/Linux', function() {
|
||||
const command = unmount.getUNIXUnmountCommand('linux', {
|
||||
device: '/dev/sda',
|
||||
description: 'DataTraveler 2.0',
|
||||
size: 7823458304,
|
||||
mountpoints: [
|
||||
{
|
||||
path: '/media/UNTITLED'
|
||||
}
|
||||
],
|
||||
raw: '/dev/sda',
|
||||
protected: false,
|
||||
system: false
|
||||
});
|
||||
|
||||
m.chai.expect(command).to.equal('umount /dev/sda?* 2>/dev/null || /bin/true');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user