vs-deploy
Version:
Commands for deploying files of your workspace to a destination.
1,310 lines (1,308 loc) • 73.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_globals = require("./globals");
const deploy_helpers = require("./helpers");
const FS = require("fs");
const i18 = require("./i18");
const Moment = require("moment");
const Path = require("path");
const vscode = require("vscode");
const Workflows = require("node-workflows");
const Zip = require('node-zip');
/**
* A basic deploy plugin that is specially based on single
* file operations (s. deployFile() method).
*/
class DeployPluginBase {
/**
* Initializes a new instance of that class.
*
* @param {deploy_contracts.DeployContext} [ctx] The underlying deploy context.
*/
constructor(ctx) {
this._context = ctx;
deploy_globals.EVENTS.on(deploy_contracts.EVENT_CONFIG_RELOADED, this.onConfigReloaded);
}
/** @inheritdoc */
get canGetFileInfo() {
return false;
}
/** @inheritdoc */
get canPull() {
return false;
}
/** @inheritdoc */
compareFiles(file, target, opts) {
return __awaiter(this, void 0, void 0, function* () {
let me = this;
if (!opts) {
opts = {};
}
let wf = Workflows.create();
// get info about REMOTE file
wf.next((ctx) => __awaiter(this, void 0, void 0, function* () {
return yield me.getFileInfo(file, target, opts);
}));
// check if local file exists
wf.next((ctx) => {
let right = ctx.previousValue;
return new Promise((resolve, reject) => {
try {
let left = {
exists: undefined,
isRemote: false,
};
FS.exists(file, (exists) => {
left.exists = exists;
if (!left.exists) {
ctx.finish(); // no need to get local file info
}
let result = {
left: left,
right: right,
};
ctx.result = result;
resolve(result);
});
}
catch (e) {
reject(e);
}
});
});
// get local file info
wf.next((ctx) => {
let result = ctx.previousValue;
return new Promise((resolve, reject) => {
FS.lstat(file, (err, stat) => {
if (err) {
reject(err);
}
else {
try {
result.left.name = Path.basename(file);
result.left.path = Path.dirname(file);
result.left.modifyTime = Moment(stat.ctime);
result.left.size = stat.size;
resolve(result);
}
catch (e) {
reject(e);
}
}
});
});
});
return yield wf.start();
});
}
/** @inheritdoc */
compareWorkspace(files, target, opts) {
return __awaiter(this, void 0, void 0, function* () {
let me = this;
let wf = Workflows.create();
wf.next((ctx) => {
ctx.result = [];
});
files.forEach(f => {
wf.next((ctx) => __awaiter(this, void 0, void 0, function* () {
let compareResult = yield me.compareFiles(f, target, opts);
ctx.result.push(compareResult);
return compareResult;
}));
});
return yield wf.start();
});
}
/**
* Gets the underlying deploy context.
*/
get context() {
return this._context;
}
/**
* Creates a basic data transformer context.
*
* @param {deploy_contracts.TransformableDeployTarget} target The target.
* @param {deploy_contracts.DataTransformerMode} mode The mode.
* @param {any} [subCtx] The "sub" context.
*/
createDataTransformerContext(target, mode, subCtx = {}) {
let me = this;
return {
context: subCtx,
data: undefined,
emitGlobal: function () {
return me.context
.emitGlobal
.apply(me.context, arguments);
},
globals: me.context.globals(),
mode: mode,
options: deploy_helpers.cloneObject((target || {}).transformerOptions),
replaceWithValues: (val) => {
return me.context.replaceWithValues(val);
},
require: function (id) {
return me.context.require(id);
},
};
}
/** @inheritdoc */
deployWorkspace(files, target, opts) {
let me = this;
if (!opts) {
opts = {};
}
let hasCancelled = false;
let filesTodo = files.map(x => x);
let completed = (err) => {
filesTodo = [];
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: hasCancelled,
error: err,
target: target,
});
}
};
hasCancelled = me.context.isCancelling();
if (hasCancelled) {
completed(); // cancellation requested
}
else {
try {
let deployNextFile;
let fileCompleted = function (sender, e) {
try {
if (opts.onFileCompleted) {
opts.onFileCompleted(sender, e);
}
hasCancelled = hasCancelled || deploy_helpers.toBooleanSafe(e.canceled);
if (hasCancelled) {
completed(); // cancellation requested
}
else {
deployNextFile();
}
}
catch (err) {
me.context.log(i18.t('errors.withCategory', 'DeployPluginBase.deployWorkspace(1)', err));
}
};
deployNextFile = () => {
if (filesTodo.length < 1) {
completed();
return;
}
let f = filesTodo.shift();
if (!f) {
completed();
return;
}
try {
me.deployFile(f, target, {
context: opts.context,
onBeforeDeploy: (sender, e) => {
if (opts.onBeforeDeployFile) {
opts.onBeforeDeployFile(sender, e);
}
},
onCompleted: (sender, e) => {
fileCompleted(sender, e);
}
});
}
catch (e) {
fileCompleted(me, {
error: e,
file: f,
target: target,
});
}
};
deployNextFile();
}
catch (e) {
completed(e);
}
}
}
/** @inheritdoc */
dispose() {
deploy_globals.EVENTS.removeListener(deploy_contracts.EVENT_CONFIG_RELOADED, this.onConfigReloaded);
}
/** @inheritdoc */
downloadFile(file, target, opts) {
throw new Error("Not implemented!");
}
/** @inheritdoc */
getFileInfo(file, target, opts) {
throw new Error("Not implemented!");
}
/**
* Returns the others targets and their plugins.
*
* @param {deploy_contracts.DeployTarget} target The target for this plugin.
* @param {string | string[]} otherTargets The list of names of the "others" targets.
*
* @return {deploy_contracts.DeployTargetWithPlugins[]} The targets and their plugins.
*/
getTargetsWithPlugins(target, otherTargets) {
let batchTargets = [];
let normalizeString = (val) => {
return deploy_helpers.normalizeString(val);
};
let myTargetName = normalizeString(target.name);
let targetNames = deploy_helpers.asArray(otherTargets)
.map(x => normalizeString(x))
.filter(x => '' !== x);
if (targetNames.indexOf(myTargetName) > -1) {
// no recurrence!
vscode.window.showWarningMessage(i18.t('targets.cannotUseRecurrence', myTargetName));
}
// prevent recurrence
targetNames = targetNames.filter(x => x !== myTargetName);
let knownTargets = this.context.targets();
let knownPlugins = this.context.plugins();
// first find targets by name
let foundTargets = [];
targetNames.forEach(tn => {
let found = false;
knownTargets.forEach(t => {
if (normalizeString(t.name) === tn) {
found = true;
foundTargets.push(t);
}
});
if (!found) {
// we have an unknown target here
vscode.window.showWarningMessage(i18.t('targets.notFound', tn));
}
});
// now collect plugins for each
// found target
foundTargets.forEach(t => {
let newBatchTarget = {
plugins: [],
target: t,
};
knownPlugins.forEach(pi => {
let pluginType = normalizeString(pi.__type);
if (!pluginType || (pluginType === normalizeString(t.type))) {
newBatchTarget.plugins
.push(pi);
}
});
batchTargets.push(newBatchTarget);
});
return batchTargets;
}
/**
* Loads a data transformer by target.
* @param {TTarget} target The target.
* @param {deploy_contracts.DataTransformerMode} mode The mode.
* @param {(t: TTarget) => string} [scriptProvider] The custom logic to get the script path.
*
* @returns {deploy_contracts.DataTransformer} The loaded transformer.
*/
loadDataTransformer(target, mode, scriptProvider) {
if (!scriptProvider) {
scriptProvider = (t) => t.transformer; // default
}
let transformer;
let script = deploy_helpers.toStringSafe(scriptProvider(target));
script = this.context.replaceWithValues(script);
if (!deploy_helpers.isEmptyString(script)) {
let scriptModule = deploy_helpers.loadDataTransformerModule(script);
if (scriptModule) {
switch (mode) {
case deploy_contracts.DataTransformerMode.Restore:
transformer = scriptModule.restoreData;
if (!transformer) {
transformer = scriptModule.transformData;
}
break;
case deploy_contracts.DataTransformerMode.Transform:
transformer = scriptModule.transformData;
if (!transformer) {
transformer = scriptModule.restoreData;
}
break;
}
}
}
return deploy_helpers.toDataTransformerSafe(transformer);
}
/**
* Registers for a callback for a 'cancel' event that is called once.
*
* @param {deploy_contracts.EventHandler} callback The callback to register.
* @param {deploy_contracts.DeployFileOptions | deploy_contracts.DeployWorkspaceOptions} [opts] The underlying options.
*/
onCancelling(callback, opts) {
let ctx;
if (opts) {
ctx = opts.context;
}
ctx = ctx || this.context;
if (ctx) {
ctx.once(deploy_contracts.EVENT_CANCEL_DEPLOY, callback);
}
}
/**
* Is invoked after app config has been reloaded.
*
* @param {deploy_contracts.DeployConfiguration} cfg The new config.
*/
onConfigReloaded(cfg) {
}
/** @inheritdoc */
pullFile(file, target, opts) {
let me = this;
if (!opts) {
opts = {};
}
let hasCancelled = false;
let completed = (err) => {
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: hasCancelled,
error: err,
file: file,
target: target,
});
}
};
let downloadCompleted = (downloadedData) => {
try {
if (!downloadedData) {
downloadedData = Buffer.alloc(0);
}
if (hasCancelled) {
completed(null);
}
else {
FS.writeFile(file, downloadedData, (err) => {
completed(err);
});
}
}
catch (e) {
completed(e);
}
};
me.onCancelling(() => hasCancelled = true);
if (hasCancelled) {
completed(null);
}
else {
try {
let result = this.downloadFile(file, target, {
baseDirectory: opts.baseDirectory,
context: opts.context,
onBeforeDeploy: opts.onBeforeDeploy,
});
if (result) {
if (hasCancelled) {
completed(null);
}
else {
if (Buffer.isBuffer(result)) {
downloadCompleted(result);
}
else {
result.then((d) => {
downloadCompleted(d);
}).catch((err) => {
completed(err);
});
}
}
}
else {
downloadCompleted(null);
}
}
catch (e) {
completed(e);
}
}
}
/** @inheritdoc */
pullWorkspace(files, target, opts) {
let me = this;
if (!opts) {
opts = {};
}
let hasCancelled = false;
let filesTodo = files.map(x => x);
let completed = (err) => {
filesTodo = [];
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: hasCancelled,
error: err,
target: target,
});
}
};
hasCancelled = me.context.isCancelling();
if (hasCancelled) {
completed(); // cancellation requested
}
else {
try {
let pullNextFile;
let fileCompleted = function (sender, e) {
try {
if (opts.onFileCompleted) {
opts.onFileCompleted(sender, e);
}
hasCancelled = hasCancelled || deploy_helpers.toBooleanSafe(e.canceled);
if (hasCancelled) {
completed(); // cancellation requested
}
else {
pullNextFile();
}
}
catch (err) {
me.context.log(i18.t('errors.withCategory', 'DeployPluginBase.pullWorkspace(1)', err));
}
};
pullNextFile = () => {
if (filesTodo.length < 1) {
completed();
return;
}
let f = filesTodo.shift();
if (!f) {
completed();
return;
}
try {
me.pullFile(f, target, {
context: opts.context,
onBeforeDeploy: (sender, e) => {
if (opts.onBeforeDeployFile) {
opts.onBeforeDeployFile(sender, e);
}
},
onCompleted: (sender, e) => {
fileCompleted(sender, e);
}
});
}
catch (e) {
fileCompleted(me, {
error: e,
file: f,
target: target,
});
}
};
pullNextFile();
}
catch (e) {
completed(e);
}
}
}
}
exports.DeployPluginBase = DeployPluginBase;
/**
* A basic deploy plugin that is specially based on multi
* file operations (s. deployWorkspace() method).
*/
class MultiFileDeployPluginBase extends DeployPluginBase {
/** @inheritdoc */
deployFile(file, target, opts) {
if (!opts) {
opts = {};
}
let completedInvoked = false;
let completed = (sender, e) => {
if (completedInvoked) {
return;
}
completedInvoked = true;
if (opts.onCompleted) {
opts.onCompleted(sender, {
canceled: e.canceled,
error: e.error,
file: e.file,
target: e.target,
});
}
};
this.deployWorkspace([file], target, {
context: opts.context,
onBeforeDeployFile: (sender, e) => {
if (opts.onBeforeDeploy) {
opts.onBeforeDeploy(sender, {
destination: e.destination,
file: e.file,
target: e.target,
});
}
},
onFileCompleted: (sender, e) => {
completed(sender, e);
},
onCompleted: (sender, e) => {
completed(sender, {
canceled: e.canceled,
error: e.error,
file: file,
target: e.target,
});
},
});
}
/** @inheritdoc */
pullFile(file, target, opts) {
if (!opts) {
opts = {};
}
let me = this;
let completedInvoked = false;
let completed = (sender, e) => {
if (completedInvoked) {
return;
}
completedInvoked = true;
if (opts.onCompleted) {
opts.onCompleted(sender, {
canceled: e.canceled,
error: e.error,
file: e.file,
target: e.target,
});
}
};
this.pullWorkspace([file], target, {
context: opts.context,
onBeforeDeployFile: (sender, e) => {
if (opts.onBeforeDeploy) {
opts.onBeforeDeploy(sender, {
destination: e.destination,
file: e.file,
target: e.target,
});
}
},
onFileCompleted: (sender, e) => {
completed(sender, e);
},
onCompleted: (sender, e) => {
completed(sender, {
canceled: e.canceled,
error: e.error,
file: file,
target: e.target,
});
},
});
}
/** @inheritdoc */
pullWorkspace(files, target, opts) {
let me = this;
if (!opts) {
opts = {};
}
let hasCancelled = false;
files.forEach(x => {
hasCancelled = hasCancelled || me.context.isCancelling();
if (opts.onBeforeDeployFile) {
opts.onBeforeDeployFile(me, {
destination: null,
file: x,
target: target,
});
}
if (opts.onFileCompleted) {
opts.onFileCompleted(me, {
canceled: hasCancelled,
error: new Error("Not implemented!"),
file: x,
target: target,
});
}
});
hasCancelled = hasCancelled || me.context.isCancelling();
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: hasCancelled,
error: new Error("Not implemented!"),
target: target,
});
}
}
}
exports.MultiFileDeployPluginBase = MultiFileDeployPluginBase;
/**
* A basic deploy plugin that is specially based on multi
* file operations which uses a context, like a network connection (s. deployFileWithContext() method).
*/
class DeployPluginWithContextBase extends MultiFileDeployPluginBase {
/** @inheritdoc */
compareFiles(file, target, opts) {
let me = this;
if (!opts) {
opts = {};
}
return new Promise((resolve, reject) => {
let wrapper;
let completed = (err, result) => {
let finished = () => {
if (err) {
reject(err);
}
else {
resolve(result);
}
};
me.destroyContext(wrapper).then(() => {
finished();
}).catch(() => {
finished();
});
};
let wf = Workflows.create();
// create context
wf.next((wfCtx) => __awaiter(this, void 0, void 0, function* () {
wrapper = yield me.createContext(target, [file], opts, deploy_contracts.DeployDirection.FileInfo);
}));
// compare file
wf.next((wfCtx) => __awaiter(this, void 0, void 0, function* () {
wfCtx.result = yield me.compareFilesWithContext(wrapper.context, file, target, opts);
}));
wf.start().then((result) => {
completed(null, result);
}).catch((err) => {
completed(err);
});
});
}
/**
* Compares a local file with a remote one by using a context.
*
* @param {TContext} ctx The context.
* @param {string} file The file to compare.
* @param {DeployTarget} target The source from where to download the file from.
* @param {DeployFileOptions} [opts] Additional options.
*
* @return {Promise<FileCompareResult>} The result.
*/
compareFilesWithContext(ctx, file, target, opts) {
return __awaiter(this, void 0, void 0, function* () {
let me = this;
let wf = Workflows.create();
// get info about REMOTE file
wf.next(() => __awaiter(this, void 0, void 0, function* () {
return yield me.getFileInfoWithContext(ctx, file, target, opts);
}));
// check if local file exists
wf.next((ctx) => {
let right = ctx.previousValue;
return new Promise((resolve, reject) => {
try {
let left = {
exists: undefined,
isRemote: false,
};
FS.exists(file, (exists) => {
left.exists = exists;
if (!left.exists) {
ctx.finish(); // no need to get local file info
}
let result = {
left: left,
right: right,
};
ctx.result = result;
resolve(result);
});
}
catch (e) {
reject(e);
}
});
});
// get local file info
wf.next((ctx) => {
let result = ctx.previousValue;
return new Promise((resolve, reject) => {
FS.lstat(file, (err, stat) => {
if (err) {
reject(err);
}
else {
try {
result.left.name = Path.basename(file);
result.left.path = Path.dirname(file);
result.left.modifyTime = Moment(stat.ctime);
result.left.size = stat.size;
resolve(result);
}
catch (e) {
reject(e);
}
}
});
});
});
return yield wf.start();
});
}
/** @inheritdoc */
compareWorkspace(files, target, opts) {
let me = this;
if (!opts) {
opts = {};
}
return new Promise((resolve, reject) => {
let wrapper;
let completed = (err, result) => {
let finished = () => {
if (err) {
reject(err);
}
else {
resolve(result);
}
};
me.destroyContext(wrapper).then(() => {
finished();
}).catch(() => {
finished();
});
};
let wf = Workflows.create();
// create context
wf.next((ctx) => __awaiter(this, void 0, void 0, function* () {
wrapper = yield me.createContext(target, files, opts, deploy_contracts.DeployDirection.FileInfo);
ctx.result = [];
}));
// check files
files.forEach(f => {
wf.next((ctx) => __awaiter(this, void 0, void 0, function* () {
let compareResult = yield me.compareFilesWithContext(wrapper.context, f, target, opts);
ctx.result.push(compareResult);
return compareResult;
}));
});
wf.start().then((result) => {
completed(null, result);
}).catch((err) => {
completed(err);
});
});
}
/** @inheritdoc */
deployWorkspace(files, target, opts) {
if (!opts) {
opts = {};
}
let me = this;
// report that whole operation has been completed
let filesTodo = files.map(x => x); // create "TODO"" list
let hasCancelled = false;
let completed = (err) => {
filesTodo = [];
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: hasCancelled,
error: err,
target: target,
});
}
};
hasCancelled = me.context.isCancelling();
if (hasCancelled) {
completed(); // cancellation requested
}
else {
// destroy context before raise
// "completed" event
let destroyContext = (wrapper, completedErr) => {
try {
if (wrapper.destroy) {
// destroy context
wrapper.destroy().then(() => {
completed(completedErr);
}).catch((e) => {
me.context.log(i18.t('errors.withCategory', 'DeployPluginWithContextBase.deployWorkspace(2)', e));
completed(completedErr);
});
}
else {
completed(completedErr);
}
}
catch (e) {
me.context.log(i18.t('errors.withCategory', 'DeployPluginWithContextBase.deployWorkspace(1)', e));
completed(completedErr);
}
};
try {
// create context...
this.createContext(target, files, opts, deploy_contracts.DeployDirection.Deploy).then((wrapper) => {
try {
let deployNext;
// report that single file
// deployment has been completed
let fileCompleted = function (file, err, canceled) {
if (opts.onFileCompleted) {
opts.onFileCompleted(me, {
canceled: canceled,
error: err,
file: file,
target: target,
});
}
hasCancelled = hasCancelled || deploy_helpers.toBooleanSafe(canceled);
if (hasCancelled) {
destroyContext(wrapper, null);
}
else {
deployNext(); // deploy next
}
};
deployNext = () => {
if (filesTodo.length < 1) {
destroyContext(wrapper);
return;
}
let currentFile = filesTodo.shift();
try {
me.deployFileWithContext(wrapper.context, currentFile, target, {
context: opts.context,
onBeforeDeploy: (sender, e) => {
if (opts.onBeforeDeployFile) {
opts.onBeforeDeployFile(sender, {
destination: e.destination,
file: e.file,
target: e.target,
});
}
},
onCompleted: (sender, e) => {
fileCompleted(e.file, e.error, e.canceled);
}
});
}
catch (e) {
fileCompleted(currentFile, e); // deploy error
}
};
deployNext(); // start with first file
}
catch (e) {
destroyContext(wrapper, e); // global deploy error
}
}).catch((err) => {
completed(err); // could not create context
});
}
catch (e) {
completed(e); // global error
}
}
}
/**
* Destroys a context.
*
* @param {DeployPluginContextWrapper<TContext>} wrapper The wrapper with the context.
*
* @return {Promise<TContext>} The promise.
*/
destroyContext(wrapper) {
return __awaiter(this, void 0, void 0, function* () {
if (wrapper) {
if (wrapper.destroy) {
return yield wrapper.destroy();
}
}
});
}
/** @inheritdoc */
downloadFile(file, target, opts) {
if (!opts) {
opts = {};
}
let me = this;
return new Promise((resolve, reject) => {
let completed = (err, data) => {
if (err) {
reject(err);
}
else {
resolve(data);
}
};
// destroy context before raise
// "completed" event
let destroyContext = (wrapper, completedErr, data) => {
try {
if (wrapper.destroy) {
// destroy context
wrapper.destroy().then(() => {
completed(completedErr, data);
}).catch((e) => {
me.context.log(i18.t('errors.withCategory', 'DeployPluginWithContextBase.downloadFile(2)', e));
completed(completedErr, data);
});
}
else {
completed(completedErr, data);
}
}
catch (e) {
me.context.log(i18.t('errors.withCategory', 'DeployPluginWithContextBase.downloadFile(1)', e));
completed(completedErr, data);
}
};
// create context...
me.createContext(target, [file], opts, deploy_contracts.DeployDirection.Download).then((wrapper) => {
try {
let result = me.downloadFileWithContext(wrapper.context, file, target, opts);
if (result) {
if (Buffer.isBuffer(result)) {
destroyContext(wrapper, null, result);
}
else {
result.then((data) => {
destroyContext(wrapper, null, data);
}).catch((err) => {
destroyContext(wrapper, err);
});
}
}
else {
destroyContext(wrapper, null);
}
}
catch (e) {
destroyContext(wrapper, e);
}
}).catch((err) => {
completed(err);
});
});
}
/**
* Downloads a file by using a context.
*
* @param {TContext} ctx The context to use.
* @param {string} file The path of the local file.
* @param {DeployTarget} target The target.
* @param {DeployFileOptions} [opts] Additional options.
*
* @return {Promise<Buffer>|Buffer} The result.
*/
downloadFileWithContext(ctx, file, target, opts) {
throw new Error("Not implemented!");
}
/** @inheritdoc */
getFileInfo(file, target, opts) {
let me = this;
if (!opts) {
opts = {};
}
return new Promise((resolve, reject) => {
let completed = deploy_helpers.createSimplePromiseCompletedAction(resolve, reject);
let wf = Workflows.create();
let wrapper;
wf.once('end', (err, wcnt, info) => {
if (wrapper) {
if (wrapper.destroy) {
try {
Promise.resolve(wrapper.destroy()).then(() => {
completed(err, info);
}).catch((e) => {
me.context.log(i18.t('errors.withCategory', 'DeployPluginWithContextBase.getFileInfo(2)', e));
completed(err, info);
});
}
catch (e) {
me.context.log(i18.t('errors.withCategory', 'DeployPluginWithContextBase.getFileInfo(1)', e));
completed(err, info);
}
}
else {
completed(err, info);
}
}
else {
completed(err, info);
}
});
// create context
wf.next((ctx) => __awaiter(this, void 0, void 0, function* () {
return yield me.createContext(target, [file], opts, deploy_contracts.DeployDirection.FileInfo);
}));
// get file info
wf.next((ctx) => __awaiter(this, void 0, void 0, function* () {
wrapper = ctx.previousValue;
return yield me.getFileInfoWithContext(wrapper.context, file, target, opts);
}));
// write result
wf.next((ctx) => {
ctx.result = ctx.previousValue;
});
wf.start().then(() => {
// is done by 'end' event
}).catch((err) => {
// is done by 'end' event
});
});
}
/**
* Gets the info of a file by using a context.
*
* @param {TContext} ctx The context to use.
* @param {string} file The path of the local file.
* @param {DeployTarget} target The target.
* @param {DeployFileOptions} [opts] Additional options.
*
* @return {Promise<deploy_contracts.FileInfo>|deploy_contracts.FileInfo} The result.
*/
getFileInfoWithContext(ctx, file, target, opts) {
throw new Error("Not implemented!");
}
/**
* Pulls a file by using a context.
*
* @param {TContext} ctx The context to use.
* @param {string} file The path of the local file.
* @param {DeployTarget} target The target.
* @param {DeployFileOptions} [opts] Additional options.
*/
pullFileWithContext(ctx, file, target, opts) {
let me = this;
if (!opts) {
opts = {};
}
let hasCancelled = false;
let completed = (err) => {
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: hasCancelled,
error: err,
file: file,
target: target,
});
}
};
let downloadCompleted = (downloadedData) => {
try {
if (!downloadedData) {
downloadedData = Buffer.alloc(0);
}
if (hasCancelled) {
completed(null);
}
else {
FS.writeFile(file, downloadedData, (err) => {
completed(err);
});
}
}
catch (e) {
completed(e);
}
};
me.onCancelling(() => hasCancelled = true);
if (hasCancelled) {
completed(null);
}
else {
try {
let result = this.downloadFileWithContext(ctx, file, target, {
baseDirectory: opts.baseDirectory,
context: opts.context,
onBeforeDeploy: opts.onBeforeDeploy,
});
if (result) {
if (hasCancelled) {
completed(null);
}
else {
if (Buffer.isBuffer(result)) {
downloadCompleted(result);
}
else {
result.then((d) => {
downloadCompleted(d);
}).catch((err) => {
completed(err);
});
}
}
}
else {
downloadCompleted(null);
}
}
catch (e) {
completed(e);
}
}
}
/** @inheritdoc */
pullWorkspace(files, target, opts) {
if (!opts) {
opts = {};
}
let me = this;
// report that whole operation has been completed
let filesTodo = files.map(x => x); // create "TODO"" list
let hasCancelled = false;
let completed = (err) => {
filesTodo = [];
if (opts.onCompleted) {
opts.onCompleted(me, {
canceled: hasCancelled,
error: err,
target: target,
});
}
};
hasCancelled = me.context.isCancelling();
if (hasCancelled) {
completed(); // cancellation requested
}
else {
// destroy context before raise
// "completed" event
let destroyContext = (wrapper, completedErr) => {
try {
if (wrapper.destroy) {
// destroy context
wrapper.destroy().then(() => {
completed(completedErr);
}).catch((e) => {
me.context.log(i18.t('errors.withCategory', 'DeployPluginWithContextBase.pullWorkspace(2)', e));
completed(completedErr);
});
}
else {
completed(completedErr);
}
}
catch (e) {
me.context.log(i18.t('errors.withCategory', 'DeployPluginWithContextBase.pullWorkspace(1)', e));
completed(completedErr);
}
};
try {
// create context...
this.createContext(target, files, opts, deploy_contracts.DeployDirection.Pull).then((wrapper) => {
try {
let pullNext;
// report that single file
// pull has been completed
let fileCompleted = function (file, err, canceled) {
if (opts.onFileCompleted) {
opts.onFileCompleted(me, {
canceled: canceled,
error: err,
file: file,
target: target,
});
}
hasCancelled = hasCancelled || deploy_helpers.toBooleanSafe(canceled);
if (hasCancelled) {
destroyContext(wrapper, null);
}
else {
pullNext(); // pull next
}
};
pullNext = () => {
if (filesTodo.length < 1) {
destroyContext(wrapper);
return;
}
let currentFile = filesTodo.shift();
try {
me.pullFileWithContext(wrapper.context, currentFile, target, {
context: opts.context,
onBeforeDeploy: (sender, e) => {
if (opts.onBeforeDeployFile) {
opts.onBeforeDeployFile(sender, {
destination: e.destination,
file: e.file,
target: e.target,
});
}
},
onCompleted: (sender, e) => {
fileCompleted(e.file, e.error, e.canceled);
}
});
}
catch (e) {
fileCompleted(currentFile, e); // pull error
}
};
pullNext(); // start with first file
}
catch (e) {
destroyContext(wrapper, e); // global deploy error
}
}).catch((err) => {
completed(err); // could not create context
});
}
catch (e) {
completed(e); // global error
}
}
}
}
exports.DeployPluginWithContextBase = DeployPluginWithContextBase;
/**
* A deployer plugin that creates a ZIP file to deploy files to.
*/
class ZipFileDeployPluginBase extends DeployPluginWithContextBase {
/** @inheritdoc */
get canGetFileInfo() {
return true;
}
/** @inheritdoc */
createContext(target, files, opts, direction) {