Skip to content

Commit 2d39dc6

Browse files
author
Jeff Young
authored
Merge pull request #4829 from jeffyoung/users/jeyou/jenkins-download-artifacts
Users/jeyou/jenkins download artifacts
2 parents fe8fdd1 + 020f1e7 commit 2d39dc6

File tree

16 files changed

+801
-177
lines changed

16 files changed

+801
-177
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// npm install mocha --save-dev
2+
// typings install dt~mocha --save --global
3+
4+
import assert = require('assert');
5+
import path = require('path');
6+
import os = require('os');
7+
import process = require('process');
8+
import fs = require('fs');
9+
10+
import * as ttm from 'vsts-task-lib/mock-test';
11+
12+
describe('JenkinsDownloadArtifacts L0 Suite', function () {
13+
this.timeout(20000);
14+
15+
before((done) => {
16+
process.env['ENDPOINT_AUTH_ID1'] = '{\"scheme\":\"UsernamePassword\", \"parameters\": {\"username\": \"uname\", \"password\": \"pword\"}}';
17+
process.env['ENDPOINT_URL_ID1'] = 'bogusURL';
18+
19+
done();
20+
});
21+
22+
/* tslint:disable:no-empty */
23+
after(function () { });
24+
/* tslint:enable:no-empty */
25+
26+
it('run JenkinsDownloadArtifacts with no server endpoint', (done) => {
27+
const tp: string = path.join(__dirname, 'L0NoServerEndpoint.js');
28+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
29+
30+
try {
31+
tr.run();
32+
33+
assert(tr.stderr.indexOf('Input required: serverEndpoint') !== -1, 'should have written to stderr');
34+
assert(tr.failed, 'task should have failed');
35+
done();
36+
37+
//assert(tr.ran(gradleWrapper + ' build'), 'it should have run gradlew build');
38+
//assert(tr.invokedToolCount === 1, 'should have only run gradle 1 time');
39+
//assert(tr.stderr.length === 0, 'should not have written to stderr');
40+
//assert(tr.succeeded, 'task should have succeeded');
41+
//assert(tr.stdout.indexOf('GRADLE_OPTS is now set to -Xmx2048m') > 0);
42+
} catch (err) {
43+
console.log(tr.stdout);
44+
console.log(tr.stderr);
45+
console.log(err);
46+
done(err);
47+
}
48+
});
49+
50+
it('run JenkinsDownloadArtifacts with no save to', (done) => {
51+
const tp: string = path.join(__dirname, 'L0NoSaveTo.js');
52+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
53+
54+
try {
55+
tr.run();
56+
57+
assert(tr.stderr.indexOf('Input required: saveTo') !== -1, 'should have written to stderr');
58+
assert(tr.failed, 'task should have failed');
59+
done();
60+
} catch (err) {
61+
console.log(tr.stdout);
62+
console.log(tr.stderr);
63+
console.log(err);
64+
done(err);
65+
}
66+
});
67+
68+
it('run JenkinsDownloadArtifacts with no job name', (done) => {
69+
const tp: string = path.join(__dirname, 'L0NoJobName.js');
70+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
71+
72+
try {
73+
tr.run();
74+
75+
assert(tr.stderr.indexOf('Input required: jobName') !== -1, 'should have written to stderr');
76+
assert(tr.failed, 'task should have failed');
77+
done();
78+
} catch (err) {
79+
console.log(tr.stdout);
80+
console.log(tr.stderr);
81+
console.log(err);
82+
done(err);
83+
}
84+
});
85+
86+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import ma = require('vsts-task-lib/mock-answer');
2+
import tmrm = require('vsts-task-lib/mock-run');
3+
import path = require('path');
4+
5+
const taskPath = path.join(__dirname, '..', 'jenkinsdownloadartifacts.js');
6+
const tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
7+
8+
tr.setInput('serverEndpoint', 'ID1');
9+
//tr.setInput('jobName', 'SomeJobName');
10+
tr.setInput('saveTo', path.join(__dirname, '..', 'tmp/'));
11+
tr.setInput('jenkinsBuild', 'LastSuccessfulBuild'); //BuildNumber
12+
13+
// provide answers for task mock
14+
const a: ma.TaskLibAnswers = <ma.TaskLibAnswers>{
15+
'checkPath': {
16+
'gradlew': true,
17+
'gradlew.bat': true
18+
}
19+
};
20+
tr.setAnswers(a);
21+
22+
tr.run();
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import ma = require('vsts-task-lib/mock-answer');
2+
import tmrm = require('vsts-task-lib/mock-run');
3+
import path = require('path');
4+
5+
const taskPath = path.join(__dirname, '..', 'jenkinsdownloadartifacts.js');
6+
const tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
7+
8+
tr.setInput('serverEndpoint', 'ID1');
9+
tr.setInput('jobName', 'SomeJobName');
10+
//tr.setInput('saveTo', path.join(__dirname, '..'));
11+
tr.setInput('jenkinsBuild', 'LastSuccessfulBuild'); //BuildNumber
12+
13+
// provide answers for task mock
14+
const a: ma.TaskLibAnswers = <ma.TaskLibAnswers>{
15+
'checkPath': {
16+
'gradlew': true,
17+
'gradlew.bat': true
18+
}
19+
};
20+
tr.setAnswers(a);
21+
22+
tr.run();
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import ma = require('vsts-task-lib/mock-answer');
2+
import tmrm = require('vsts-task-lib/mock-run');
3+
import path = require('path');
4+
5+
const taskPath = path.join(__dirname, '..', 'jenkinsdownloadartifacts.js');
6+
const tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
7+
8+
tr.setInput('jobName', 'SomeJobName');
9+
tr.setInput('saveTo', path.join(__dirname, '..'));
10+
tr.setInput('jenkinsBuild', 'LastSuccessfulBuild'); //BuildNumber
11+
//tr.setInput('jenkinsBuildNumber', '42');
12+
13+
// tr.setInput('serverEndpoint', 'ID1');
14+
15+
// provide answers for task mock
16+
const a: ma.TaskLibAnswers = <ma.TaskLibAnswers>{
17+
'checkPath': {
18+
'gradlew': true,
19+
'gradlew.bat': true
20+
}
21+
};
22+
tr.setAnswers(a);
23+
24+
tr.run();

Tasks/JenkinsDownloadArtifacts/jenkinsdownloadartifacts.ts

Lines changed: 83 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import fs = require('fs');
66
import path = require('path');
77
import shell = require('shelljs');
88
import Q = require('q');
9-
10-
var request = require('request');
9+
import request = require('request');
1110

1211
class Credential {
1312
mUsername: string;
@@ -20,140 +19,137 @@ class Credential {
2019
}
2120

2221
function getRequest(url: string, cred: Credential, strictSSL: boolean): Q.Promise<any> {
23-
let defer = Q.defer<any>();
24-
25-
request
26-
.get({url: url, strictSSL: strictSSL}, (err, res, body) => {
27-
if (res && body && res.statusCode === 200) {
28-
defer.resolve(JSON.parse(body));
29-
} else {
30-
if (res && res.statusCode) {
31-
tl.debug(tl.loc('ServerCallErrorCode', res.statusCode));
22+
const defer = Q.defer<any>();
23+
24+
request.get({url: url, strictSSL: strictSSL}, (err, res, body) => {
25+
if (res && body && res.statusCode === 200) {
26+
defer.resolve(JSON.parse(body));
27+
} else {
28+
if (res && res.statusCode) {
29+
tl.debug(tl.loc('ServerCallErrorCode', res.statusCode));
30+
}
31+
if (body) {
32+
tl.debug(body);
33+
}
34+
defer.reject(new Error(tl.loc('ServerCallFailed')));
3235
}
33-
if (body) {
34-
tl.debug(body);
35-
}
36-
defer.reject(new Error(tl.loc('ServerCallFailed')));
37-
}
38-
})
39-
.auth(cred.mUsername, cred.mPassword, true)
40-
.on('error', err => {
41-
defer.reject(new Error(err));
42-
});
36+
})
37+
.auth(cred.mUsername, cred.mPassword, true)
38+
.on('error', (err) => {
39+
//TODO: Do we even need an 'error' handler here if we're just re-throwing?
40+
defer.reject(new Error(err.message));
41+
});
4342

44-
return defer.promise;
43+
return defer.promise;
4544
}
4645

4746
function getLastSuccessful(serverEndpointUrl: string, jobName: string, cred: Credential, strictSSL: boolean): Q.Promise<number> {
48-
let defer = Q.defer<number>();
49-
let lastSuccessfulUrl = `${serverEndpointUrl}/job/${jobName}/api/json?tree=lastSuccessfulBuild[id,displayname]`;
50-
51-
getRequest(lastSuccessfulUrl, cred, strictSSL)
52-
.then( result => {
53-
if (result && result['lastSuccessfulBuild']) {
54-
let lastSuccessfulBuildId = result['lastSuccessfulBuild']['id'];
55-
if (lastSuccessfulBuildId) {
56-
defer.resolve(lastSuccessfulBuildId);
47+
const defer = Q.defer<number>();
48+
const lastSuccessfulUrl: string = `${serverEndpointUrl}/job/${jobName}/api/json?tree=lastSuccessfulBuild[id,displayname]`;
49+
50+
getRequest(lastSuccessfulUrl, cred, strictSSL).then((result) => {
51+
if (result && result['lastSuccessfulBuild']) {
52+
const lastSuccessfulBuildId = result['lastSuccessfulBuild']['id'];
53+
if (lastSuccessfulBuildId) {
54+
defer.resolve(lastSuccessfulBuildId);
55+
} else {
56+
defer.reject(new Error(tl.loc('CouldNotGetLastSuccessfuilBuildNumber')));
57+
}
5758
} else {
5859
defer.reject(new Error(tl.loc('CouldNotGetLastSuccessfuilBuildNumber')));
5960
}
60-
} else {
61-
defer.reject(new Error(tl.loc('CouldNotGetLastSuccessfuilBuildNumber')));
62-
}
63-
})
64-
.fail(err => {defer.reject(err)});
61+
})
62+
.fail((err) => { defer.reject(err); });
6563

6664
return defer.promise;
6765
}
6866

69-
function getArtifactsRelativePaths(serverEndpointUrl: string, jobName: string, jobBuildId: number,
70-
cred: Credential, strictSSL: boolean): Q.Promise<string[]> {
71-
let defer = Q.defer<string[]>();
72-
let artifactQueryUrl = `${serverEndpointUrl}/job/${jobName}/${jobBuildId}/api/json?tree=artifacts[*]`;
73-
74-
getRequest(artifactQueryUrl, cred, strictSSL)
75-
.then(result => {
76-
if (result && result['artifacts']) {
77-
let artifacts = result['artifacts'];
78-
if (artifacts.length === 0) {
79-
defer.reject(new Error(tl.loc('CouldNotFindArtifacts', jobName, jobBuildId)));
67+
function getArtifactsRelativePaths(serverEndpointUrl: string, jobName: string, jobBuildId: number, cred: Credential, strictSSL: boolean): Q.Promise<string[]> {
68+
const defer = Q.defer<string[]>();
69+
const artifactQueryUrl: string = `${serverEndpointUrl}/job/${jobName}/${jobBuildId}/api/json?tree=artifacts[*]`;
70+
71+
getRequest(artifactQueryUrl, cred, strictSSL).then((result) => {
72+
if (result && result['artifacts']) {
73+
const artifacts = result['artifacts'];
74+
if (artifacts.length === 0) {
75+
defer.reject(new Error(tl.loc('CouldNotFindArtifacts', jobName, jobBuildId)));
76+
} else {
77+
const artifactsRelativePaths = result['artifacts'].map((artifact) => {
78+
return artifact['relativePath'];
79+
});
80+
defer.resolve(artifactsRelativePaths);
81+
}
8082
} else {
81-
let artifactsRelativePaths = result['artifacts'].map(artifact => {
82-
return artifact['relativePath'];
83-
});
84-
defer.resolve(artifactsRelativePaths);
83+
// no artifacts for this job
84+
defer.reject(new Error(tl.loc('CouldNotFindArtifacts', jobName, jobBuildId)));
8585
}
86-
} else {
87-
// no artifacts for this job
88-
defer.reject(new Error(tl.loc('CouldNotFindArtifacts', jobName, jobBuildId)));
89-
}
90-
})
91-
.fail(err => {defer.reject(err)});
86+
})
87+
.fail((err) => { defer.reject(err); });
9288

93-
return defer.promise
89+
return defer.promise;
9490
}
9591

96-
async function download(url: string, localFile: string, cred: Credential, strictSSL: boolean) {
97-
tl.debug(tl.loc("DownloadFileTo", url, localFile));
92+
async function download(url: string, localFile: string, cred: Credential, strictSSL: boolean): Promise<void> {
93+
tl.debug(tl.loc('DownloadFileTo', url, localFile));
9894
await request.get( {url: url, strictSSL: strictSSL} )
9995
.auth(cred.mUsername, cred.mPassword, true)
100-
.on('error', err => {
101-
throw new Error(err);
96+
.on('error', (err) => {
97+
//TODO: Do we even need an 'error' handler here if we're just re-throwing?
98+
throw new Error(err.message);
10299
})
103100
.pipe(fs.createWriteStream(localFile));
104101

105-
tl.debug(tl.loc("FileSuccessfullyDownloaded", localFile));
102+
tl.debug(tl.loc('FileSuccessfullyDownloaded', localFile));
106103
}
107104

108-
function getArtifactUrl(serverEndpointUrl: string, jobName: string, jobBuildId: number, relativePath: string) {
105+
function getArtifactUrl(serverEndpointUrl: string, jobName: string, jobBuildId: number, relativePath: string): string {
109106
return `${serverEndpointUrl}/job/${jobName}/${jobBuildId}/artifact/${relativePath}`;
110107
}
111108

112109
async function doWork() {
113110
try {
114111
tl.setResourcePath(path.join( __dirname, 'task.json'));
115112

116-
let serverEndpoint: string = tl.getInput('serverEndpoint', true);
117-
let serverEndpointUrl: string = tl.getEndpointUrl(serverEndpoint, false);
113+
const serverEndpoint: string = tl.getInput('serverEndpoint', true);
114+
const serverEndpointUrl: string = tl.getEndpointUrl(serverEndpoint, false);
118115

119-
let serverEndpointAuth: tl.EndpointAuthorization = tl.getEndpointAuthorization(serverEndpoint, false);
120-
let username: string = serverEndpointAuth['parameters']['username'];
121-
let password: string = serverEndpointAuth['parameters']['password'];
122-
let cred: Credential = (username && password) ? new Credential(username, password) : new Credential("", "");
116+
const serverEndpointAuth: tl.EndpointAuthorization = tl.getEndpointAuthorization(serverEndpoint, false);
117+
const username: string = serverEndpointAuth['parameters']['username'];
118+
const password: string = serverEndpointAuth['parameters']['password'];
119+
const cred: Credential = (username && password) ? new Credential(username, password) : new Credential('', '');
123120

124-
let jobName: string = tl.getInput('jobName', true);
125-
let localPathRoot: string = tl.getPathInput('saveTo', true);
121+
const jobName: string = tl.getInput('jobName', true);
122+
const localPathRoot: string = tl.getPathInput('saveTo', true);
126123

127-
let strictSSL: boolean = ("true" !== tl.getEndpointDataParameter(serverEndpoint, "acceptUntrustedCerts", true));
124+
const strictSSL: boolean = ('true' !== tl.getEndpointDataParameter(serverEndpoint, 'acceptUntrustedCerts', true));
128125

129-
let jenkinsBuild: string = tl.getInput("jenkinsBuild", true);
126+
const jenkinsBuild: string = tl.getInput('jenkinsBuild', true);
130127
let buildId: number;
131-
if (jenkinsBuild === "LastSuccessfulBuild") {
128+
if (jenkinsBuild === 'LastSuccessfulBuild') {
132129
tl.debug(tl.loc('GetArtifactsFromLastSuccessfulBuild', jobName));
133130
buildId = await getLastSuccessful(serverEndpointUrl, jobName, cred, strictSSL);
134131
} else {
135-
let buildIdStr = tl.getInput("jenkinsBuildNumber");
132+
const buildIdStr: string = tl.getInput('jenkinsBuildNumber');
136133
buildId = parseInt(buildIdStr);
137134
}
138135
tl.debug(tl.loc('GetArtifactsFromBuildNumber', buildId, jobName));
139136

140-
let artifactsRelativePaths = await getArtifactsRelativePaths(serverEndpointUrl, jobName,
141-
buildId, cred, strictSSL);
142-
artifactsRelativePaths.forEach(relativePath => {
143-
let localPath = path.resolve(localPathRoot, relativePath);
144-
let dir = path.dirname(localPath);
137+
const artifactsRelativePaths: string[] = await getArtifactsRelativePaths(serverEndpointUrl, jobName, buildId, cred, strictSSL);
138+
artifactsRelativePaths.forEach((relativePath) => {
139+
const localPath: string = path.resolve(localPathRoot, relativePath);
140+
const dir: string = path.dirname(localPath);
145141
if (!tl.exist(dir)) {
146142
tl.mkdirP(dir);
147143
}
148144

149-
let artifactUrl = getArtifactUrl(serverEndpointUrl, jobName, buildId, relativePath);
145+
const artifactUrl: string = getArtifactUrl(serverEndpointUrl, jobName, buildId, relativePath);
146+
//Q: Are we supposed to await on this download call?
150147
download(artifactUrl, localPath, cred, strictSSL);
151148
});
152-
153-
} catch (e) {
154-
tl.debug(e.message);
155-
tl._writeError(e);
156-
tl.setResult(tl.TaskResult.Failed, e.message);
149+
} catch (err) {
150+
tl.debug(err.message);
151+
tl._writeError(err);
152+
tl.setResult(tl.TaskResult.Failed, err.message);
157153
}
158154
}
159155

0 commit comments

Comments
 (0)