Skip to content

Commit 6303d62

Browse files
author
Ivan Duplenskikh
committed
Add uploadFolder method
Rework Q to native Promise Add returns after rejects
1 parent 1a2311e commit 6303d62

File tree

14 files changed

+506
-410
lines changed

14 files changed

+506
-410
lines changed

Tasks/CopyFilesOverSSHV0/copyfilesoverssh.ts

+60-50
Original file line numberDiff line numberDiff line change
@@ -225,66 +225,76 @@ async function run() {
225225
}
226226
}
227227

228-
// identify the files to copy
229-
const filesToCopy: string[] = getFilesToCopy(sourceFolder, contents);
230-
231-
// copy files to remote machine
232-
if (filesToCopy) {
233-
tl.debug('Number of files to copy = ' + filesToCopy.length);
234-
tl.debug('filesToCopy = ' + filesToCopy);
235-
236-
console.log(tl.loc('CopyingFiles', filesToCopy.length));
237-
createRemoteFolders(filesToCopy, targetFolder, sourceFolder, flattenFolders);
238-
239-
const results = await Promise.allSettled(filesToCopy.map(async (fileToCopy) => {
240-
tl.debug('fileToCopy = ' + fileToCopy);
241-
242-
let relativePath;
243-
244-
if (flattenFolders) {
245-
relativePath = path.basename(fileToCopy);
246-
} else {
247-
relativePath = fileToCopy.substring(sourceFolder.length)
248-
.replace(/^\\/g, "")
249-
.replace(/^\//g, "");
250-
}
228+
if (contents.length === 1 && contents[0] === "**") {
229+
tl.debug("Upload all files to a remote machine");
230+
231+
try {
232+
const completedDirectory = await sshHelper.uploadFolder(sourceFolder, targetFolder);
233+
console.log(tl.loc('CopyDirectoryCompleted', completedDirectory));
234+
} catch (err) {
235+
throw tl.loc("CopyDirectoryFailed", sourceFolder, err);
236+
}
237+
} else {
238+
// identify the files to copy
239+
const filesToCopy: string[] = getFilesToCopy(sourceFolder, contents);
251240

252-
tl.debug('relativePath = ' + relativePath);
253-
let targetPath = path.posix.join(targetFolder, relativePath);
241+
// copy files to remote machine
242+
if (filesToCopy) {
243+
tl.debug('Number of files to copy = ' + filesToCopy.length);
244+
tl.debug('filesToCopy = ' + filesToCopy);
254245

255-
if (!path.isAbsolute(targetPath) && !utils.pathIsUNC(targetPath)) {
256-
targetPath = `./${targetPath}`;
257-
}
246+
console.log(tl.loc('CopyingFiles', filesToCopy.length));
247+
createRemoteFolders(filesToCopy, targetFolder, sourceFolder, flattenFolders);
258248

259-
console.log(tl.loc('StartedFileCopy', fileToCopy, targetPath));
249+
const results = await Promise.allSettled(filesToCopy.map(async (fileToCopy) => {
250+
tl.debug('fileToCopy = ' + fileToCopy);
260251

261-
if (!overwrite) {
262-
const fileExists: boolean = await sshHelper.checkRemotePathExists(targetPath);
252+
let relativePath;
263253

264-
if (fileExists) {
265-
throw tl.loc('FileExists', targetPath);
254+
if (flattenFolders) {
255+
relativePath = path.basename(fileToCopy);
256+
} else {
257+
relativePath = fileToCopy.substring(sourceFolder.length)
258+
.replace(/^\\/g, "")
259+
.replace(/^\//g, "");
266260
}
267-
}
268261

269-
targetPath = utils.unixyPath(targetPath);
270-
// looks like scp can only handle one file at a time reliably
271-
const uploadResult = await sshHelper.uploadFile(fileToCopy, targetPath);
272-
return uploadResult;
273-
}));
262+
tl.debug('relativePath = ' + relativePath);
263+
let targetPath = path.posix.join(targetFolder, relativePath);
274264

275-
var errors = results.filter(p => p.status === 'rejected') as PromiseRejectedResult[];
265+
if (!path.isAbsolute(targetPath) && !utils.pathIsUNC(targetPath)) {
266+
targetPath = `./${targetPath}`;
267+
}
276268

277-
if (errors.length > 0) {
278-
errors.forEach(x => tl.error(x.reason));
279-
tl.setResult(tl.TaskResult.Failed, tl.loc('NumberFailed', errors.length));
280-
}
269+
console.log(tl.loc('StartedFileCopy', fileToCopy, targetPath));
281270

282-
var successedFiles = results.filter(p => p.status === 'fulfilled');
283-
console.log(tl.loc('CopyCompleted', successedFiles.length));
284-
} else if (failOnEmptySource) {
285-
throw tl.loc('NothingToCopy');
286-
} else {
287-
tl.warning(tl.loc('NothingToCopy'));
271+
if (!overwrite) {
272+
const fileExists: boolean = await sshHelper.checkRemotePathExists(targetPath);
273+
274+
if (fileExists) {
275+
throw tl.loc('FileExists', targetPath);
276+
}
277+
}
278+
279+
targetPath = utils.unixyPath(targetPath);
280+
// looks like scp can only handle one file at a time reliably
281+
return sshHelper.uploadFile(fileToCopy, targetPath);
282+
}));
283+
284+
var errors = results.filter(p => p.status === 'rejected') as PromiseRejectedResult[];
285+
286+
if (errors.length > 0) {
287+
errors.forEach(x => tl.error(x.reason));
288+
tl.setResult(tl.TaskResult.Failed, tl.loc('NumberFailed', errors.length));
289+
}
290+
291+
var successedFiles = results.filter(p => p.status === 'fulfilled');
292+
console.log(tl.loc('CopyCompleted', successedFiles.length));
293+
} else if (failOnEmptySource) {
294+
throw tl.loc('NothingToCopy');
295+
} else {
296+
tl.warning(tl.loc('NothingToCopy'));
297+
}
288298
}
289299
} catch (err) {
290300
tl.setResult(tl.TaskResult.Failed, err);

Tasks/CopyFilesOverSSHV0/sshhelper.ts

+98-84
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import Q = require('q');
21
import tl = require('azure-pipelines-task-lib/task');
3-
const path = require('path');
42
var Ssh2Client = require('ssh2').Client;
53
var SftpClient = require('ssh2-sftp-client');
64

@@ -30,27 +28,27 @@ export class SshHelper {
3028
}
3129

3230
private async setupSshClientConnection() : Promise<void> {
33-
const defer = Q.defer<void>();
34-
this.sshClient = new Ssh2Client();
35-
this.sshClient.once('ready', () => {
36-
defer.resolve();
37-
}).once('error', (err) => {
38-
defer.reject(tl.loc('ConnectionFailed', err));
39-
}).connect(this.sshConfig);
40-
await defer.promise;
31+
return new Promise((resolve, reject) => {
32+
this.sshClient = new Ssh2Client();
33+
this.sshClient.once('ready', () => {
34+
resolve();
35+
}).once('error', (err) => {
36+
reject(tl.loc('ConnectionFailed', err));
37+
}).connect(this.sshConfig);
38+
});
4139
}
4240

4341
private async setupSftpConnection() : Promise<void> {
44-
const defer = Q.defer<void>();
45-
try {
46-
this.sftpClient = new SftpClient();
47-
await this.sftpClient.connect(this.sshConfig);
48-
defer.resolve();
49-
} catch (err) {
50-
this.sftpClient = null;
51-
defer.reject(tl.loc('ConnectionFailed', err));
52-
}
53-
await defer.promise;
42+
return new Promise(async (resolve, reject) => {
43+
try {
44+
this.sftpClient = new SftpClient();
45+
await this.sftpClient.connect(this.sshConfig);
46+
resolve();
47+
} catch (err) {
48+
this.sftpClient = null;
49+
reject(tl.loc('ConnectionFailed', err));
50+
}
51+
});
5452
}
5553

5654
/**
@@ -106,16 +104,7 @@ export class SshHelper {
106104
return new Promise(async (resolve, reject) => {
107105
if (!this.sftpClient) {
108106
reject(tl.loc('ConnectionNotSetup'));
109-
}
110-
111-
const remotePath = path.dirname(dest);
112-
113-
try {
114-
if (!await this.sftpClient.exists(remotePath)) {
115-
await this.sftpClient.mkdir(remotePath, true);
116-
}
117-
} catch (error) {
118-
reject(tl.loc('TargetNotCreated', remotePath));
107+
return;
119108
}
120109

121110
try {
@@ -124,36 +113,55 @@ export class SshHelper {
124113
} else {
125114
await this.sftpClient.put(sourceFile, dest);
126115
}
116+
127117
resolve(dest);
128118
} catch (err) {
129119
reject(tl.loc('UploadFileFailed', sourceFile, dest, err));
130120
}
131121
});
132122
}
133123

124+
async uploadFolder(sourceFolder: string, destFolder: string) : Promise<string> {
125+
tl.debug('Upload ' + sourceFolder + ' to ' + destFolder + ' on remote machine.');
126+
127+
return new Promise(async (resolve, reject) => {
128+
if (!this.sftpClient) {
129+
reject(tl.loc('ConnectionNotSetup'));
130+
return;
131+
}
132+
133+
try {
134+
await this.sftpClient.uploadDir(sourceFolder, destFolder);
135+
} catch (err) {
136+
reject(tl.loc('UploadFolderFailed', sourceFolder, destFolder, err));
137+
}
138+
});
139+
}
140+
134141
/**
135142
* Returns true if the path exists on remote machine, false if it does not exist
136143
* @param path
137144
* @returns {Promise<boolean>}
138145
*/
139146
async checkRemotePathExists(path: string) : Promise<boolean> {
140-
var defer = Q.defer<boolean>();
147+
return new Promise(async (resolve, reject) => {
148+
tl.debug(tl.loc('CheckingPathExistance', path));
141149

142-
tl.debug(tl.loc('CheckingPathExistance', path));
143-
if(!this.sftpClient) {
144-
defer.reject(tl.loc('ConnectionNotSetup'));
145-
}
146-
if (await this.sftpClient.exists(path)) {
147-
//path exists
148-
tl.debug(tl.loc('PathExists', path));
149-
defer.resolve(true);
150-
} else {
151-
//path does not exist
152-
tl.debug(tl.loc('PathNotExists', path));
153-
defer.resolve(false);
154-
}
150+
if (!this.sftpClient) {
151+
reject(tl.loc('ConnectionNotSetup'));
152+
return;
153+
}
155154

156-
return defer.promise;
155+
if (await this.sftpClient.exists(path)) {
156+
//path exists
157+
tl.debug(tl.loc('PathExists', path));
158+
resolve(true);
159+
} else {
160+
//path does not exist
161+
tl.debug(tl.loc('PathNotExists', path));
162+
resolve(false);
163+
}
164+
});
157165
}
158166

159167
/**
@@ -162,58 +170,64 @@ export class SshHelper {
162170
* @param options
163171
* @returns {Promise<string>}
164172
*/
165-
runCommandOnRemoteMachine(command: string, options: RemoteCommandOptions) : Q.Promise<string> {
166-
var defer = Q.defer<string>();
167-
var stdErrWritten:boolean = false;
173+
runCommandOnRemoteMachine(command: string, options: RemoteCommandOptions) : Promise<string> {
174+
return new Promise((resolve, reject) => {
175+
let stdErrWritten = false;
168176

169-
if(!this.sshClient) {
170-
defer.reject(tl.loc('ConnectionNotSetup'));
171-
}
177+
if (!this.sshClient) {
178+
reject(tl.loc('ConnectionNotSetup'));
179+
return;
180+
}
172181

173-
if(!options) {
174-
tl.debug('Options not passed to runCommandOnRemoteMachine, setting defaults.');
175-
var options = new RemoteCommandOptions();
176-
options.failOnStdErr = true;
177-
}
182+
if (!options) {
183+
tl.debug('Options not passed to runCommandOnRemoteMachine, setting defaults.');
184+
const options = new RemoteCommandOptions();
185+
options.failOnStdErr = true;
186+
}
178187

179-
var cmdToRun = command;
180-
if(cmdToRun.indexOf(';') > 0) {
181-
//multiple commands were passed separated by ;
182-
cmdToRun = cmdToRun.replace(/;/g, '\n');
183-
}
184-
tl.debug('cmdToRun = ' + cmdToRun);
188+
let cmdToRun = command;
185189

186-
this.sshClient.exec(cmdToRun, (err, stream) => {
187-
if(err) {
188-
defer.reject(tl.loc('RemoteCmdExecutionErr', cmdToRun, err))
190+
if (cmdToRun.indexOf(';') > 0) {
191+
//multiple commands were passed separated by ;
192+
cmdToRun = cmdToRun.replace(/;/g, '\n');
189193
}
190-
stream.on('close', (code, signal) => {
191-
tl.debug('code = ' + code + ', signal = ' + signal);
192-
if(code && code != 0) {
193-
//non zero exit code - fail
194-
defer.reject(tl.loc('RemoteCmdNonZeroExitCode', cmdToRun, code));
195-
} else {
196-
//no exit code or exit code of 0
197194

198-
//based on the options decide whether to fail the build or not if data was written to STDERR
199-
if(stdErrWritten === true && options.failOnStdErr === true) {
200-
//stderr written - fail the build
201-
defer.reject(tl.loc('RemoteCmdExecutionErr', cmdToRun, tl.loc('CheckLogForStdErr')));
195+
tl.debug('cmdToRun = ' + cmdToRun);
196+
197+
this.sshClient.exec(cmdToRun, (err, stream) => {
198+
if (err) {
199+
reject(tl.loc('RemoteCmdExecutionErr', cmdToRun, err));
200+
return;
201+
}
202+
203+
stream.on('close', (code, signal) => {
204+
tl.debug('code = ' + code + ', signal = ' + signal);
205+
206+
if (code && code != 0) {
207+
//non zero exit code - fail
208+
reject(tl.loc('RemoteCmdNonZeroExitCode', cmdToRun, code));
202209
} else {
203-
//success
204-
defer.resolve('0');
210+
//no exit code or exit code of 0
211+
212+
//based on the options decide whether to fail the build or not if data was written to STDERR
213+
if (stdErrWritten === true && options.failOnStdErr === true) {
214+
//stderr written - fail the build
215+
reject(tl.loc('RemoteCmdExecutionErr', cmdToRun, tl.loc('CheckLogForStdErr')));
216+
} else {
217+
//success
218+
resolve('0');
219+
}
205220
}
206-
}
207-
}).on('data', (data) => {
208-
console.log(data);
209-
}).stderr.on('data', (data) => {
221+
}).on('data', (data) => {
222+
console.log(data);
223+
}).stderr.on('data', (data) => {
210224
stdErrWritten = true;
211225
tl.debug('stderr = ' + data);
212-
if(data && data.toString().trim() !== '') {
226+
if (data && data.toString().trim() !== '') {
213227
tl.error(data);
214228
}
215229
});
230+
});
216231
});
217-
return defer.promise;
218232
}
219233
}

Tasks/CopyFilesOverSSHV0/task.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@
153153
"ConnectionFailed": "Failed to connect to remote machine. Verify the SSH service connection details. %s.",
154154
"ConnectionNotSetup": "SSH service connection is not set up.",
155155
"CopyCompleted": "Completed copying %s files to the remote machine.",
156+
"CopyDirectoryCompleted": "Completed copying %s directory to the remote machine.",
157+
"CopyDirectoryFailed": "Failed to copy %s directory to the remote machine. %s",
156158
"CopyingFiles": "Found %s files to copy to the remote machine.",
157159
"FailedOnFile": "Failed to copy %s. %s",
158160
"FileExists": "File %s cannot be copied to the remote machine because it already exists and the 'Overwrite' option is disabled.",
@@ -168,6 +170,7 @@
168170
"TargetNotCreated": "Unable to create target folder %s.",
169171
"CheckingPathExistance": "Checking if %s on the remote machine exists.",
170172
"PathExists": "%s exists on the remote machine",
171-
"PathNotExists": "%s doesn't exist on the remote machine"
173+
"PathNotExists": "%s doesn't exist on the remote machine",
174+
"UploadFolderFailed": "Failed to upload folder %s to %s on remote machine. %s."
172175
}
173176
}

0 commit comments

Comments
 (0)