@elisra-devops/docgen-data-provider 1.110.0 → 1.111.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/helpers/tfs.js +9 -6
- package/bin/helpers/tfs.js.map +1 -1
- package/bin/modules/PipelinesDataProvider.d.ts +97 -2
- package/bin/modules/PipelinesDataProvider.js +281 -9
- package/bin/modules/PipelinesDataProvider.js.map +1 -1
- package/bin/modules/TicketsDataProvider.js +2 -1
- package/bin/modules/TicketsDataProvider.js.map +1 -1
- package/bin/tests/helpers/tfs.test.js +4 -3
- package/bin/tests/helpers/tfs.test.js.map +1 -1
- package/bin/tests/modules/pipelineDataProvider.test.js +336 -4
- package/bin/tests/modules/pipelineDataProvider.test.js.map +1 -1
- package/bin/tests/modules/ticketsDataProvider.test.js +2 -0
- package/bin/tests/modules/ticketsDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/helpers/tfs.ts +11 -6
- package/src/modules/PipelinesDataProvider.ts +400 -9
- package/src/modules/TicketsDataProvider.ts +2 -1
- package/src/tests/helpers/tfs.test.ts +4 -3
- package/src/tests/modules/pipelineDataProvider.test.ts +512 -5
- package/src/tests/modules/ticketsDataProvider.test.ts +5 -0
package/bin/helpers/tfs.js
CHANGED
|
@@ -90,7 +90,7 @@ class TFSServices {
|
|
|
90
90
|
headers: customHeaders,
|
|
91
91
|
method: requestMethod,
|
|
92
92
|
data: data,
|
|
93
|
-
timeout: requestMethod.toLocaleLowerCase() === 'get' ?
|
|
93
|
+
timeout: requestMethod.toLocaleLowerCase() === 'get' ? 30000 : undefined,
|
|
94
94
|
};
|
|
95
95
|
this.applyAuth(config, pat);
|
|
96
96
|
return this.executeWithRetry(cleanUrl, config, printError, (response) => {
|
|
@@ -105,7 +105,7 @@ class TFSServices {
|
|
|
105
105
|
headers: customHeaders,
|
|
106
106
|
method: requestMethod,
|
|
107
107
|
data: data,
|
|
108
|
-
timeout: requestMethod.toLocaleLowerCase() === 'get' ?
|
|
108
|
+
timeout: requestMethod.toLocaleLowerCase() === 'get' ? 30000 : undefined,
|
|
109
109
|
};
|
|
110
110
|
this.applyAuth(config, pat);
|
|
111
111
|
return this.executeWithRetry(cleanUrl, config, printError, (response) => {
|
|
@@ -149,8 +149,8 @@ class TFSServices {
|
|
|
149
149
|
*/
|
|
150
150
|
static async executeWithRetry(url, config, printError, responseProcessor) {
|
|
151
151
|
let attempts = 0;
|
|
152
|
-
const maxAttempts = 3;
|
|
153
152
|
const baseDelay = 500; // Start with 500ms delay
|
|
153
|
+
const maxAttempts = 3;
|
|
154
154
|
while (true) {
|
|
155
155
|
try {
|
|
156
156
|
const result = await this.axiosInstance.request(Object.assign(Object.assign({}, config), { url }));
|
|
@@ -187,7 +187,10 @@ class TFSServices {
|
|
|
187
187
|
static isRetryableError(error) {
|
|
188
188
|
var _a, _b;
|
|
189
189
|
// Network errors
|
|
190
|
-
if (error.code === 'ECONNRESET' ||
|
|
190
|
+
if (error.code === 'ECONNRESET' ||
|
|
191
|
+
error.code === 'ETIMEDOUT' ||
|
|
192
|
+
error.code === 'ENOTFOUND' ||
|
|
193
|
+
error.message.includes('timeout')) {
|
|
191
194
|
return true;
|
|
192
195
|
}
|
|
193
196
|
// Server errors (5xx)
|
|
@@ -205,7 +208,7 @@ class TFSServices {
|
|
|
205
208
|
*/
|
|
206
209
|
static logDetailedError(error, url) {
|
|
207
210
|
if (error.response) {
|
|
208
|
-
logger_1.default.error(`Error for ${url}
|
|
211
|
+
logger_1.default.error(`Error for ${url} - ${error.message}`);
|
|
209
212
|
logger_1.default.error(`Status: ${error.response.status}`);
|
|
210
213
|
if (error.response.data) {
|
|
211
214
|
if (typeof error.response.data === 'string') {
|
|
@@ -218,7 +221,7 @@ class TFSServices {
|
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
223
|
else {
|
|
221
|
-
logger_1.default.error(`Error for ${url}
|
|
224
|
+
logger_1.default.error(`Error for ${url} - ${error.message}`);
|
|
222
225
|
}
|
|
223
226
|
}
|
|
224
227
|
static getErrorMessage(error) {
|
package/bin/helpers/tfs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tfs.js","sourceRoot":"","sources":["../../src/helpers/tfs.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tfs.js","sourceRoot":"","sources":["../../src/helpers/tfs.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,86 @@ export default class PipelinesDataProvider {
|
|
|
5
5
|
token: string;
|
|
6
6
|
private projectNameByIdCache;
|
|
7
7
|
constructor(orgUrl: string, token: string);
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the previous pipeline run used as the source side of an SVD pipeline range.
|
|
10
|
+
*
|
|
11
|
+
* For regular pipeline ranges, the Builds API is used first because it supports paging,
|
|
12
|
+
* completed/succeeded filters, and branch filtering. Stage-specific discovery keeps the
|
|
13
|
+
* older Pipelines Runs path because stage status must be checked from run details.
|
|
14
|
+
*
|
|
15
|
+
* @param teamProject Azure DevOps project name.
|
|
16
|
+
* @param pipelineId Pipeline/build definition id.
|
|
17
|
+
* @param toPipelineRunId Target run/build id. Candidates must be older than this id.
|
|
18
|
+
* @param targetPipeline Full target pipeline run details, used for repository/branch matching.
|
|
19
|
+
* @param searchPrevPipelineFromDifferentCommit When true, skip candidates from the same commit.
|
|
20
|
+
* @param fromStage Optional stage name. When set, only previous runs with this successful stage match.
|
|
21
|
+
*/
|
|
8
22
|
findPreviousPipeline(teamProject: string, pipelineId: string, toPipelineRunId: number, targetPipeline: any, searchPrevPipelineFromDifferentCommit: boolean, fromStage?: string): Promise<any>;
|
|
23
|
+
/**
|
|
24
|
+
* Finds the previous successful completed build for a definition.
|
|
25
|
+
*
|
|
26
|
+
* Discovery prefers the target run's branch first. If no same-branch candidate exists, it
|
|
27
|
+
* falls back to the same repository on any branch so auto-discovery can still work for
|
|
28
|
+
* sparse branches or customer histories where the previous success is on another branch.
|
|
29
|
+
*
|
|
30
|
+
* @returns Previous build id, or undefined when no valid candidate is found.
|
|
31
|
+
*/
|
|
32
|
+
findPreviousSuccessfulBuild(teamProject: string, definitionId: string, toBuildId: number, targetPipeline: any, searchPrevPipelineFromDifferentCommit: boolean): Promise<number | undefined>;
|
|
33
|
+
/**
|
|
34
|
+
* Pages the Builds API until a valid previous successful build is found.
|
|
35
|
+
*
|
|
36
|
+
* The API query already asks for completed/succeeded builds ordered by finish time, but
|
|
37
|
+
* candidates are validated locally as well to protect callers from incomplete API data,
|
|
38
|
+
* mocks, or future response-shape differences.
|
|
39
|
+
*/
|
|
40
|
+
private findPreviousSuccessfulBuildPage;
|
|
41
|
+
private getContinuationToken;
|
|
42
|
+
/**
|
|
43
|
+
* Extracts the primary repository from a pipeline run details response.
|
|
44
|
+
*
|
|
45
|
+
* Newer YAML runs expose repository resources as an array with a `self` entry, while some
|
|
46
|
+
* designer/classic pipeline responses expose the repository under `__designer_repo`.
|
|
47
|
+
*/
|
|
48
|
+
private getPrimaryPipelineRepository;
|
|
49
|
+
/**
|
|
50
|
+
* Validates a Builds API candidate against the target run.
|
|
51
|
+
*
|
|
52
|
+
* A candidate must be older than the target, completed successfully, from the same
|
|
53
|
+
* repository, and optionally from the required branch. When the caller asks for a
|
|
54
|
+
* different commit, candidates with the same source version are excluded.
|
|
55
|
+
*/
|
|
56
|
+
private isMatchingPreviousBuild;
|
|
57
|
+
/**
|
|
58
|
+
* Finds the previous successful release/deployment for an SVD release range.
|
|
59
|
+
*
|
|
60
|
+
* Release discovery pages the Release List API and expands environments so candidates can
|
|
61
|
+
* be validated by deployment status. A matching release must be older than the target,
|
|
62
|
+
* active, and have at least one succeeded environment.
|
|
63
|
+
*
|
|
64
|
+
* @returns Previous release id, or undefined when no valid candidate is found.
|
|
65
|
+
*/
|
|
66
|
+
findPreviousSuccessfulRelease(projectName: string, definitionId: string, toReleaseId: number): Promise<number | undefined>;
|
|
67
|
+
/**
|
|
68
|
+
* Finds the latest successful release/deployment for an SVD release range.
|
|
69
|
+
*
|
|
70
|
+
* Used when the release template omits `toReleaseId`. The same candidate rule is used as
|
|
71
|
+
* previous-release discovery: active release with at least one succeeded environment.
|
|
72
|
+
*
|
|
73
|
+
* @returns Latest release id, or undefined when no valid candidate is found.
|
|
74
|
+
*/
|
|
75
|
+
findLatestSuccessfulRelease(projectName: string, definitionId: string): Promise<number | undefined>;
|
|
76
|
+
private findSuccessfulReleasePage;
|
|
77
|
+
private isUnsupportedApiVersionError;
|
|
78
|
+
private getErrorMessage;
|
|
79
|
+
/**
|
|
80
|
+
* Validates a release candidate for auto-discovery.
|
|
81
|
+
*
|
|
82
|
+
* A release is considered successful for this SVD range purpose when at least one
|
|
83
|
+
* deployment environment succeeded. This mirrors the existing release artifact flow,
|
|
84
|
+
* where a release may contain multiple environments but still provide usable artifacts.
|
|
85
|
+
*/
|
|
86
|
+
private isPreviousSuccessfulRelease;
|
|
87
|
+
private isSuccessfulRelease;
|
|
9
88
|
/**
|
|
10
89
|
* Determines if a pipeline run is invalid based on various conditions.
|
|
11
90
|
*
|
|
@@ -198,11 +277,27 @@ export default class PipelinesDataProvider {
|
|
|
198
277
|
* @throws Will log an error message if the pipeline run history could not be fetched.
|
|
199
278
|
*/
|
|
200
279
|
GetPipelineRunHistory(projectName: string, pipelineId: string): Promise<any>;
|
|
280
|
+
/**
|
|
281
|
+
* Fetches the first page of release history for a definition.
|
|
282
|
+
*
|
|
283
|
+
* Kept for backward compatibility with existing consumers. New range/discovery flows
|
|
284
|
+
* should prefer GetAllReleaseHistory when full release history is required.
|
|
285
|
+
*/
|
|
201
286
|
GetReleaseHistory(projectName: string, definitionId: string): Promise<any>;
|
|
202
287
|
/**
|
|
203
|
-
*
|
|
288
|
+
* Fetches all releases for a definition using continuation tokens.
|
|
289
|
+
*
|
|
290
|
+
* This is used by SVD release range handling because the requested from/to releases may be
|
|
291
|
+
* older than the first page returned by Azure DevOps.
|
|
292
|
+
*
|
|
293
|
+
* @param range When provided, pagination stops as soon as both fromId and toId are present in
|
|
294
|
+
* the accumulated results. ADO returns releases in descending id order, so once the smallest
|
|
295
|
+
* id seen is <= the lower requested id both endpoints are guaranteed to be loaded.
|
|
204
296
|
*/
|
|
205
|
-
GetAllReleaseHistory(projectName: string, definitionId: string
|
|
297
|
+
GetAllReleaseHistory(projectName: string, definitionId: string, range?: {
|
|
298
|
+
fromId: number;
|
|
299
|
+
toId: number;
|
|
300
|
+
}): Promise<{
|
|
206
301
|
count: number;
|
|
207
302
|
value: any[];
|
|
208
303
|
}>;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tfs_1 = require("../helpers/tfs");
|
|
4
4
|
const logger_1 = require("../utils/logger");
|
|
5
|
+
const pLimit = require('p-limit');
|
|
6
|
+
const MAX_DISCOVERY_PAGES = 50;
|
|
5
7
|
class PipelinesDataProvider {
|
|
6
8
|
constructor(orgUrl, token) {
|
|
7
9
|
this.orgUrl = '';
|
|
@@ -10,10 +12,30 @@ class PipelinesDataProvider {
|
|
|
10
12
|
this.orgUrl = orgUrl;
|
|
11
13
|
this.token = token;
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the previous pipeline run used as the source side of an SVD pipeline range.
|
|
17
|
+
*
|
|
18
|
+
* For regular pipeline ranges, the Builds API is used first because it supports paging,
|
|
19
|
+
* completed/succeeded filters, and branch filtering. Stage-specific discovery keeps the
|
|
20
|
+
* older Pipelines Runs path because stage status must be checked from run details.
|
|
21
|
+
*
|
|
22
|
+
* @param teamProject Azure DevOps project name.
|
|
23
|
+
* @param pipelineId Pipeline/build definition id.
|
|
24
|
+
* @param toPipelineRunId Target run/build id. Candidates must be older than this id.
|
|
25
|
+
* @param targetPipeline Full target pipeline run details, used for repository/branch matching.
|
|
26
|
+
* @param searchPrevPipelineFromDifferentCommit When true, skip candidates from the same commit.
|
|
27
|
+
* @param fromStage Optional stage name. When set, only previous runs with this successful stage match.
|
|
28
|
+
*/
|
|
13
29
|
async findPreviousPipeline(teamProject, pipelineId, toPipelineRunId, targetPipeline, searchPrevPipelineFromDifferentCommit, fromStage = '') {
|
|
14
30
|
var _a;
|
|
31
|
+
if (!fromStage) {
|
|
32
|
+
const previousBuildId = await this.findPreviousSuccessfulBuild(teamProject, pipelineId, toPipelineRunId, targetPipeline, searchPrevPipelineFromDifferentCommit);
|
|
33
|
+
if (previousBuildId) {
|
|
34
|
+
return previousBuildId;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
15
37
|
const pipelineRuns = await this.GetPipelineRunHistory(teamProject, pipelineId);
|
|
16
|
-
if (!pipelineRuns.value) {
|
|
38
|
+
if (!(pipelineRuns === null || pipelineRuns === void 0 ? void 0 : pipelineRuns.value)) {
|
|
17
39
|
return undefined;
|
|
18
40
|
}
|
|
19
41
|
for (const pipelineRun of pipelineRuns.value) {
|
|
@@ -33,6 +55,233 @@ class PipelinesDataProvider {
|
|
|
33
55
|
}
|
|
34
56
|
return undefined;
|
|
35
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Finds the previous successful completed build for a definition.
|
|
60
|
+
*
|
|
61
|
+
* Discovery prefers the target run's branch first. If no same-branch candidate exists, it
|
|
62
|
+
* falls back to the same repository on any branch so auto-discovery can still work for
|
|
63
|
+
* sparse branches or customer histories where the previous success is on another branch.
|
|
64
|
+
*
|
|
65
|
+
* @returns Previous build id, or undefined when no valid candidate is found.
|
|
66
|
+
*/
|
|
67
|
+
async findPreviousSuccessfulBuild(teamProject, definitionId, toBuildId, targetPipeline, searchPrevPipelineFromDifferentCommit) {
|
|
68
|
+
const targetRepo = this.getPrimaryPipelineRepository(targetPipeline);
|
|
69
|
+
const targetBranch = this.normalizeBranchName(targetRepo === null || targetRepo === void 0 ? void 0 : targetRepo.refName);
|
|
70
|
+
if (targetBranch) {
|
|
71
|
+
const sameBranchResult = await this.findPreviousSuccessfulBuildPage(teamProject, definitionId, toBuildId, targetPipeline, searchPrevPipelineFromDifferentCommit, targetBranch);
|
|
72
|
+
if (sameBranchResult.status === 'failed') {
|
|
73
|
+
throw sameBranchResult.error;
|
|
74
|
+
}
|
|
75
|
+
if (sameBranchResult.status === 'found') {
|
|
76
|
+
return sameBranchResult.id;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const anyBranchResult = await this.findPreviousSuccessfulBuildPage(teamProject, definitionId, toBuildId, targetPipeline, searchPrevPipelineFromDifferentCommit);
|
|
80
|
+
if (anyBranchResult.status === 'failed') {
|
|
81
|
+
throw anyBranchResult.error;
|
|
82
|
+
}
|
|
83
|
+
return anyBranchResult.status === 'found' ? anyBranchResult.id : undefined;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Pages the Builds API until a valid previous successful build is found.
|
|
87
|
+
*
|
|
88
|
+
* The API query already asks for completed/succeeded builds ordered by finish time, but
|
|
89
|
+
* candidates are validated locally as well to protect callers from incomplete API data,
|
|
90
|
+
* mocks, or future response-shape differences.
|
|
91
|
+
*/
|
|
92
|
+
async findPreviousSuccessfulBuildPage(teamProject, definitionId, toBuildId, targetPipeline, searchPrevPipelineFromDifferentCommit, branchName) {
|
|
93
|
+
const targetRepo = this.getPrimaryPipelineRepository(targetPipeline);
|
|
94
|
+
let continuationToken = undefined;
|
|
95
|
+
let pageCount = 0;
|
|
96
|
+
do {
|
|
97
|
+
let url = `${this.orgUrl}${teamProject}/_apis/build/builds?definitions=${encodeURIComponent(String(definitionId))}&resultFilter=succeeded&statusFilter=completed&queryOrder=finishTimeDescending&$top=200&api-version=6.0`;
|
|
98
|
+
if (branchName) {
|
|
99
|
+
url += `&branchName=${encodeURIComponent(branchName)}`;
|
|
100
|
+
}
|
|
101
|
+
if (continuationToken) {
|
|
102
|
+
url += `&continuationToken=${encodeURIComponent(continuationToken)}`;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const { data, headers } = await tfs_1.TFSServices.getItemContentWithHeaders(url, this.token, 'get', null, null);
|
|
106
|
+
pageCount++;
|
|
107
|
+
const builds = (data === null || data === void 0 ? void 0 : data.value) || [];
|
|
108
|
+
const match = builds.find((build) => this.isMatchingPreviousBuild(build, targetRepo, toBuildId, searchPrevPipelineFromDifferentCommit, branchName));
|
|
109
|
+
if (match === null || match === void 0 ? void 0 : match.id) {
|
|
110
|
+
return { status: 'found', id: Number(match.id) };
|
|
111
|
+
}
|
|
112
|
+
continuationToken = this.getContinuationToken(headers);
|
|
113
|
+
if (continuationToken && pageCount >= MAX_DISCOVERY_PAGES) {
|
|
114
|
+
return {
|
|
115
|
+
status: 'failed',
|
|
116
|
+
error: new Error(`Pipeline discovery exceeded ${MAX_DISCOVERY_PAGES} pages`),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
logger_1.default.warn(`Could not fetch previous successful builds: ${this.getErrorMessage(err)}`);
|
|
122
|
+
return { status: 'failed', error: err };
|
|
123
|
+
}
|
|
124
|
+
} while (continuationToken);
|
|
125
|
+
return { status: 'not_found' };
|
|
126
|
+
}
|
|
127
|
+
getContinuationToken(headers) {
|
|
128
|
+
return (headers === null || headers === void 0 ? void 0 : headers['x-ms-continuationtoken']) || (headers === null || headers === void 0 ? void 0 : headers['x-ms-continuation-token']) || undefined;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Extracts the primary repository from a pipeline run details response.
|
|
132
|
+
*
|
|
133
|
+
* Newer YAML runs expose repository resources as an array with a `self` entry, while some
|
|
134
|
+
* designer/classic pipeline responses expose the repository under `__designer_repo`.
|
|
135
|
+
*/
|
|
136
|
+
getPrimaryPipelineRepository(pipeline) {
|
|
137
|
+
var _a, _b;
|
|
138
|
+
const repositories = (_a = pipeline === null || pipeline === void 0 ? void 0 : pipeline.resources) === null || _a === void 0 ? void 0 : _a.repositories;
|
|
139
|
+
if (!repositories)
|
|
140
|
+
return undefined;
|
|
141
|
+
return ((_b = repositories[0]) === null || _b === void 0 ? void 0 : _b.self) || repositories.__designer_repo;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Validates a Builds API candidate against the target run.
|
|
145
|
+
*
|
|
146
|
+
* A candidate must be older than the target, completed successfully, from the same
|
|
147
|
+
* repository, and optionally from the required branch. When the caller asks for a
|
|
148
|
+
* different commit, candidates with the same source version are excluded.
|
|
149
|
+
*/
|
|
150
|
+
isMatchingPreviousBuild(build, targetRepo, toBuildId, searchPrevPipelineFromDifferentCommit, requiredBranch) {
|
|
151
|
+
var _a, _b;
|
|
152
|
+
const buildId = Number(build === null || build === void 0 ? void 0 : build.id);
|
|
153
|
+
if (!Number.isFinite(buildId) || buildId >= toBuildId)
|
|
154
|
+
return false;
|
|
155
|
+
if ((build === null || build === void 0 ? void 0 : build.status) && build.status !== 'completed')
|
|
156
|
+
return false;
|
|
157
|
+
if ((build === null || build === void 0 ? void 0 : build.result) && build.result !== 'succeeded')
|
|
158
|
+
return false;
|
|
159
|
+
const buildRepoId = (_a = build === null || build === void 0 ? void 0 : build.repository) === null || _a === void 0 ? void 0 : _a.id;
|
|
160
|
+
const targetRepoId = (_b = targetRepo === null || targetRepo === void 0 ? void 0 : targetRepo.repository) === null || _b === void 0 ? void 0 : _b.id;
|
|
161
|
+
if (!buildRepoId || !targetRepoId || buildRepoId !== targetRepoId)
|
|
162
|
+
return false;
|
|
163
|
+
if (requiredBranch && (build === null || build === void 0 ? void 0 : build.sourceBranch) !== requiredBranch)
|
|
164
|
+
return false;
|
|
165
|
+
if ((build === null || build === void 0 ? void 0 : build.sourceVersion) && (targetRepo === null || targetRepo === void 0 ? void 0 : targetRepo.version) && build.sourceVersion === targetRepo.version) {
|
|
166
|
+
return !searchPrevPipelineFromDifferentCommit;
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Finds the previous successful release/deployment for an SVD release range.
|
|
172
|
+
*
|
|
173
|
+
* Release discovery pages the Release List API and expands environments so candidates can
|
|
174
|
+
* be validated by deployment status. A matching release must be older than the target,
|
|
175
|
+
* active, and have at least one succeeded environment.
|
|
176
|
+
*
|
|
177
|
+
* @returns Previous release id, or undefined when no valid candidate is found.
|
|
178
|
+
*/
|
|
179
|
+
async findPreviousSuccessfulRelease(projectName, definitionId, toReleaseId) {
|
|
180
|
+
let result = await this.findSuccessfulReleasePage(projectName, definitionId, '7.1', (release) => this.isPreviousSuccessfulRelease(release, toReleaseId), 'previous');
|
|
181
|
+
if (result.status === 'failed' && this.isUnsupportedApiVersionError(result.error)) {
|
|
182
|
+
result = await this.findSuccessfulReleasePage(projectName, definitionId, '6.0', (release) => this.isPreviousSuccessfulRelease(release, toReleaseId), 'previous');
|
|
183
|
+
}
|
|
184
|
+
if (result.status === 'failed') {
|
|
185
|
+
throw result.error;
|
|
186
|
+
}
|
|
187
|
+
return result.status === 'found' ? result.id : undefined;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Finds the latest successful release/deployment for an SVD release range.
|
|
191
|
+
*
|
|
192
|
+
* Used when the release template omits `toReleaseId`. The same candidate rule is used as
|
|
193
|
+
* previous-release discovery: active release with at least one succeeded environment.
|
|
194
|
+
*
|
|
195
|
+
* @returns Latest release id, or undefined when no valid candidate is found.
|
|
196
|
+
*/
|
|
197
|
+
async findLatestSuccessfulRelease(projectName, definitionId) {
|
|
198
|
+
let result = await this.findSuccessfulReleasePage(projectName, definitionId, '7.1', (release) => this.isSuccessfulRelease(release), 'latest');
|
|
199
|
+
if (result.status === 'failed' && this.isUnsupportedApiVersionError(result.error)) {
|
|
200
|
+
result = await this.findSuccessfulReleasePage(projectName, definitionId, '6.0', (release) => this.isSuccessfulRelease(release), 'latest');
|
|
201
|
+
}
|
|
202
|
+
if (result.status === 'failed') {
|
|
203
|
+
throw result.error;
|
|
204
|
+
}
|
|
205
|
+
return result.status === 'found' ? result.id : undefined;
|
|
206
|
+
}
|
|
207
|
+
async findSuccessfulReleasePage(projectName, definitionId, apiVersion, isMatch, discoveryLabel) {
|
|
208
|
+
let baseUrl = `${this.orgUrl}${projectName}/_apis/release/releases?definitionId=${encodeURIComponent(String(definitionId))}&queryOrder=descending&$top=200&$expand=environments&api-version=${apiVersion}`;
|
|
209
|
+
if (baseUrl.startsWith('https://dev.azure.com')) {
|
|
210
|
+
baseUrl = baseUrl.replace('https://dev.azure.com', 'https://vsrm.dev.azure.com');
|
|
211
|
+
}
|
|
212
|
+
let continuationToken = undefined;
|
|
213
|
+
let pageCount = 0;
|
|
214
|
+
do {
|
|
215
|
+
let url = baseUrl;
|
|
216
|
+
if (continuationToken) {
|
|
217
|
+
url += `&continuationToken=${encodeURIComponent(continuationToken)}`;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const { data, headers } = await tfs_1.TFSServices.getItemContentWithHeaders(url, this.token, 'get', null, null);
|
|
221
|
+
pageCount++;
|
|
222
|
+
const releases = (data === null || data === void 0 ? void 0 : data.value) || [];
|
|
223
|
+
const match = releases.find(isMatch);
|
|
224
|
+
if (match === null || match === void 0 ? void 0 : match.id) {
|
|
225
|
+
return { status: 'found', id: Number(match.id) };
|
|
226
|
+
}
|
|
227
|
+
continuationToken = this.getContinuationToken(headers);
|
|
228
|
+
if (continuationToken && pageCount >= MAX_DISCOVERY_PAGES) {
|
|
229
|
+
return {
|
|
230
|
+
status: 'failed',
|
|
231
|
+
error: new Error(`Release discovery exceeded ${MAX_DISCOVERY_PAGES} pages`),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
logger_1.default.warn(`Could not fetch ${discoveryLabel} successful releases: ${this.getErrorMessage(err)}`);
|
|
237
|
+
return { status: 'failed', error: err };
|
|
238
|
+
}
|
|
239
|
+
} while (continuationToken);
|
|
240
|
+
return { status: 'not_found' };
|
|
241
|
+
}
|
|
242
|
+
isUnsupportedApiVersionError(err) {
|
|
243
|
+
var _a;
|
|
244
|
+
const error = err;
|
|
245
|
+
const responseData = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
246
|
+
const message = [
|
|
247
|
+
error === null || error === void 0 ? void 0 : error.message,
|
|
248
|
+
responseData === null || responseData === void 0 ? void 0 : responseData.message,
|
|
249
|
+
typeof responseData === 'string' ? responseData : JSON.stringify(responseData || ''),
|
|
250
|
+
]
|
|
251
|
+
.join(' ')
|
|
252
|
+
.toLowerCase();
|
|
253
|
+
return (message.includes('api-version') &&
|
|
254
|
+
(message.includes('unsupported') ||
|
|
255
|
+
message.includes('not support') ||
|
|
256
|
+
message.includes('not supported')));
|
|
257
|
+
}
|
|
258
|
+
getErrorMessage(err) {
|
|
259
|
+
return err instanceof Error ? err.message : String(err);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Validates a release candidate for auto-discovery.
|
|
263
|
+
*
|
|
264
|
+
* A release is considered successful for this SVD range purpose when at least one
|
|
265
|
+
* deployment environment succeeded. This mirrors the existing release artifact flow,
|
|
266
|
+
* where a release may contain multiple environments but still provide usable artifacts.
|
|
267
|
+
*/
|
|
268
|
+
isPreviousSuccessfulRelease(release, toReleaseId) {
|
|
269
|
+
const releaseId = Number(release === null || release === void 0 ? void 0 : release.id);
|
|
270
|
+
if (!Number.isFinite(releaseId) || releaseId >= toReleaseId)
|
|
271
|
+
return false;
|
|
272
|
+
return this.isSuccessfulRelease(release);
|
|
273
|
+
}
|
|
274
|
+
isSuccessfulRelease(release) {
|
|
275
|
+
const releaseId = Number(release === null || release === void 0 ? void 0 : release.id);
|
|
276
|
+
if (!Number.isFinite(releaseId))
|
|
277
|
+
return false;
|
|
278
|
+
if ((release === null || release === void 0 ? void 0 : release.status) && String(release.status).toLowerCase() !== 'active')
|
|
279
|
+
return false;
|
|
280
|
+
const environments = Array.isArray(release === null || release === void 0 ? void 0 : release.environments) ? release.environments : [];
|
|
281
|
+
return environments.some((environment) => {
|
|
282
|
+
return String((environment === null || environment === void 0 ? void 0 : environment.status) || '').toLowerCase() === 'succeeded';
|
|
283
|
+
});
|
|
284
|
+
}
|
|
36
285
|
/**
|
|
37
286
|
* Determines if a pipeline run is invalid based on various conditions.
|
|
38
287
|
*
|
|
@@ -406,7 +655,8 @@ class PipelinesDataProvider {
|
|
|
406
655
|
}
|
|
407
656
|
const pipelineEntries = Object.entries(inPipeline.resources.pipelines);
|
|
408
657
|
logger_1.default.debug(`getPipelineResourcePipelinesFromObject: resolving ${pipelineEntries.length} pipeline resources`);
|
|
409
|
-
|
|
658
|
+
const concurrencyLimit = pLimit(8);
|
|
659
|
+
await Promise.all(pipelineEntries.map(([resourcePipelineAlias, resource]) => concurrencyLimit(async () => {
|
|
410
660
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
411
661
|
const resourcePipelineObj = resource === null || resource === void 0 ? void 0 : resource.pipeline;
|
|
412
662
|
const pipelineIdCandidate = Number(resourcePipelineObj === null || resourcePipelineObj === void 0 ? void 0 : resourcePipelineObj.id);
|
|
@@ -476,7 +726,7 @@ class PipelinesDataProvider {
|
|
|
476
726
|
if (!resourcePipelinesByKey.has(key))
|
|
477
727
|
resourcePipelinesByKey.set(key, resourcePipelineToAdd);
|
|
478
728
|
}
|
|
479
|
-
}));
|
|
729
|
+
})));
|
|
480
730
|
return [...resourcePipelinesByKey.values()];
|
|
481
731
|
}
|
|
482
732
|
/**
|
|
@@ -621,6 +871,12 @@ class PipelinesDataProvider {
|
|
|
621
871
|
logger_1.default.error(`Could not fetch Pipeline Run History: ${err.message}`);
|
|
622
872
|
}
|
|
623
873
|
}
|
|
874
|
+
/**
|
|
875
|
+
* Fetches the first page of release history for a definition.
|
|
876
|
+
*
|
|
877
|
+
* Kept for backward compatibility with existing consumers. New range/discovery flows
|
|
878
|
+
* should prefer GetAllReleaseHistory when full release history is required.
|
|
879
|
+
*/
|
|
624
880
|
async GetReleaseHistory(projectName, definitionId) {
|
|
625
881
|
let url = `${this.orgUrl}${projectName}/_apis/release/releases?definitionId=${definitionId}&$top=200`;
|
|
626
882
|
if (url.startsWith('https://dev.azure.com')) {
|
|
@@ -630,9 +886,16 @@ class PipelinesDataProvider {
|
|
|
630
886
|
return res;
|
|
631
887
|
}
|
|
632
888
|
/**
|
|
633
|
-
*
|
|
889
|
+
* Fetches all releases for a definition using continuation tokens.
|
|
890
|
+
*
|
|
891
|
+
* This is used by SVD release range handling because the requested from/to releases may be
|
|
892
|
+
* older than the first page returned by Azure DevOps.
|
|
893
|
+
*
|
|
894
|
+
* @param range When provided, pagination stops as soon as both fromId and toId are present in
|
|
895
|
+
* the accumulated results. ADO returns releases in descending id order, so once the smallest
|
|
896
|
+
* id seen is <= the lower requested id both endpoints are guaranteed to be loaded.
|
|
634
897
|
*/
|
|
635
|
-
async GetAllReleaseHistory(projectName, definitionId) {
|
|
898
|
+
async GetAllReleaseHistory(projectName, definitionId, range) {
|
|
636
899
|
let baseUrl = `${this.orgUrl}${projectName}/_apis/release/releases?definitionId=${definitionId}&api-version=6.0`;
|
|
637
900
|
if (baseUrl.startsWith('https://dev.azure.com')) {
|
|
638
901
|
baseUrl = baseUrl.replace('https://dev.azure.com', 'https://vsrm.dev.azure.com');
|
|
@@ -649,15 +912,24 @@ class PipelinesDataProvider {
|
|
|
649
912
|
const { data, headers } = await tfs_1.TFSServices.getItemContentWithHeaders(url, this.token, 'get', null, null);
|
|
650
913
|
const { value = [] } = data || {};
|
|
651
914
|
all.push(...value);
|
|
652
|
-
// Azure DevOps returns continuation token header for next page
|
|
653
|
-
continuationToken =
|
|
654
|
-
(headers === null || headers === void 0 ? void 0 : headers['x-ms-continuationtoken']) || (headers === null || headers === void 0 ? void 0 : headers['x-ms-continuation-token']) || undefined;
|
|
655
915
|
page++;
|
|
656
916
|
logger_1.default.debug(`GetAllReleaseHistory: fetched page ${page}, cumulative ${all.length} releases`);
|
|
917
|
+
// Stop-early: once the smallest id in the accumulated list is <= the lower
|
|
918
|
+
// requested id, both endpoints of the range are present.
|
|
919
|
+
if (range && all.length > 0) {
|
|
920
|
+
const lowerBound = Math.min(range.fromId, range.toId);
|
|
921
|
+
const minId = Math.min(...all.map((r) => Number(r.id)));
|
|
922
|
+
if (minId <= lowerBound) {
|
|
923
|
+
logger_1.default.debug(`GetAllReleaseHistory: stop-early at page ${page} (minId=${minId} <= lowerBound=${lowerBound})`);
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
// Azure DevOps returns continuation token header for next page
|
|
928
|
+
continuationToken = this.getContinuationToken(headers);
|
|
657
929
|
}
|
|
658
930
|
catch (err) {
|
|
659
931
|
logger_1.default.error(`GetAllReleaseHistory failed: ${err.message}`);
|
|
660
|
-
|
|
932
|
+
throw err;
|
|
661
933
|
}
|
|
662
934
|
} while (continuationToken);
|
|
663
935
|
return { count: all.length, value: all };
|