diff --git a/README.md b/README.md index 9510d0e9..1cc8c620 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,34 @@ Refer to the [downloads page](http://etcher.io) for the latest pre-made installe Developing ---------- -You can manually run the application with the following commands: +You can manually run the application with the following steps: + +- Clone the repository. ```sh git clone https://github.com/resin-io/etcher cd etcher -npm install && bower install +``` + +- Install dependencies. + +```sh +npm install +bower install +``` + +- Run the GUI application. + +```sh npm start ``` +- Run the CLI application. + +```sh +node bin/etcher +``` + Take a look at our [contributing guide](https://github.com/resin-io/etcher/blob/master/CONTRIBUTING.md). Support diff --git a/bin/etcher b/bin/etcher new file mode 100755 index 00000000..3656e96b --- /dev/null +++ b/bin/etcher @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../lib/cli/etcher'); diff --git a/lib/cli/cli.js b/lib/cli/cli.js new file mode 100644 index 00000000..677cbf56 --- /dev/null +++ b/lib/cli/cli.js @@ -0,0 +1,88 @@ +/* + * 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 fs = require('fs'); +const yargs = require('yargs'); +const utils = require('./utils'); +const packageJSON = require('../../package.json'); + +/** + * @summary Parsed CLI options and arguments + * @type Object + * @public + */ +module.exports = yargs + .demand(1, 'Missing image') + + // Usage help + .usage('Usage: $0 ') + .epilogue([ + 'If you need help, don\'t hesitate in contacting us at:', + '', + ' GitHub: https://github.com/resin-io/etcher-cli/issues/new', + ' Gitter: https://gitter.im/resin-io/chat' + ].join('\n')) + + // Examples + .example('$0 raspberry-pi.img') + .example('$0 -d /dev/disk2 ubuntu.iso') + .example('$0 -d /dev/disk2 -y rpi.img') + + // Help option + .help() + + // Version option + .version(_.constant(packageJSON.version)) + + // Error reporting + .fail(function(message, error) { + yargs.showHelp(); + utils.printError(error || message); + process.exit(1); + }) + + // Assert that image exists + .check(function(argv) { + fs.accessSync(argv._[0]); + return true; + }) + + .options({ + help: { + describe: 'show help', + boolean: true, + alias: 'h' + }, + version: { + describe: 'show version number', + boolean: true, + alias: 'v' + }, + drive: { + describe: 'drive', + string: true, + alias: 'd' + }, + yes: { + describe: 'confirm non-interactively', + boolean: true, + alias: 'y' + } + }) + .argv; diff --git a/lib/cli/etcher.js b/lib/cli/etcher.js new file mode 100644 index 00000000..4b4034e7 --- /dev/null +++ b/lib/cli/etcher.js @@ -0,0 +1,67 @@ +/* + * 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 visuals = require('resin-cli-visuals'); +const form = require('resin-cli-form'); +const writer = require('../src/writer'); +const utils = require('./utils'); +const options = require('./cli'); + +form.run([ + { + message: 'Select drive', + type: 'drive', + name: 'drive' + }, + { + message: 'This will erase the selected drive. Are you sure?', + type: 'confirm', + name: 'yes', + default: false + } +], { + override: { + drive: options.drive, + + // If `options.yes` is `false`, pass `undefined`, + // otherwise the question will not be asked because + // `false` is a defined value. + yes: options.yes || undefined + + } +}).then(function(answers) { + if (!answers.yes) { + throw new Error('Aborted'); + } + + var progressBar = new visuals.Progress('Burning'); + + return writer.writeImage(options._[0], { + device: answers.drive + }, { + unmountOnSuccess: false, + validateWriteOnSuccess: false + }, function(state) { + return progressBar.update(state); + }); +}).then(function() { + console.log('Your flash is complete!'); +}).catch(function(error) { + utils.printError(error); + process.exit(1); +}); diff --git a/lib/cli/utils.js b/lib/cli/utils.js new file mode 100644 index 00000000..87efecb1 --- /dev/null +++ b/lib/cli/utils.js @@ -0,0 +1,39 @@ +/* + * 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 errors = require('resin-cli-errors'); +const chalk = require('chalk'); + +/** + * @summary Print an error to stderr + * @function + * @public + * + * @param {(Error|String)} error - error + * + * @example + * utils.printError(new Error('Oops!')); + */ +exports.printError = function(error) { + if (_.isString(error)) { + error = new Error(error); + } + + console.error(chalk.red(errors.interpret(error))); +}; diff --git a/package.json b/package.json index 5164c6fb..0c4090ad 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "screenshot.png", "tests", "etcher-release", - "lib/scss" + "lib/scss", + "lib/cli", + "bin/etcher" ], "builder": { "win": { @@ -52,6 +54,7 @@ "angular-ui-router": "^0.2.18", "bluebird": "^3.0.5", "bootstrap-sass": "^3.3.5", + "chalk": "^1.1.3", "drivelist": "^2.0.13", "flexboxgrid": "^6.3.0", "is-elevated": "^1.0.0", @@ -59,11 +62,15 @@ "ngstorage": "^0.3.10", "open": "0.0.5", "resin-image-write": "^3.0.4", + "resin-cli-errors": "^1.2.0", + "resin-cli-form": "^1.4.1", + "resin-cli-visuals": "^1.2.8", "resin-zip-image": "^1.1.2", "sudo-prompt": "^2.2.0", "trackjs": "^2.1.16", "umount": "^1.1.1", - "username": "^2.1.0" + "username": "^2.1.0", + "yargs": "^4.6.0" }, "devDependencies": { "angular-mocks": "^1.4.7",