vs-deploy
Version:
Commands for deploying files of your workspace to a destination.
931 lines • 52.8 kB
JavaScript
"use strict";
/// <reference types="node" />
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
// The MIT License (MIT)
//
// vs-deploy (https://github.com/mkloubert/vs-deploy)
// Copyright (c) Marcel Joachim Kloubert <marcel.kloubert@gmx.net>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
const deploy_contracts = require("../contracts");
const deploy_helpers = require("../helpers");
const deploy_objects = require("../objects");
const deploy_values = require("../values");
const deploy_workspace = require("../workspace");
const FS = require("fs");
const i18 = require("../i18");
const Moment = require("moment");
const Path = require("path");
const SFTP = require('ssh2-sftp-client');
const sshpk = require("sshpk");
const TMP = require("tmp");
const vscode = require("vscode");
const Workflows = require("node-workflows");
const MODE_PAD = '000';
const TOUCH_TIME_FORMAT = 'YYYYMMDDHHmm.ss';
function toHashSafe(hash) {
return deploy_helpers.normalizeString(hash);
}
function toSFTPPath(path) {
return deploy_helpers.replaceAllStrings(path, Path.sep, '/');
}
class SFtpPlugin extends deploy_objects.DeployPluginWithContextBase {
applyExecActionsToWorkflow(eventName, ctx, wf, commands, values) {
let me = this;
let commandsToExecute = deploy_helpers.asArray(commands)
.map((x, i) => {
if ('object' !== typeof x) {
x = {
command: deploy_helpers.toStringSafe(x),
};
}
x = deploy_helpers.cloneObject(x);
x.__index = i;
return x;
})
.filter(x => !deploy_helpers.isEmptyString(x.command));
const ALL_VALUES = deploy_helpers.asArray(values)
.filter(v => v);
if (ctx.executionValues) {
for (let p in ctx.executionValues) {
ALL_VALUES.push(new deploy_values.StaticValue({
name: p,
value: ctx.executionValues[p],
}));
}
}
commandsToExecute.forEach(uc => {
wf.next(() => {
return new Promise((resolve, reject) => {
try {
let cmd = me.context.replaceWithValues(uc.command);
cmd = deploy_values.replaceWithValues(ALL_VALUES, cmd);
const OUTPUT_CHANNEL = me.context.outputChannel();
const VERBOSE = deploy_helpers.toBooleanSafe(uc.verbose);
const WRITE_OUTPUT_TO = deploy_helpers.toStringSafe(uc.writeOutputTo).trim();
let outputEnc = deploy_helpers.normalizeString(uc.outputEncoding);
if ('' === outputEnc) {
outputEnc = 'utf8';
}
let client = ctx.connection.client;
OUTPUT_CHANNEL.appendLine('');
OUTPUT_CHANNEL.appendLine(`[SSH command :: ${deploy_helpers.toStringSafe(eventName)} :: #${uc.__index + 1}] Executing '${deploy_helpers.toStringSafe(cmd)}'...`);
const NO_OUTPUT = deploy_helpers.toBooleanSafe(uc.noOutput, ctx.noCommandOutput);
let execFunc = client['exec'];
let execArgs = [
cmd,
function (err, stream) {
if (err) {
reject(err);
return;
}
let commandResult = Buffer.alloc(0);
let outputFinishedInvoked = false;
const OUTPUT_FINISHED = (outputErr, cmdOutput) => {
if (outputFinishedInvoked) {
return;
}
outputFinishedInvoked = true;
if (outputErr) {
deploy_helpers.log(i18.t('errors.withCategory', 'plugins.sftp.applyExecActionsToWorkflow(1)', outputErr));
}
try {
let valueToWrite = deploy_helpers.toStringSafe(cmdOutput);
if ('' !== WRITE_OUTPUT_TO) {
try {
const EXECUTE_BEFORE_WRITE_OUTPUT_TO = deploy_helpers.toStringSafe(uc.executeBeforeWriteOutputTo);
if ('' !== EXECUTE_BEFORE_WRITE_OUTPUT_TO.trim()) {
// execute JavaScript code
// BEFORE write output as placeholder
const CODE_VALUE = new deploy_values.CodeValue({
name: '',
code: EXECUTE_BEFORE_WRITE_OUTPUT_TO,
type: "code",
});
const CURRENT_OUTPUT_VALUE = new deploy_values.StaticValue({
name: WRITE_OUTPUT_TO,
value: valueToWrite,
});
CODE_VALUE.otherValueProvider = () => {
return ALL_VALUES.concat([CURRENT_OUTPUT_VALUE]);
};
valueToWrite = CODE_VALUE.value;
}
}
catch (e) {
deploy_helpers.log(i18.t('errors.withCategory', 'plugins.sftp.applyExecActionsToWorkflow(2)', e));
}
ctx.executionValues[WRITE_OUTPUT_TO] = valueToWrite;
}
if (VERBOSE) {
OUTPUT_CHANNEL.append(deploy_helpers.toStringSafe(valueToWrite));
}
}
catch (e) {
deploy_helpers.log(i18.t('errors.withCategory', 'plugins.sftp.applyExecActionsToWorkflow(3)', e));
}
finally {
resolve();
}
};
// try get execution result
try {
if (NO_OUTPUT) {
OUTPUT_FINISHED(null); // skip
}
else {
stream.once('error', (streamErr) => {
OUTPUT_FINISHED(streamErr);
});
stream.once('end', () => {
try {
OUTPUT_FINISHED(null, commandResult.toString(outputEnc));
}
catch (e) {
OUTPUT_FINISHED(e);
}
});
stream.on('data', (data) => {
try {
if (!Buffer.isBuffer(data)) {
data = new Buffer(deploy_helpers.toStringSafe(data), outputEnc);
}
if (data.length > 0) {
commandResult = Buffer.concat([commandResult, data]);
}
}
catch (e) {
OUTPUT_FINISHED(e);
}
});
}
}
catch (e) {
OUTPUT_FINISHED(e);
}
},
];
execFunc.apply(client, execArgs);
}
catch (e) {
reject(e);
}
});
});
});
}
get canGetFileInfo() {
return true;
}
get canPull() {
return true;
}
createContext(target, files, opts) {
let me = this;
return new Promise((resolve, reject) => {
let completed = (err, conn) => {
if (err) {
reject(err);
}
else {
let dataTransformer;
if (target.unix) {
if (deploy_helpers.toBooleanSafe(target.unix.convertCRLF)) {
let textEnc = deploy_helpers.normalizeString(target.unix.encoding);
if ('' === textEnc) {
textEnc = 'ascii';
}
dataTransformer = (ctx) => {
return new Promise((resolve2, reject2) => {
let completed2 = deploy_helpers.createSimplePromiseCompletedAction(resolve2, reject2);
deploy_helpers.isBinaryContent(ctx.data).then((isBinary) => {
try {
let newData = ctx.data;
if (!isBinary) {
// seems to be a text file
newData = new Buffer(deploy_helpers.replaceAllStrings(newData.toString(textEnc), "\r\n", "\n"), textEnc);
}
completed2(null, newData);
}
catch (e) {
completed2(e);
}
}).catch((err2) => {
completed2(err2);
});
});
};
}
}
let ctx = {
cachedRemoteDirectories: {},
connection: conn,
dataTransformer: deploy_helpers.toDataTransformerSafe(dataTransformer),
executionValues: {},
hasCancelled: deploy_helpers.isNullOrUndefined(conn),
noCommandOutput: deploy_helpers.toBooleanSafe(target.noCommandOutput, true),
user: user,
};
me.onCancelling(() => ctx.hasCancelled = true, opts);
let connectionEstablishWorkflow = Workflows.create();
let connectionValues = [];
// user
connectionValues.push(new deploy_values.StaticValue({
name: 'user',
value: ctx.user,
}));
let appendTimeValue = (name, timeValue) => {
connectionValues.push(new deploy_values.StaticValue({
name: name + '_iso',
value: Moment(timeValue).toISOString(),
}));
connectionValues.push(new deploy_values.StaticValue({
name: name + '_iso_utc',
value: Moment(timeValue).utc().toISOString(),
}));
connectionValues.push(new deploy_values.StaticValue({
name: name + '_touch',
value: Moment(timeValue).format(TOUCH_TIME_FORMAT),
}));
connectionValues.push(new deploy_values.StaticValue({
name: name + '_touch_utc',
value: Moment(timeValue).utc().format(TOUCH_TIME_FORMAT),
}));
connectionValues.push(new deploy_values.StaticValue({
name: name + '_unix',
value: Moment(timeValue).unix(),
}));
connectionValues.push(new deploy_values.StaticValue({
name: name + '_unix_utc',
value: Moment(timeValue).utc().unix(),
}));
};
connectionEstablishWorkflow.next((cewfCtx) => {
let wrapper = {
context: ctx,
destroy: function () {
return new Promise((resolve2, reject2) => {
delete ctx.cachedRemoteDirectories;
let closingConnectionWorkflow = Workflows.create();
// setup "close" time
connectionEstablishWorkflow.next(() => {
appendTimeValue('close_time', new Date());
});
// commands to execute BEFORE connection is closed
me.applyExecActionsToWorkflow('closing', ctx, closingConnectionWorkflow, target.closing, connectionValues);
closingConnectionWorkflow.next(() => {
if (conn) {
conn.end();
}
});
closingConnectionWorkflow.start().then(() => {
resolve2(conn);
}).catch((e) => {
reject2(e);
});
});
},
};
cewfCtx.result = wrapper;
});
// setup "connection" time
connectionEstablishWorkflow.next(() => {
appendTimeValue('connected_time', new Date());
});
// commands to execute after
// connection has been established
me.applyExecActionsToWorkflow('connected', ctx, connectionEstablishWorkflow, target.connected, connectionValues);
connectionEstablishWorkflow.start().then((wrapper) => {
resolve(wrapper);
}).catch((err) => {
reject(err);
});
}
};
// host & TCP port
let host = deploy_helpers.toStringSafe(target.host, deploy_contracts.DEFAULT_HOST);
let port = parseInt(deploy_helpers.toStringSafe(target.port, '22').trim());
// username and password
let user = deploy_helpers.toStringSafe(target.user);
if ('' === user) {
user = undefined;
}
let pwd = deploy_helpers.toStringSafe(target.password);
if ('' === pwd) {
pwd = undefined;
}
// supported hashes
let hashes = deploy_helpers.asArray(target.hashes)
.map(x => toHashSafe(x))
.filter(x => '' !== x);
hashes = deploy_helpers.distinctArray(hashes);
let hashAlgo = toHashSafe(target.hashAlgorithm);
if ('' === hashAlgo) {
hashAlgo = 'md5';
}
let privateKeyFile = deploy_helpers.toStringSafe(target.privateKey);
privateKeyFile = me.context.replaceWithValues(privateKeyFile);
if ('' !== privateKeyFile.trim()) {
if (!Path.isAbsolute(privateKeyFile)) {
privateKeyFile = Path.join(deploy_workspace.getRootPath(), privateKeyFile);
}
}
let agent = deploy_helpers.toStringSafe(target.agent);
agent = me.context.replaceWithValues(agent);
if ('' === agent.trim()) {
agent = undefined;
}
let agentForward = deploy_helpers.toBooleanSafe(target.agentForward);
let tryKeyboard = deploy_helpers.toBooleanSafe(target.tryKeyboard);
let readyTimeout = parseInt(deploy_helpers.toStringSafe(target.readyTimeout).trim());
if (isNaN(readyTimeout)) {
readyTimeout = undefined;
}
let privateKeyPassphrase = deploy_helpers.toStringSafe(target.privateKeyPassphrase);
if ('' === privateKeyPassphrase) {
privateKeyPassphrase = undefined;
}
try {
let privateKey;
let openConnection = () => {
if (!privateKey) {
if (!user) {
user = 'anonymous';
}
}
let conn = new SFTP();
if (tryKeyboard) {
conn.client.on('keyboard-interactive', (name, instructions, instructionsLang, prompts, finish) => {
try {
finish([pwd]);
}
catch (e) {
deploy_helpers.log(i18.t('errors.withCategory', 'plugins.sftp.keyboard-interactive', e));
}
});
}
conn.connect({
host: host,
port: port,
username: user,
password: pwd,
privateKey: privateKey,
passphrase: privateKeyPassphrase,
hostHash: hashAlgo,
hostVerifier: (hashedKey, cb) => {
hashedKey = toHashSafe(hashedKey);
if (hashes.length < 1) {
return true;
}
return hashes.indexOf(hashedKey) > -1;
},
agent: agent,
agentForward: agentForward,
tryKeyboard: tryKeyboard,
readyTimeout: readyTimeout,
}).then(() => {
completed(null, conn);
}).catch((err) => {
completed(err);
});
};
let setupPrivateKeyIfNeeded = () => {
try {
if (privateKey) {
let privateKeySourceFormat = deploy_helpers.toStringSafe(me.context.replaceWithValues(target.privateKeySourceFormat));
privateKeySourceFormat = privateKeySourceFormat.trim();
if ('' !== privateKeySourceFormat) {
let privateKeyTargetFormat = deploy_helpers.toStringSafe(me.context.replaceWithValues(target.privateKeyTargetFormat));
privateKeyTargetFormat = privateKeyTargetFormat.trim();
if ('' === privateKeyTargetFormat) {
privateKeyTargetFormat = 'ssh';
}
const OPTS = {
'filename': privateKeyFile,
'passphrase': privateKeyPassphrase,
};
privateKey = sshpk.parsePrivateKey(privateKey, privateKeySourceFormat, OPTS)
.toBuffer(privateKeyTargetFormat, OPTS);
}
}
openConnection();
}
catch (e) {
completed(e);
}
};
let askForPasswordIfNeeded = (defaultValueForShowPasswordPrompt, passwordGetter, passwordSetter, cacheKey) => {
let showPasswordPrompt = false;
if (!deploy_helpers.isEmptyString(user) && deploy_helpers.isNullOrUndefined(passwordGetter())) {
// user defined, but no password
let pwdFromCache = deploy_helpers.toStringSafe(me.context.targetCache().get(target, cacheKey));
if ('' === pwdFromCache) {
// nothing in cache
showPasswordPrompt = deploy_helpers.toBooleanSafe(target.promptForPassword, defaultValueForShowPasswordPrompt);
}
else {
passwordSetter(pwdFromCache);
}
}
if (showPasswordPrompt) {
vscode.window.showInputBox({
ignoreFocusOut: true,
placeHolder: i18.t('prompts.inputPassword'),
password: true,
}).then((passwordFromUser) => {
if ('undefined' === typeof passwordFromUser) {
completed(null, null); // cancelled
}
else {
passwordSetter(passwordFromUser);
me.context.targetCache().set(target, cacheKey, passwordFromUser);
setupPrivateKeyIfNeeded();
}
}, (err) => {
completed(err);
});
}
else {
setupPrivateKeyIfNeeded();
}
};
if (deploy_helpers.isNullUndefinedOrEmptyString(privateKeyFile)) {
askForPasswordIfNeeded(true, () => pwd, (pwdToSet) => pwd = pwdToSet, 'password');
}
else {
// try read private key
FS.readFile(privateKeyFile, (err, data) => {
if (err) {
completed(err);
return;
}
privateKey = data;
askForPasswordIfNeeded(false, () => privateKeyPassphrase, (pwdToSet) => privateKeyPassphrase = pwdToSet, 'privateKeyPassphrase');
});
}
}
catch (e) {
completed(e); // global error
}
});
}
deployFileWithContext(ctx, file, target, opts) {
let me = this;
let completed = (err) => {
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: ctx.hasCancelled,
error: err,
file: file,
target: target,
});
}
};
if (ctx.hasCancelled) {
completed(); // cancellation requested
}
else {
let relativeFilePath = deploy_helpers.toRelativeTargetPathWithValues(file, target, me.context.values(), opts.baseDirectory);
if (false === relativeFilePath) {
completed(new Error(i18.t('relativePaths.couldNotResolve', file)));
return;
}
let dir = me.getDirFromTarget(target);
let targetFile = toSFTPPath(Path.join(dir, relativeFilePath));
let targetDirectory = toSFTPPath(Path.dirname(targetFile));
let getModeValue = (pathVal) => {
let mode;
if (!deploy_helpers.isNullOrUndefined(target.modes)) {
let asOctalNumber = (val) => {
if (deploy_helpers.isNullUndefinedOrEmptyString(val)) {
return;
}
return parseInt(deploy_helpers.toStringSafe(val).trim(), 8);
};
if ('object' === typeof target.modes) {
for (let p in target.modes) {
let r = new RegExp(p);
if (r.test(deploy_helpers.toStringSafe(pathVal))) {
mode = asOctalNumber(target.modes[p]);
}
}
}
else {
// handle as string or number
mode = asOctalNumber(target.modes);
}
}
if (deploy_helpers.isNullUndefinedOrEmptyString(mode)) {
mode = undefined;
}
return mode;
};
let putOpts = {};
putOpts['mode'] = getModeValue(targetFile);
if (deploy_helpers.toBooleanSafe(target.updateModesOfDirectories)) {
putOpts['dirMode'] = getModeValue(targetDirectory);
}
// upload the file
let uploadFile = (initDirCache) => {
if (ctx.hasCancelled) {
completed(); // cancellation requested
return;
}
if (deploy_helpers.toBooleanSafe(initDirCache)) {
ctx.cachedRemoteDirectories[targetDirectory] = [];
}
FS.readFile(file, (err, untransformedJsonData) => {
if (err) {
completed(err);
return;
}
try {
let subCtx = {
file: file,
remoteFile: relativeFilePath,
sftp: ctx,
};
let dtCtx = me.createDataTransformerContext(target, deploy_contracts.DataTransformerMode.Transform, subCtx);
dtCtx.data = untransformedJsonData;
let dtResult = Promise.resolve(ctx.dataTransformer(dtCtx));
dtResult.then((transformedData) => {
try {
let subCtx2 = {
file: file,
remoteFile: relativeFilePath,
sftp: ctx,
};
let tCtx = me.createDataTransformerContext(target, deploy_contracts.DataTransformerMode.Transform, subCtx2);
tCtx.data = transformedData;
let tResult = me.loadDataTransformer(target, deploy_contracts.DataTransformerMode.Transform)(tCtx);
Promise.resolve(tResult).then((dataToUpload) => {
let putWorkflow = Workflows.create();
let putValues = [];
// get information of the local file
putWorkflow.next((wfCtx) => {
return new Promise((resolve, reject) => {
FS.lstat(file, (err, stats) => {
if (err) {
reject(err);
}
else {
let ftu = {
localPath: file,
stats: stats,
values: putValues,
};
wfCtx.value = ftu;
resolve();
}
});
});
});
// "time" values
putWorkflow.next((wfCtx) => {
let ftu = wfCtx.value;
let timeProperties = ['ctime', 'atime', 'mtime', 'birthtime'];
timeProperties.forEach(tp => {
let timeValue = ftu.stats[tp];
if (!timeValue) {
return;
}
ftu.values.push(new deploy_values.StaticValue({
name: tp + '_iso',
value: Moment(timeValue).toISOString(),
}));
ftu.values.push(new deploy_values.StaticValue({
name: tp + '_iso_utc',
value: Moment(timeValue).utc().toISOString(),
}));
ftu.values.push(new deploy_values.StaticValue({
name: tp + '_touch',
value: Moment(timeValue).format(TOUCH_TIME_FORMAT),
}));
ftu.values.push(new deploy_values.StaticValue({
name: tp + '_touch_utc',
value: Moment(timeValue).utc().format(TOUCH_TIME_FORMAT),
}));
ftu.values.push(new deploy_values.StaticValue({
name: tp + '_unix',
value: Moment(timeValue).unix(),
}));
ftu.values.push(new deploy_values.StaticValue({
name: tp + '_unix_utc',
value: Moment(timeValue).utc().unix(),
}));
});
// GID & UID
ftu.values.push(new deploy_values.StaticValue({
name: 'gid',
value: ftu.stats.gid,
}));
ftu.values.push(new deploy_values.StaticValue({
name: 'uid',
value: ftu.stats.uid,
}));
// file & directory
ftu.values.push(new deploy_values.StaticValue({
name: 'remote_file',
value: targetFile,
}));
ftu.values.push(new deploy_values.StaticValue({
name: 'remote_dir',
value: targetDirectory,
}));
ftu.values.push(new deploy_values.StaticValue({
name: 'remote_name',
value: Path.basename(targetFile),
}));
let modeFull = ftu.stats.mode.toString(8);
let modeDec = ftu.stats.mode.toString();
let modeSmall = modeFull;
modeSmall = MODE_PAD.substring(0, MODE_PAD.length - modeSmall.length) + modeSmall;
if (modeSmall.length >= 3) {
modeSmall = modeSmall.substr(-3, 3);
}
// mode
ftu.values.push(new deploy_values.StaticValue({
name: 'mode',
value: modeSmall,
}));
// mode_full
ftu.values.push(new deploy_values.StaticValue({
name: 'mode_full',
value: modeFull,
}));
// mode_decimal
ftu.values.push(new deploy_values.StaticValue({
name: 'mode_decimal',
value: modeDec,
}));
// user
ftu.values.push(new deploy_values.StaticValue({
name: 'user',
value: ctx.user,
}));
});
let applyExecActions = (eventName, commands) => {
me.applyExecActionsToWorkflow(eventName, ctx, putWorkflow, commands, putValues);
};
// commands to execute BEFORE the upload
applyExecActions('beforeUpload', target.beforeUpload);
// upload
putWorkflow.next(() => {
return new Promise((resolve, reject) => {
ctx.connection.put(dataToUpload, targetFile).then(() => {
if (deploy_helpers.isNullOrUndefined(putOpts['mode'])) {
resolve();
}
else {
ctx.connection.sftp.chmod(targetFile, putOpts['mode'], (err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
}
}).catch((e) => {
reject(e);
});
});
});
// commands to execute AFTER the upload
applyExecActions('uploaded', target.uploaded);
putWorkflow.start().then(() => {
completed();
}).catch((e) => {
completed(e);
});
}).catch((e) => {
completed(e);
});
}
catch (e) {
completed(e);
}
}).catch((err) => {
completed(err);
});
}
catch (e) {
completed(e);
}
});
};
if (opts.onBeforeDeploy) {
opts.onBeforeDeploy(me, {
destination: targetDirectory,
file: file,
target: target,
});
}
let changeModForDirectory = (initDirCache) => {
if (ctx.hasCancelled) {
completed(); // cancellation requested
return;
}
if (deploy_helpers.isNullUndefinedOrEmptyString(putOpts['dirMode'])) {
uploadFile(initDirCache);
}
else {
ctx.connection.sftp.chmod(targetDirectory, putOpts['dirMode'], (err) => {
if (err) {
completed(err);
}
else {
uploadFile(initDirCache);
}
});
}
};
if (deploy_helpers.isNullOrUndefined(ctx.cachedRemoteDirectories[targetDirectory])) {
// first check if target directory exists
ctx.connection.list(targetDirectory).then(() => {
changeModForDirectory(true);
}).catch((err) => {
// no => try to create
if (ctx.hasCancelled) {
completed(); // cancellation requested
return;
}
ctx.connection.mkdir(targetDirectory, true).then(() => {
changeModForDirectory(true);
}).catch((err) => {
completed(err);
});
});
}
else {
changeModForDirectory();
}
}
}
downloadFileWithContext(ctx, file, target, opts) {
let me = this;
return new Promise((resolve, reject) => {
let completedInvoked = false;
let completed = (err, data) => {
if (completedInvoked) {
return;
}
completedInvoked = true;
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: ctx.hasCancelled,
error: err,
file: file,
target: target,
});
}
if (err) {
reject(err);
}
else {
resolve(data);
}
};
if (ctx.hasCancelled) {
completed(null); // cancellation requested
}
else {
let relativeFilePath = deploy_helpers.toRelativeTargetPathWithValues(file, target, me.context.values(), opts.baseDirectory);
if (false === relativeFilePath) {
completed(new Error(i18.t('relativePaths.couldNotResolve', file)));
return;
}
let dir = me.getDirFromTarget(target);
let targetFile = toSFTPPath(Path.join(dir, relativeFilePath));
let targetDirectory = toSFTPPath(Path.dirname(targetFile));
if (opts.onBeforeDeploy) {
opts.onBeforeDeploy(me, {
destination: targetDirectory,
file: file,
target: target,
});
}
ctx.connection.get(targetFile).then((data) => {
if (data) {
try {
data.once('error', (err) => {
;
completed(err);
});
TMP.tmpName({
keep: true,
}, (err, tmpFile) => {
if (err) {
completed(err);
}
else {
let deleteTempFile = (err, data) => {
// delete temp file ...
FS.exists(tmpFile, (exists) => {
if (exists) {
// ... if exist
FS.unlink(tmpFile, () => {
completed(err, data);
});
}
else {
completed(err, data);
}
});
};
let downloadCompleted = (err) => {
if (err) {
deleteTempFile(err);
}
else {
FS.readFile(tmpFile, (err, transformedData) => {
if (err) {
deleteTempFile(err);
}
else {
try {
let subCtx = {
file: file,
remoteFile: relativeFilePath,
};
let tCtx = me.createDataTransformerContext(target, deploy_contracts.DataTransformerMode.Restore, subCtx);
tCtx.data = transformedData;
let tResult = me.loadDataTransformer(target, deploy_contracts.DataTransformerMode.Restore)(tCtx);
Promise.resolve(tResult).then((untransformedJsonData) => {
deleteTempFile(null, untransformedJsonData);
}).catch((e) => {
deleteTempFile(e);
});
}
catch (e) {
deleteTempFile(e);
}
}
});
}
};
try {
// copy to temp file
let pipe = data.pipe(FS.createWriteStream(tmpFile));
pipe.once('error', (err) => {
;
downloadCompleted(err);
});
data.once('end', () => {
downloadCompleted(null);
});
}
catch (e) {
downloadCompleted(e);
}
}
});
}
catch (e) {
completed(e);
}
}
else {
completed(new Error("No data!")); //TODO
}
}).catch((err) => {
completed(err);
});
}
});
}
getDirFromTarget(target) {
let dir = this.context.replaceWithValues(target.dir);
if (deploy_helpers.isEmptyString(dir)) {
dir = '/';
}
return dir;
}
getFileInfoWithContext(ctx, file, target, opts) {
return __awaiter(this, void 0, void 0, function* () {
let me = this;
let relativeFilePath = deploy_helpers.toRelativeTargetPathWithValues(file, target, me.contex