Skip to content

Commit 52ffb5f

Browse files
authored
Add new version JenkinsDownloadArtifactsV2 (#20529)
1 parent 543e5fc commit 52ffb5f

File tree

121 files changed

+29907
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+29907
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import * as Q from 'q';
2+
import * as tl from 'azure-pipelines-task-lib/task';
3+
4+
import {JenkinsRestClient, JenkinsJobDetails} from "./JenkinsRestClient"
5+
import {CommitsDownloader} from "./CommitsDownloader"
6+
import {WorkItemsDownloader} from "./WorkItemsDownloader"
7+
8+
var handlebars = require('handlebars');
9+
10+
export class ArtifactDetailsDownloader {
11+
public DownloadCommitsAndWorkItems(jenkinsJobDetails: JenkinsJobDetails): Q.Promise<void> {
12+
let defer: Q.Deferred<any> = Q.defer<any>();
13+
14+
console.log(tl.loc("DownloadingCommitsAndWorkItems"));
15+
16+
let jenkinsBuild = tl.getInput('jenkinsBuild', true);
17+
let startBuildIdStr: string = tl.getInput("startJenkinsBuildNumber", false) || "";
18+
let startBuildId: number = this.GetBuildIdFromVersion(startBuildIdStr, jenkinsJobDetails.isMultiBranchPipeline);
19+
let endBuildId: number = jenkinsJobDetails.buildId;
20+
let commitsDownloader: CommitsDownloader = new CommitsDownloader();
21+
22+
// validate endBuildId which is a mandatory parameter
23+
if (isNaN(endBuildId) || !endBuildId) {
24+
defer.reject(new Error(tl.loc("InvalidJenkinsBuildNumber")));
25+
return defer.promise;
26+
}
27+
28+
// validate the start build only if its required.
29+
if (startBuildIdStr.trim().length > 0 && isNaN(startBuildId) && jenkinsBuild !== 'LastSuccessfulBuild') {
30+
defer.reject(new Error(tl.loc("InvalidJenkinsStartBuildNumber", startBuildIdStr)));
31+
return defer.promise;
32+
}
33+
34+
// if you have a valid start and end buildId and then switch to LastSuccessfulBuild
35+
// previous start build still exists though its not visible. Hence check the jenkinsBuild input
36+
// and if its set to LastSuccessfulBuild consider startBuild is not mentioned.
37+
if (!startBuildId || isNaN(startBuildId) || jenkinsBuild === 'LastSuccessfulBuild') {
38+
console.log(tl.loc("JenkinsDownloadingChangeFromCurrentBuild", jenkinsJobDetails.buildId));
39+
commitsDownloader.DownloadFromSingleBuildAndSave(jenkinsJobDetails).then((commits: string) => {
40+
let commitMessages: string[] = CommitsDownloader.GetCommitMessagesFromCommits(commits);
41+
let workItemsDownloader: WorkItemsDownloader = new WorkItemsDownloader(commitMessages);
42+
workItemsDownloader.DownloadFromSingleBuildAndSave(jenkinsJobDetails).then(() => {
43+
defer.resolve(null);
44+
}, (error) => {
45+
defer.reject(error);
46+
});
47+
}, (error) => {
48+
defer.reject(error);
49+
});
50+
}
51+
else {
52+
if (jenkinsJobDetails.isMultiBranchPipeline) {
53+
// if multibranch validate if the branch names are same.
54+
let startBuildBranchName: string = this.GetBranchNameFromVersion(startBuildIdStr, jenkinsJobDetails.isMultiBranchPipeline);
55+
56+
if (startBuildBranchName.toLowerCase() !== jenkinsJobDetails.multiBranchPipelineName.toLowerCase()) {
57+
defer.reject(new Error(tl.loc("InvalidMultiBranchPipelineName", startBuildBranchName, jenkinsJobDetails.multiBranchPipelineName)));
58+
return defer.promise;
59+
}
60+
}
61+
62+
if (startBuildId < endBuildId) {
63+
console.log(tl.loc("DownloadingJenkinsChangeBetween", startBuildId, endBuildId));
64+
}
65+
else if (startBuildId > endBuildId) {
66+
console.log(tl.loc("DownloadingJenkinsChangeBetween", endBuildId, startBuildId));
67+
tl.debug(`Start build ${startBuildId} is greater than end build ${endBuildId}`);
68+
69+
// swap the Build IDs to fetch the roll back commits
70+
let temp: number = startBuildId;
71+
startBuildId = endBuildId;
72+
endBuildId = temp;
73+
}
74+
else if (startBuildId == endBuildId) {
75+
console.log(tl.loc("JenkinsNoCommitsToFetch"));
76+
defer.resolve(null);
77+
return defer.promise;
78+
}
79+
80+
// #1. Since we have two builds, we need to figure the build index
81+
this.GetBuildIdIndex(jenkinsJobDetails, startBuildId, endBuildId).then((buildIndex) => {
82+
let startIndex: number = buildIndex['startIndex'];
83+
let endIndex: number = buildIndex['endIndex'];
84+
85+
//#2. Download the commits using range and save
86+
commitsDownloader.DownloadFromBuildRangeAndSave(jenkinsJobDetails, startIndex, endIndex).then((commits: string) => {
87+
//#3. download workitems
88+
let commitMessages: string[] = CommitsDownloader.GetCommitMessagesFromCommits(commits);
89+
let workItemsDownloader: WorkItemsDownloader = new WorkItemsDownloader(commitMessages);
90+
workItemsDownloader.DownloadFromBuildRangeAndSave(jenkinsJobDetails, startIndex, endIndex).then(() => {
91+
defer.resolve(null);
92+
}, (error) => {
93+
defer.reject(error);
94+
});
95+
}, (error) => {
96+
defer.reject(error);
97+
})
98+
}, (error) => {
99+
defer.reject(error);
100+
});
101+
}
102+
103+
return defer.promise;
104+
}
105+
106+
private GetBuildIdFromVersion(version: string, isMultiBranchPipeline: boolean): number {
107+
let buildId: number = NaN;
108+
109+
if (!!version) {
110+
if (!isMultiBranchPipeline) {
111+
buildId = parseInt(version);
112+
}
113+
else {
114+
buildId = parseInt(version.substring(version.lastIndexOf(JenkinsRestClient.JenkinsBranchPathSeparator) + 1));
115+
}
116+
}
117+
118+
return buildId;
119+
}
120+
121+
private GetBranchNameFromVersion(version: string, isMultibranchPipeline: boolean): string {
122+
let branchName: string = version;
123+
124+
if (!!version && isMultibranchPipeline) {
125+
branchName = version.substring(0, version.lastIndexOf(JenkinsRestClient.JenkinsBranchPathSeparator));
126+
}
127+
128+
return branchName;
129+
}
130+
131+
private GetBuildIdIndex(jenkinsJobDetails: JenkinsJobDetails, startBuildId: number, endBuildId: number): Q.Promise<any> {
132+
let defer = Q.defer<any>();
133+
let buildUrl: string = `${jenkinsJobDetails.multiBranchPipelineUrlInfix}/api/json?tree=allBuilds[number]`;
134+
let startIndex: number = -1;
135+
let endIndex: number = -1;
136+
137+
console.log(tl.loc("FindBuildIndex"));
138+
handlebars.registerHelper('BuildIndex', function(buildId, index, options) {
139+
if (buildId == startBuildId) {
140+
startIndex = index;
141+
} else if (buildId == endBuildId) {
142+
endIndex = index;
143+
}
144+
145+
return options.fn(this);
146+
});
147+
148+
let source: string = '{{#each allBuilds}}{{#BuildIndex this.number @index}}{{/BuildIndex}}{{/each}}';
149+
let downloadHelper: JenkinsRestClient = new JenkinsRestClient();
150+
downloadHelper.DownloadJsonContent(buildUrl, source, null).then(() => {
151+
console.log(tl.loc("FoundBuildIndex", startIndex, endIndex));
152+
if (startIndex === -1 || endIndex === -1) {
153+
tl.debug(`cannot find valid startIndex ${startIndex} or endIndex ${endIndex}`);
154+
defer.reject(tl.loc("CannotFindBuilds"));
155+
}
156+
else {
157+
defer.resolve({startIndex: startIndex, endIndex: endIndex});
158+
}
159+
}, (error) => {
160+
defer.reject(error);
161+
});
162+
163+
return defer.promise;
164+
}
165+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import * as Q from 'q';
2+
import * as fs from 'fs';
3+
import * as path from 'path'
4+
import * as tl from 'azure-pipelines-task-lib/task';
5+
import {JenkinsJobDetails} from "./JenkinsRestClient"
6+
7+
export abstract class ArtifactDetailsDownloaderBase {
8+
protected WriteContentToFileAndUploadAsAttachment(content: string, filePath: string): Q.Promise<any> {
9+
let defer = Q.defer<void>();
10+
11+
// ensure it has .json extension
12+
if (path.extname(filePath) !== ".json") {
13+
filePath = `${filePath}.json`;
14+
}
15+
16+
fs.writeFile(filePath, content, (err) => {
17+
if (err) {
18+
console.log(tl.loc("CouldNotWriteToFile", err));
19+
defer.reject(err);
20+
return;
21+
}
22+
23+
console.log(tl.loc("UploadingAttachment", filePath));
24+
console.log(`##vso[task.uploadfile]${filePath}`);
25+
defer.resolve(null);
26+
});
27+
28+
return defer.promise;
29+
}
30+
31+
public abstract DownloadFromSingleBuildAndSave(jenkinsJobDetails: JenkinsJobDetails): Q.Promise<string>;
32+
public abstract DownloadFromBuildRangeAndSave(jenkinsJobDetails: JenkinsJobDetails, startIndex: number, endIndex: number): Q.Promise<string>;
33+
}

0 commit comments

Comments
 (0)