123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- "use strict";
- var _path = _interopRequireDefault(require("path"));
- var _ssh = _interopRequireDefault(require("ssh2"));
- var _pMap = _interopRequireDefault(require("p-map"));
- var _assert = _interopRequireDefault(require("assert"));
- var _sbScandir = _interopRequireDefault(require("sb-scandir"));
- var _shellEscape = _interopRequireDefault(require("shell-escape"));
- var Helpers = _interopRequireWildcard(require("./helpers"));
- function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
- function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
- class SSH {
- constructor() {
- this.connection = null;
- }
- connect(givenConfig) {
- const connection = new _ssh.default();
- this.connection = connection;
- return new Promise(function (resolve) {
- resolve(Helpers.normalizeConfig(givenConfig));
- }).then(config => new Promise((resolve, reject) => {
- connection.on('error', reject);
- if (config.onKeyboardInteractive) {
- connection.on('keyboard-interactive', config.onKeyboardInteractive);
- }
- connection.on('ready', () => {
- connection.removeListener('error', reject);
- resolve(this);
- });
- connection.on('end', () => {
- if (this.connection === connection) {
- this.connection = null;
- }
- });
- connection.on('close', () => {
- if (this.connection === connection) {
- this.connection = null;
- }
- const error = new Error('No response from server'); // $FlowIgnore: Custom attribute
- error.code = 'ETIMEDOUT';
- reject(error);
- });
- connection.connect(config);
- }));
- }
- requestShell() {
- var _this = this;
- return _asyncToGenerator(function* () {
- const connection = _this.connection;
- (0, _assert.default)(connection, 'Not connected to server');
- return new Promise(function (resolve, reject) {
- connection.shell(Helpers.generateCallback(resolve, reject));
- });
- })();
- }
- requestSFTP() {
- var _this2 = this;
- return _asyncToGenerator(function* () {
- const connection = _this2.connection;
- (0, _assert.default)(connection, 'Not connected to server');
- return new Promise(function (resolve, reject) {
- connection.sftp(Helpers.generateCallback(resolve, reject));
- });
- })();
- }
- mkdir(path, type = 'sftp', givenSftp = null) {
- var _this3 = this;
- return _asyncToGenerator(function* () {
- (0, _assert.default)(_this3.connection, 'Not connected to server');
- (0, _assert.default)(type === 'exec' || type === 'sftp', 'Type should either be sftp or exec');
- if (type === 'exec') {
- const output = yield _this3.exec('mkdir', ['-p', path]);
- if (output.stdout) {
- throw new Error(output.stdout);
- }
- } else {
- (0, _assert.default)(!givenSftp || typeof givenSftp === 'object', 'sftp must be an object');
- const sftp = givenSftp || (yield _this3.requestSFTP());
- const makeSftpDirectory = retry => Helpers.mkdirSftp(path, sftp).catch(error => {
- if (retry && error && (error.message === 'No such file' || error.code === 'ENOENT')) {
- return _this3.mkdir(_path.default.dirname(path), 'sftp', sftp).then(() => makeSftpDirectory(false));
- }
- throw error;
- });
- try {
- yield makeSftpDirectory(true);
- } finally {
- if (!givenSftp) {
- sftp.end();
- }
- }
- }
- })();
- }
- exec(command, parameters = [], options = {}) {
- var _this4 = this;
- return _asyncToGenerator(function* () {
- (0, _assert.default)(_this4.connection, 'Not connected to server');
- (0, _assert.default)(typeof options === 'object' && options, 'options must be an Object');
- (0, _assert.default)(!options.cwd || typeof options.cwd === 'string', 'options.cwd must be a string');
- (0, _assert.default)(!options.stdin || typeof options.stdin === 'string', 'options.stdin must be a string');
- (0, _assert.default)(!options.stream || ['stdout', 'stderr', 'both'].indexOf(options.stream) !== -1, 'options.stream must be among "stdout", "stderr" and "both"');
- (0, _assert.default)(!options.options || typeof options.options === 'object', 'options.options must be an object');
- const output = yield _this4.execCommand([command].concat((0, _shellEscape.default)(parameters)).join(' '), options);
- if (!options.stream || options.stream === 'stdout') {
- if (output.stderr) {
- throw new Error(output.stderr);
- }
- return output.stdout;
- }
- if (options.stream === 'stderr') {
- return output.stderr;
- }
- return output;
- })();
- }
- execCommand(givenCommand, options = {}) {
- var _this5 = this;
- return _asyncToGenerator(function* () {
- let command = givenCommand;
- const connection = _this5.connection;
- (0, _assert.default)(connection, 'Not connected to server');
- (0, _assert.default)(typeof options === 'object' && options, 'options must be an Object');
- (0, _assert.default)(!options.cwd || typeof options.cwd === 'string', 'options.cwd must be a string');
- (0, _assert.default)(!options.stdin || typeof options.stdin === 'string', 'options.stdin must be a string');
- (0, _assert.default)(!options.options || typeof options.options === 'object', 'options.options must be an object');
- if (options.cwd) {
- // NOTE: Output piping cd command to hide directory non-existent errors
- command = `cd ${(0, _shellEscape.default)([options.cwd])} 1> /dev/null 2> /dev/null; ${command}`;
- }
- const output = {
- stdout: [],
- stderr: []
- };
- return new Promise(function (resolve, reject) {
- connection.exec(command, options.options || {}, Helpers.generateCallback(function (stream) {
- stream.on('data', function (chunk) {
- if (options.onStdout) options.onStdout(chunk);
- output.stdout.push(chunk);
- });
- stream.stderr.on('data', function (chunk) {
- if (options.onStderr) options.onStderr(chunk);
- output.stderr.push(chunk);
- });
- if (options.stdin) {
- stream.write(options.stdin);
- stream.end();
- }
- stream.on('close', function (code, signal) {
- resolve({
- code,
- signal,
- stdout: output.stdout.join('').trim(),
- stderr: output.stderr.join('').trim()
- });
- });
- }, reject));
- });
- })();
- }
- getFile(localFile, remoteFile, givenSftp = null, givenOpts = null) {
- var _this6 = this;
- return _asyncToGenerator(function* () {
- (0, _assert.default)(_this6.connection, 'Not connected to server');
- (0, _assert.default)(typeof localFile === 'string' && localFile, 'localFile must be a string');
- (0, _assert.default)(typeof remoteFile === 'string' && remoteFile, 'remoteFile must be a string');
- (0, _assert.default)(!givenSftp || typeof givenSftp === 'object', 'sftp must be an object');
- (0, _assert.default)(!givenOpts || typeof givenOpts === 'object', 'opts must be an object');
- const opts = givenOpts || {};
- const sftp = givenSftp || (yield _this6.requestSFTP());
- try {
- yield new Promise(function (resolve, reject) {
- sftp.fastGet(remoteFile, localFile, opts, Helpers.generateCallback(resolve, reject));
- });
- } finally {
- if (!givenSftp) {
- sftp.end();
- }
- }
- })();
- }
- putFile(localFile, remoteFile, givenSftp = null, givenOpts = null) {
- var _this7 = this;
- return _asyncToGenerator(function* () {
- (0, _assert.default)(_this7.connection, 'Not connected to server');
- (0, _assert.default)(typeof localFile === 'string' && localFile, 'localFile must be a string');
- (0, _assert.default)(typeof remoteFile === 'string' && remoteFile, 'remoteFile must be a string');
- (0, _assert.default)(!givenSftp || typeof givenSftp === 'object', 'sftp must be an object');
- (0, _assert.default)(!givenOpts || typeof givenOpts === 'object', 'opts must be an object');
- (0, _assert.default)((yield Helpers.exists(localFile)), `localFile does not exist at ${localFile}`);
- const that = _this7;
- const opts = givenOpts || {};
- const sftp = givenSftp || (yield _this7.requestSFTP());
- function putFile(retry) {
- return new Promise(function (resolve, reject) {
- sftp.fastPut(localFile, remoteFile, opts, Helpers.generateCallback(resolve, function (error) {
- if (error.message === 'No such file' && retry) {
- resolve(that.mkdir(_path.default.dirname(remoteFile), 'sftp', sftp).then(() => putFile(false)));
- } else {
- reject(error);
- }
- }));
- });
- }
- try {
- yield putFile(true);
- } finally {
- if (!givenSftp) {
- sftp.end();
- }
- }
- })();
- }
- putFiles(files, givenConfig = {}) {
- var _this8 = this;
- return _asyncToGenerator(function* () {
- (0, _assert.default)(_this8.connection, 'Not connected to server');
- (0, _assert.default)(Array.isArray(files), 'files must be an array');
- for (let i = 0, length = files.length; i < length; ++i) {
- const file = files[i];
- (0, _assert.default)(file, 'files items must be valid objects');
- (0, _assert.default)(file.local && typeof file.local === 'string', `files[${i}].local must be a string`);
- (0, _assert.default)(file.remote && typeof file.remote === 'string', `files[${i}].remote must be a string`);
- }
- const transferred = [];
- const config = Helpers.normalizePutFilesOptions(givenConfig);
- const sftp = config.sftp || (yield _this8.requestSFTP());
- try {
- yield (0, _pMap.default)(files,
- /*#__PURE__*/
- function () {
- var _ref = _asyncToGenerator(function* (file) {
- yield _this8.putFile(file.local, file.remote, sftp, config.sftpOptions);
- transferred.push(file);
- });
- return function (_x) {
- return _ref.apply(this, arguments);
- };
- }());
- } catch (error) {
- error.transferred = transferred;
- throw error;
- } finally {
- if (!sftp) {
- sftp.end();
- }
- }
- })();
- }
- putDirectory(localDirectory, remoteDirectory, givenConfig = {}) {
- var _this9 = this;
- return _asyncToGenerator(function* () {
- (0, _assert.default)(_this9.connection, 'Not connected to server');
- (0, _assert.default)(typeof localDirectory === 'string' && localDirectory, 'localDirectory must be a string');
- (0, _assert.default)(typeof remoteDirectory === 'string' && remoteDirectory, 'remoteDirectory must be a string');
- (0, _assert.default)((yield Helpers.exists(localDirectory)), `localDirectory does not exist at ${localDirectory}`);
- (0, _assert.default)((yield Helpers.stat(localDirectory)).isDirectory(), `localDirectory is not a directory at ${localDirectory}`);
- (0, _assert.default)(typeof givenConfig === 'object' && givenConfig, 'config must be an object');
- const config = Helpers.normalizePutDirectoryOptions(givenConfig);
- const sftp = config.sftp || (yield _this9.requestSFTP());
- const scanned = yield (0, _sbScandir.default)(localDirectory, config.recursive, config.validate);
- const files = scanned.files.map(i => _path.default.relative(localDirectory, i));
- const directories = scanned.directories.map(i => _path.default.relative(localDirectory, i));
- let failed = false;
- let directoriesQueue = Promise.resolve();
- const directoriesCreated = new Set();
- const createDirectory =
- /*#__PURE__*/
- function () {
- var _ref2 = _asyncToGenerator(function* (path) {
- if (!directoriesCreated.has(path)) {
- directoriesCreated.add(path);
- directoriesQueue = directoriesQueue.then(() => _this9.mkdir(path, 'sftp', sftp));
- yield directoriesQueue;
- }
- });
- return function createDirectory(_x2) {
- return _ref2.apply(this, arguments);
- };
- }();
- try {
- yield (0, _pMap.default)(files,
- /*#__PURE__*/
- function () {
- var _ref3 = _asyncToGenerator(function* (file) {
- const localFile = _path.default.join(localDirectory, file);
- const remoteFile = _path.default.join(remoteDirectory, file).split(_path.default.sep).join('/');
- const remoteFileDirectory = _path.default.dirname(remoteFile);
- yield createDirectory(remoteFileDirectory);
- try {
- yield _this9.putFile(localFile, remoteFile, sftp, config.sftpOptions);
- config.tick(localFile, remoteFile, null);
- } catch (_) {
- failed = true;
- config.tick(localFile, remoteFile, _);
- }
- });
- return function (_x3) {
- return _ref3.apply(this, arguments);
- };
- }(), {
- concurrency: config.concurrency
- });
- yield (0, _pMap.default)(directories,
- /*#__PURE__*/
- function () {
- var _ref4 = _asyncToGenerator(function* (entry) {
- const remoteEntry = _path.default.join(remoteDirectory, entry).split(_path.default.sep).join('/');
- yield createDirectory(remoteEntry);
- });
- return function (_x4) {
- return _ref4.apply(this, arguments);
- };
- }(), {
- concurrency: config.concurrency
- });
- } finally {
- if (!config.sftp) {
- sftp.end();
- }
- }
- return !failed;
- })();
- }
- dispose() {
- if (this.connection) {
- this.connection.end();
- }
- }
- }
- module.exports = SSH;
|