@paklo/core 0.1.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/LICENSE +21 -0
- package/dist/browser/defineProperty-ie4tC-F5.js +43 -0
- package/dist/browser/environment-DinhzwQn.js +139 -0
- package/dist/browser/environment-DinhzwQn.js.map +1 -0
- package/dist/browser/environment.d.ts +33 -0
- package/dist/browser/environment.js +3 -0
- package/dist/browser/github.d.ts +151 -0
- package/dist/browser/github.js +199 -0
- package/dist/browser/github.js.map +1 -0
- package/dist/browser/http.d.ts +121 -0
- package/dist/browser/http.js +248 -0
- package/dist/browser/http.js.map +1 -0
- package/dist/browser/logger-B7HLv660.js +31 -0
- package/dist/browser/logger-B7HLv660.js.map +1 -0
- package/dist/browser/logger.d.ts +23 -0
- package/dist/browser/logger.js +4 -0
- package/dist/browser/shared-data.d.ts +22 -0
- package/dist/browser/shared-data.js +23 -0
- package/dist/browser/shared-data.js.map +1 -0
- package/dist/browser/usage.d.ts +99 -0
- package/dist/browser/usage.js +383 -0
- package/dist/browser/usage.js.map +1 -0
- package/dist/node/azure.d.ts +338 -0
- package/dist/node/azure.js +735 -0
- package/dist/node/azure.js.map +1 -0
- package/dist/node/dependabot-BteoKZVy.js +547 -0
- package/dist/node/dependabot-BteoKZVy.js.map +1 -0
- package/dist/node/dependabot.d.ts +3 -0
- package/dist/node/dependabot.js +6 -0
- package/dist/node/environment-DX5CD-dD.js +138 -0
- package/dist/node/environment-DX5CD-dD.js.map +1 -0
- package/dist/node/environment.d.ts +33 -0
- package/dist/node/environment.js +3 -0
- package/dist/node/github.d.ts +2 -0
- package/dist/node/github.js +198 -0
- package/dist/node/github.js.map +1 -0
- package/dist/node/http-BG_-s47I.js +245 -0
- package/dist/node/http-BG_-s47I.js.map +1 -0
- package/dist/node/http.d.ts +121 -0
- package/dist/node/http.js +4 -0
- package/dist/node/index-3wZw74Ah.d.ts +151 -0
- package/dist/node/index-DP9JfUPG.d.ts +1761 -0
- package/dist/node/job-Crr4kh3e.js +451 -0
- package/dist/node/job-Crr4kh3e.js.map +1 -0
- package/dist/node/logger-bWnHxtAf.js +31 -0
- package/dist/node/logger-bWnHxtAf.js.map +1 -0
- package/dist/node/logger.d.ts +23 -0
- package/dist/node/logger.js +4 -0
- package/dist/node/shared-data.d.ts +22 -0
- package/dist/node/shared-data.js +23 -0
- package/dist/node/shared-data.js.map +1 -0
- package/dist/node/usage.d.ts +99 -0
- package/dist/node/usage.js +48 -0
- package/dist/node/usage.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import "./environment-DX5CD-dD.js";
|
|
2
|
+
import { n as logger } from "./logger-bWnHxtAf.js";
|
|
3
|
+
import { r as isErrorTemporaryFailure, t as HttpRequestError } from "./http-BG_-s47I.js";
|
|
4
|
+
import { P as parseDependabotConfig, c as DependabotExistingGroupPRSchema, j as POSSIBLE_CONFIG_FILE_PATHS, l as DependabotExistingPRSchema } from "./job-Crr4kh3e.js";
|
|
5
|
+
import "./dependabot-BteoKZVy.js";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
|
|
10
|
+
//#region src/azure/types.ts
|
|
11
|
+
let VersionControlChangeType = /* @__PURE__ */ function(VersionControlChangeType$1) {
|
|
12
|
+
VersionControlChangeType$1[VersionControlChangeType$1["None"] = 0] = "None";
|
|
13
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Add"] = 1] = "Add";
|
|
14
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Edit"] = 2] = "Edit";
|
|
15
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Encoding"] = 4] = "Encoding";
|
|
16
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Rename"] = 8] = "Rename";
|
|
17
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Delete"] = 16] = "Delete";
|
|
18
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Undelete"] = 32] = "Undelete";
|
|
19
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Branch"] = 64] = "Branch";
|
|
20
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Merge"] = 128] = "Merge";
|
|
21
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Lock"] = 256] = "Lock";
|
|
22
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Rollback"] = 512] = "Rollback";
|
|
23
|
+
VersionControlChangeType$1[VersionControlChangeType$1["SourceRename"] = 1024] = "SourceRename";
|
|
24
|
+
VersionControlChangeType$1[VersionControlChangeType$1["TargetRename"] = 2048] = "TargetRename";
|
|
25
|
+
VersionControlChangeType$1[VersionControlChangeType$1["Property"] = 4096] = "Property";
|
|
26
|
+
VersionControlChangeType$1[VersionControlChangeType$1["All"] = 8191] = "All";
|
|
27
|
+
return VersionControlChangeType$1;
|
|
28
|
+
}({});
|
|
29
|
+
let GitPullRequestMergeStrategy = /* @__PURE__ */ function(GitPullRequestMergeStrategy$1) {
|
|
30
|
+
GitPullRequestMergeStrategy$1[GitPullRequestMergeStrategy$1["NoFastForward"] = 1] = "NoFastForward";
|
|
31
|
+
GitPullRequestMergeStrategy$1[GitPullRequestMergeStrategy$1["Squash"] = 2] = "Squash";
|
|
32
|
+
GitPullRequestMergeStrategy$1[GitPullRequestMergeStrategy$1["Rebase"] = 3] = "Rebase";
|
|
33
|
+
GitPullRequestMergeStrategy$1[GitPullRequestMergeStrategy$1["RebaseMerge"] = 4] = "RebaseMerge";
|
|
34
|
+
return GitPullRequestMergeStrategy$1;
|
|
35
|
+
}({});
|
|
36
|
+
let CommentThreadStatus = /* @__PURE__ */ function(CommentThreadStatus$1) {
|
|
37
|
+
CommentThreadStatus$1[CommentThreadStatus$1["Unknown"] = 0] = "Unknown";
|
|
38
|
+
CommentThreadStatus$1[CommentThreadStatus$1["Active"] = 1] = "Active";
|
|
39
|
+
CommentThreadStatus$1[CommentThreadStatus$1["Fixed"] = 2] = "Fixed";
|
|
40
|
+
CommentThreadStatus$1[CommentThreadStatus$1["WontFix"] = 3] = "WontFix";
|
|
41
|
+
CommentThreadStatus$1[CommentThreadStatus$1["Closed"] = 4] = "Closed";
|
|
42
|
+
CommentThreadStatus$1[CommentThreadStatus$1["ByDesign"] = 5] = "ByDesign";
|
|
43
|
+
CommentThreadStatus$1[CommentThreadStatus$1["Pending"] = 6] = "Pending";
|
|
44
|
+
return CommentThreadStatus$1;
|
|
45
|
+
}({});
|
|
46
|
+
let CommentType = /* @__PURE__ */ function(CommentType$1) {
|
|
47
|
+
CommentType$1[CommentType$1["Unknown"] = 0] = "Unknown";
|
|
48
|
+
CommentType$1[CommentType$1["Text"] = 1] = "Text";
|
|
49
|
+
CommentType$1[CommentType$1["CodeChange"] = 2] = "CodeChange";
|
|
50
|
+
CommentType$1[CommentType$1["System"] = 3] = "System";
|
|
51
|
+
return CommentType$1;
|
|
52
|
+
}({});
|
|
53
|
+
let ItemContentType = /* @__PURE__ */ function(ItemContentType$1) {
|
|
54
|
+
ItemContentType$1[ItemContentType$1["RawText"] = 0] = "RawText";
|
|
55
|
+
ItemContentType$1[ItemContentType$1["Base64Encoded"] = 1] = "Base64Encoded";
|
|
56
|
+
return ItemContentType$1;
|
|
57
|
+
}({});
|
|
58
|
+
let PullRequestAsyncStatus = /* @__PURE__ */ function(PullRequestAsyncStatus$1) {
|
|
59
|
+
PullRequestAsyncStatus$1[PullRequestAsyncStatus$1["NotSet"] = 0] = "NotSet";
|
|
60
|
+
PullRequestAsyncStatus$1[PullRequestAsyncStatus$1["Queued"] = 1] = "Queued";
|
|
61
|
+
PullRequestAsyncStatus$1[PullRequestAsyncStatus$1["Conflicts"] = 2] = "Conflicts";
|
|
62
|
+
PullRequestAsyncStatus$1[PullRequestAsyncStatus$1["Succeeded"] = 3] = "Succeeded";
|
|
63
|
+
PullRequestAsyncStatus$1[PullRequestAsyncStatus$1["RejectedByPolicy"] = 4] = "RejectedByPolicy";
|
|
64
|
+
PullRequestAsyncStatus$1[PullRequestAsyncStatus$1["Failure"] = 5] = "Failure";
|
|
65
|
+
return PullRequestAsyncStatus$1;
|
|
66
|
+
}({});
|
|
67
|
+
let PullRequestStatus = /* @__PURE__ */ function(PullRequestStatus$1) {
|
|
68
|
+
PullRequestStatus$1[PullRequestStatus$1["NotSet"] = 0] = "NotSet";
|
|
69
|
+
PullRequestStatus$1[PullRequestStatus$1["Active"] = 1] = "Active";
|
|
70
|
+
PullRequestStatus$1[PullRequestStatus$1["Abandoned"] = 2] = "Abandoned";
|
|
71
|
+
PullRequestStatus$1[PullRequestStatus$1["Completed"] = 3] = "Completed";
|
|
72
|
+
PullRequestStatus$1[PullRequestStatus$1["All"] = 4] = "All";
|
|
73
|
+
return PullRequestStatus$1;
|
|
74
|
+
}({});
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/azure/models.ts
|
|
78
|
+
/**
|
|
79
|
+
* Pull request property names used to store metadata about the pull request.
|
|
80
|
+
* https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-properties
|
|
81
|
+
*/
|
|
82
|
+
const DEVOPS_PR_PROPERTY_MICROSOFT_GIT_SOURCE_REF_NAME = "Microsoft.Git.PullRequest.SourceRefName";
|
|
83
|
+
const DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER = "Dependabot.PackageManager";
|
|
84
|
+
const DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES = "Dependabot.Dependencies";
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/azure/utils.ts
|
|
88
|
+
function normalizeFilePath(path$1) {
|
|
89
|
+
return path$1?.replace(/\\/g, "/")?.replace(/^\.\//, "/")?.replace(/^([^/])/, "/$1");
|
|
90
|
+
}
|
|
91
|
+
function normalizeBranchName(branch) {
|
|
92
|
+
return branch?.replace(/^refs\/heads\//i, "");
|
|
93
|
+
}
|
|
94
|
+
const DependenciesPrPropertySchema = DependabotExistingPRSchema.array().or(DependabotExistingGroupPRSchema);
|
|
95
|
+
function buildPullRequestProperties(packageManager, dependencies) {
|
|
96
|
+
return [{
|
|
97
|
+
name: DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER,
|
|
98
|
+
value: packageManager
|
|
99
|
+
}, {
|
|
100
|
+
name: DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES,
|
|
101
|
+
value: JSON.stringify(dependencies)
|
|
102
|
+
}];
|
|
103
|
+
}
|
|
104
|
+
function parsePullRequestProperties(pullRequests, packageManager) {
|
|
105
|
+
return Object.fromEntries(pullRequests.filter((pr) => {
|
|
106
|
+
return pr.properties?.find((p) => p.name === DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER && (packageManager === null || p.value === packageManager));
|
|
107
|
+
}).map((pr) => {
|
|
108
|
+
return [pr.id, DependenciesPrPropertySchema.parse(JSON.parse(pr.properties.find((p) => p.name === DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES).value))];
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
function getPullRequestForDependencyNames(existingPullRequests, packageManager, dependencyNames) {
|
|
112
|
+
return existingPullRequests.find((pr) => {
|
|
113
|
+
return pr.properties?.find((p) => p.name === DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER && p.value === packageManager) && pr.properties?.find((p) => p.name === DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES && areEqual(getDependencyNames(DependenciesPrPropertySchema.parse(JSON.parse(p.value))), dependencyNames));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function getDependencyNames(dependencies) {
|
|
117
|
+
return (Array.isArray(dependencies) ? dependencies : dependencies.dependencies).map((dep) => dep["dependency-name"]?.toString());
|
|
118
|
+
}
|
|
119
|
+
function areEqual(a, b) {
|
|
120
|
+
if (a.length !== b.length) return false;
|
|
121
|
+
return a.every((name) => b.includes(name));
|
|
122
|
+
}
|
|
123
|
+
function getPullRequestChangedFilesForOutputData(data) {
|
|
124
|
+
return data["updated-dependency-files"].filter((file) => file.type === "file").map((file) => {
|
|
125
|
+
let changeType = VersionControlChangeType.None;
|
|
126
|
+
if (file.deleted === true) changeType = VersionControlChangeType.Delete;
|
|
127
|
+
else if (file.operation === "update") changeType = VersionControlChangeType.Edit;
|
|
128
|
+
else changeType = VersionControlChangeType.Add;
|
|
129
|
+
return {
|
|
130
|
+
changeType,
|
|
131
|
+
path: path.join(file.directory, file.name),
|
|
132
|
+
content: file.content,
|
|
133
|
+
encoding: file.content_encoding ?? "utf8"
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function getPullRequestCloseReasonForOutputData(data) {
|
|
138
|
+
const leadDependencyName = data["dependency-names"][0];
|
|
139
|
+
let reason;
|
|
140
|
+
switch (data.reason) {
|
|
141
|
+
case "dependencies_changed":
|
|
142
|
+
reason = `Looks like the dependencies have changed`;
|
|
143
|
+
break;
|
|
144
|
+
case "dependency_group_empty":
|
|
145
|
+
reason = `Looks like the dependencies in this group are now empty`;
|
|
146
|
+
break;
|
|
147
|
+
case "dependency_removed":
|
|
148
|
+
reason = `Looks like ${leadDependencyName} is no longer a dependency`;
|
|
149
|
+
break;
|
|
150
|
+
case "up_to_date":
|
|
151
|
+
reason = `Looks like ${leadDependencyName} is up-to-date now`;
|
|
152
|
+
break;
|
|
153
|
+
case "update_no_longer_possible":
|
|
154
|
+
reason = `Looks like ${leadDependencyName} can no longer be updated`;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (reason && reason.length > 0) reason += ", so this is no longer needed.";
|
|
158
|
+
return reason;
|
|
159
|
+
}
|
|
160
|
+
function getPullRequestDependenciesPropertyValueForOutputData(data) {
|
|
161
|
+
const dependencies = data.dependencies?.map((dep) => {
|
|
162
|
+
return {
|
|
163
|
+
"dependency-name": dep.name,
|
|
164
|
+
"dependency-version": dep.version,
|
|
165
|
+
directory: dep.directory
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
const dependencyGroupName = data["dependency-group"]?.name;
|
|
169
|
+
if (!dependencyGroupName) return dependencies;
|
|
170
|
+
return {
|
|
171
|
+
"dependency-group-name": dependencyGroupName,
|
|
172
|
+
dependencies
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function getPullRequestDescription(packageManager, body, dependencies) {
|
|
176
|
+
let header = "";
|
|
177
|
+
const footer = "";
|
|
178
|
+
const description = (body || "").replace(new RegExp(decodeURIComponent("%EF%BF%BD%EF%BF%BD%EF%BF%BD"), "g"), "");
|
|
179
|
+
if (dependencies.length === 1) {
|
|
180
|
+
const compatibilityScoreBadges = dependencies.map((dep) => {
|
|
181
|
+
return ``;
|
|
182
|
+
});
|
|
183
|
+
header += `${compatibilityScoreBadges.join(" ")}\n\n`;
|
|
184
|
+
}
|
|
185
|
+
const maxDescriptionLengthAfterHeaderAndFooter = 4e3 - header.length - 0;
|
|
186
|
+
return `${header}${description.substring(0, maxDescriptionLengthAfterHeaderAndFooter)}${footer}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/azure/client.ts
|
|
191
|
+
/** Azure DevOps REST API client. */
|
|
192
|
+
var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
|
|
193
|
+
organisationApiUrl;
|
|
194
|
+
identityApiUrl;
|
|
195
|
+
accessToken;
|
|
196
|
+
debug;
|
|
197
|
+
authenticatedUserId;
|
|
198
|
+
resolvedUserIds;
|
|
199
|
+
static API_VERSION = "5.0";
|
|
200
|
+
static API_VERSION_PREVIEW = "5.0-preview";
|
|
201
|
+
constructor(url, accessToken, debug = false) {
|
|
202
|
+
const organisationApiUrl = url.url.toString();
|
|
203
|
+
this.organisationApiUrl = organisationApiUrl.replace(/\/$/, "");
|
|
204
|
+
this.identityApiUrl = getIdentityApiUrl(organisationApiUrl).replace(/\/$/, "");
|
|
205
|
+
this.accessToken = accessToken;
|
|
206
|
+
this.debug = debug;
|
|
207
|
+
this.resolvedUserIds = {};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get the identity of the authenticated user.
|
|
211
|
+
* @returns
|
|
212
|
+
*/
|
|
213
|
+
async getUserId() {
|
|
214
|
+
if (!this.authenticatedUserId) {
|
|
215
|
+
this.authenticatedUserId = (await this.restApiGet(`${this.organisationApiUrl}/_apis/connectiondata`, void 0, AzureDevOpsWebApiClient.API_VERSION_PREVIEW))?.authenticatedUser?.id;
|
|
216
|
+
if (!this.authenticatedUserId) throw new Error("Failed to get authenticated user ID");
|
|
217
|
+
}
|
|
218
|
+
return this.authenticatedUserId;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get the identity id from a user name, email, or group name.
|
|
222
|
+
* Requires scope "Identity (Read)" (vso.identity).
|
|
223
|
+
* @param userNameEmailOrGroupName
|
|
224
|
+
* @returns
|
|
225
|
+
*/
|
|
226
|
+
async resolveIdentityId(userNameEmailOrGroupName) {
|
|
227
|
+
if (this.resolvedUserIds[userNameEmailOrGroupName]) return this.resolvedUserIds[userNameEmailOrGroupName];
|
|
228
|
+
try {
|
|
229
|
+
const identities = await this.restApiGet(`${this.identityApiUrl}/_apis/identities`, {
|
|
230
|
+
searchFilter: "General",
|
|
231
|
+
filterValue: userNameEmailOrGroupName,
|
|
232
|
+
queryMembership: "None"
|
|
233
|
+
});
|
|
234
|
+
if (!identities?.value || identities.value.length === 0) return;
|
|
235
|
+
this.resolvedUserIds[userNameEmailOrGroupName] = identities.value[0]?.id;
|
|
236
|
+
return this.resolvedUserIds[userNameEmailOrGroupName];
|
|
237
|
+
} catch (e) {
|
|
238
|
+
logger.error(`Failed to resolve user id: ${e}`);
|
|
239
|
+
logger.debug(e);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get the default branch for a repository.
|
|
245
|
+
* Requires scope "Code (Read)" (vso.code).
|
|
246
|
+
* @param project
|
|
247
|
+
* @param repository
|
|
248
|
+
* @returns
|
|
249
|
+
*/
|
|
250
|
+
async getDefaultBranch(project, repository) {
|
|
251
|
+
try {
|
|
252
|
+
const repo = await this.restApiGet(`${this.organisationApiUrl}/${project}/_apis/git/repositories/${repository}`);
|
|
253
|
+
if (!repo) throw new Error(`Repository '${project}/${repository}' not found`);
|
|
254
|
+
return normalizeBranchName(repo.defaultBranch);
|
|
255
|
+
} catch (e) {
|
|
256
|
+
logger.error(`Failed to get default branch for '${project}/${repository}': ${e}`);
|
|
257
|
+
logger.debug(e);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Get the list of branch names for a repository.
|
|
263
|
+
* Requires scope "Code (Read)" (vso.code).
|
|
264
|
+
* @param project
|
|
265
|
+
* @param repository
|
|
266
|
+
* @returns
|
|
267
|
+
*/
|
|
268
|
+
async getBranchNames(project, repository) {
|
|
269
|
+
try {
|
|
270
|
+
const refs = await this.restApiGet(`${this.organisationApiUrl}/${project}/_apis/git/repositories/${repository}/refs`);
|
|
271
|
+
if (!refs) throw new Error(`Repository '${project}/${repository}' not found`);
|
|
272
|
+
return refs.value?.map((r) => normalizeBranchName(r.name)) || [];
|
|
273
|
+
} catch (e) {
|
|
274
|
+
logger.error(`Failed to list branch names for '${project}/${repository}': ${e}`);
|
|
275
|
+
logger.debug(e);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get the properties for all active pull request created by the supplied user.
|
|
281
|
+
* Requires scope "Code (Read)" (vso.code).
|
|
282
|
+
* @param project
|
|
283
|
+
* @param repository
|
|
284
|
+
* @param creator
|
|
285
|
+
* @returns
|
|
286
|
+
*/
|
|
287
|
+
async getActivePullRequestProperties(project, repository, creator) {
|
|
288
|
+
try {
|
|
289
|
+
const pullRequests = await this.restApiGet(`${this.organisationApiUrl}/${project}/_apis/git/repositories/${repository}/pullrequests`, {
|
|
290
|
+
"searchCriteria.creatorId": isGuid(creator) ? creator : await this.getUserId(),
|
|
291
|
+
"searchCriteria.status": "Active"
|
|
292
|
+
});
|
|
293
|
+
if (!pullRequests?.value || pullRequests.value.length === 0) return [];
|
|
294
|
+
return await Promise.all(pullRequests.value.map(async (pr) => {
|
|
295
|
+
const properties = await this.restApiGet(`${this.organisationApiUrl}/${project}/_apis/git/repositories/${repository}/pullrequests/${pr.pullRequestId}/properties`);
|
|
296
|
+
return {
|
|
297
|
+
id: pr.pullRequestId,
|
|
298
|
+
properties: Object.keys(properties?.value || {}).map((key) => {
|
|
299
|
+
return {
|
|
300
|
+
name: key,
|
|
301
|
+
value: properties.value[key]?.$value
|
|
302
|
+
};
|
|
303
|
+
}) || []
|
|
304
|
+
};
|
|
305
|
+
}));
|
|
306
|
+
} catch (e) {
|
|
307
|
+
logger.error(`Failed to list active pull request properties: ${e}`);
|
|
308
|
+
logger.debug(e);
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Create a new pull request.
|
|
314
|
+
* Requires scope "Code (Write)" (vso.code_write).
|
|
315
|
+
* Requires scope "Identity (Read)" (vso.identity), if assignees are specified.
|
|
316
|
+
* @param pr
|
|
317
|
+
* @returns
|
|
318
|
+
*/
|
|
319
|
+
async createPullRequest(pr) {
|
|
320
|
+
logger.info(`Creating pull request '${pr.title}'...`);
|
|
321
|
+
try {
|
|
322
|
+
const userId = await this.getUserId();
|
|
323
|
+
const reviewers = [];
|
|
324
|
+
if (pr.assignees && pr.assignees.length > 0) for (const assignee of pr.assignees) {
|
|
325
|
+
const identityId = isGuid(assignee) ? assignee : await this.resolveIdentityId(assignee);
|
|
326
|
+
if (identityId && !reviewers.some((r) => r.id === identityId)) reviewers.push({ id: identityId });
|
|
327
|
+
else logger.warn(`Unable to resolve assignee identity '${assignee}'`);
|
|
328
|
+
}
|
|
329
|
+
logger.info(` - Pushing ${pr.changes.length} file change(s) to branch '${pr.source.branch}'...`);
|
|
330
|
+
const push = await this.restApiPost(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pushes`, {
|
|
331
|
+
refUpdates: [{
|
|
332
|
+
name: `refs/heads/${pr.source.branch}`,
|
|
333
|
+
oldObjectId: pr.source.commit
|
|
334
|
+
}],
|
|
335
|
+
commits: [{
|
|
336
|
+
comment: pr.commitMessage,
|
|
337
|
+
author: pr.author,
|
|
338
|
+
changes: pr.changes.map((change) => {
|
|
339
|
+
return {
|
|
340
|
+
changeType: change.changeType,
|
|
341
|
+
item: { path: normalizeFilePath(change.path) },
|
|
342
|
+
newContent: {
|
|
343
|
+
content: Buffer.from(change.content, change.encoding).toString("base64"),
|
|
344
|
+
contentType: ItemContentType.Base64Encoded
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
})
|
|
348
|
+
}]
|
|
349
|
+
});
|
|
350
|
+
if (!push?.commits?.length) throw new Error("Failed to push changes to source branch, no commits were created");
|
|
351
|
+
logger.info(` - Pushed commit: ${push.commits.map((c) => c.commitId).join(", ")}.`);
|
|
352
|
+
logger.info(` - Creating pull request to merge '${pr.source.branch}' into '${pr.target.branch}'...`);
|
|
353
|
+
const pullRequest = await this.restApiPost(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests`, {
|
|
354
|
+
sourceRefName: `refs/heads/${pr.source.branch}`,
|
|
355
|
+
targetRefName: `refs/heads/${pr.target.branch}`,
|
|
356
|
+
title: pr.title,
|
|
357
|
+
description: pr.description,
|
|
358
|
+
reviewers,
|
|
359
|
+
workItemRefs: pr.workItems?.map((id) => ({ id })),
|
|
360
|
+
labels: pr.labels?.map((label) => ({ name: label }))
|
|
361
|
+
});
|
|
362
|
+
if (!pullRequest?.pullRequestId) throw new Error("Failed to create pull request, no pull request id was returned");
|
|
363
|
+
logger.info(` - Created pull request: #${pullRequest.pullRequestId}.`);
|
|
364
|
+
if (pr.properties && pr.properties.length > 0) {
|
|
365
|
+
logger.info(` - Adding dependency metadata to pull request properties...`);
|
|
366
|
+
if (!(await this.restApiPatch(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pullRequest.pullRequestId}/properties`, pr.properties.map((property) => {
|
|
367
|
+
return {
|
|
368
|
+
op: "add",
|
|
369
|
+
path: `/${property.name}`,
|
|
370
|
+
value: property.value
|
|
371
|
+
};
|
|
372
|
+
}), "application/json-patch+json"))?.count) throw new Error("Failed to add dependency metadata properties to pull request");
|
|
373
|
+
}
|
|
374
|
+
if (pr.autoComplete) {
|
|
375
|
+
logger.info(` - Updating auto-complete options...`);
|
|
376
|
+
const updatedPullRequest = await this.restApiPatch(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pullRequest.pullRequestId}`, {
|
|
377
|
+
autoCompleteSetBy: { id: userId },
|
|
378
|
+
completionOptions: {
|
|
379
|
+
autoCompleteIgnoreConfigIds: pr.autoComplete.ignorePolicyConfigIds,
|
|
380
|
+
deleteSourceBranch: true,
|
|
381
|
+
mergeCommitMessage: mergeCommitMessage(pullRequest.pullRequestId, pr.title, pr.description),
|
|
382
|
+
mergeStrategy: pr.autoComplete.mergeStrategy,
|
|
383
|
+
transitionWorkItems: false
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
if (!updatedPullRequest || updatedPullRequest.autoCompleteSetBy?.id !== userId) throw new Error("Failed to set auto-complete on pull request");
|
|
387
|
+
}
|
|
388
|
+
logger.info(` - Pull request was created successfully.`);
|
|
389
|
+
return pullRequest.pullRequestId;
|
|
390
|
+
} catch (e) {
|
|
391
|
+
logger.error(`Failed to create pull request: ${e}`);
|
|
392
|
+
logger.debug(e);
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Update a pull request.
|
|
398
|
+
* Requires scope "Code (Read & Write)" (vso.code, vso.code_write).
|
|
399
|
+
* @param pr
|
|
400
|
+
* @returns
|
|
401
|
+
*/
|
|
402
|
+
async updatePullRequest(pr) {
|
|
403
|
+
logger.info(`Updating pull request #${pr.pullRequestId}...`);
|
|
404
|
+
try {
|
|
405
|
+
const pullRequest = await this.restApiGet(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}`);
|
|
406
|
+
if (!pullRequest) throw new Error(`Pull request #${pr.pullRequestId} not found`);
|
|
407
|
+
if (pr.skipIfDraft && pullRequest.isDraft) {
|
|
408
|
+
logger.info(` - Skipping update as pull request is currently marked as a draft.`);
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
if (pr.skipIfCommitsFromAuthorsOtherThan) {
|
|
412
|
+
if ((await this.restApiGet(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}/commits`))?.value?.some((c) => c.author?.email !== pr.skipIfCommitsFromAuthorsOtherThan)) {
|
|
413
|
+
logger.info(` - Skipping update as pull request has been modified by another user.`);
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const stats = await this.restApiGet(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/stats/branches`, { name: normalizeBranchName(pullRequest.sourceRefName) });
|
|
418
|
+
if (stats?.behindCount === void 0) throw new Error(`Failed to get branch stats for '${pullRequest.sourceRefName}'`);
|
|
419
|
+
if (pr.skipIfNotBehindTargetBranch && stats.behindCount === 0) {
|
|
420
|
+
logger.info(` - Skipping update as source branch is not behind target branch.`);
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
const sourceBranchName = normalizeBranchName(pullRequest.sourceRefName);
|
|
424
|
+
const targetBranchName = normalizeBranchName(pullRequest.targetRefName);
|
|
425
|
+
if (stats.behindCount > 0) {
|
|
426
|
+
logger.info(` - Rebasing '${targetBranchName}' into '${sourceBranchName}' (${stats.behindCount} commit(s) behind)...`);
|
|
427
|
+
if ((await this.restApiPost(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/refs`, [{
|
|
428
|
+
name: pullRequest.sourceRefName,
|
|
429
|
+
oldObjectId: pullRequest.lastMergeSourceCommit.commitId,
|
|
430
|
+
newObjectId: pr.commit
|
|
431
|
+
}]))?.value?.[0]?.success !== true) throw new Error("Failed to rebase the target branch into the source branch");
|
|
432
|
+
}
|
|
433
|
+
logger.info(` - Pushing ${pr.changes.length} file change(s) to branch '${pullRequest.sourceRefName}'...`);
|
|
434
|
+
const push = await this.restApiPost(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pushes`, {
|
|
435
|
+
refUpdates: [{
|
|
436
|
+
name: pullRequest.sourceRefName,
|
|
437
|
+
oldObjectId: pr.commit
|
|
438
|
+
}],
|
|
439
|
+
commits: [{
|
|
440
|
+
comment: pullRequest.mergeStatus === PullRequestAsyncStatus.Conflicts ? "Resolve merge conflicts" : `Rebase '${sourceBranchName}' onto '${targetBranchName}'`,
|
|
441
|
+
author: pr.author,
|
|
442
|
+
changes: pr.changes.map((change) => {
|
|
443
|
+
return {
|
|
444
|
+
changeType: change.changeType,
|
|
445
|
+
item: { path: normalizeFilePath(change.path) },
|
|
446
|
+
newContent: {
|
|
447
|
+
content: Buffer.from(change.content, change.encoding).toString("base64"),
|
|
448
|
+
contentType: ItemContentType.Base64Encoded
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
})
|
|
452
|
+
}]
|
|
453
|
+
});
|
|
454
|
+
if (!push?.commits?.length) throw new Error("Failed to push changes to source branch, no commits were created");
|
|
455
|
+
logger.info(` - Pushed commit: ${push.commits.map((c) => c.commitId).join(", ")}.`);
|
|
456
|
+
logger.info(` - Pull request was updated successfully.`);
|
|
457
|
+
return true;
|
|
458
|
+
} catch (e) {
|
|
459
|
+
logger.error(`Failed to update pull request: ${e}`);
|
|
460
|
+
logger.debug(e);
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Approve a pull request.
|
|
466
|
+
* Requires scope "Code (Write)" (vso.code_write).
|
|
467
|
+
* @param pr
|
|
468
|
+
* @returns
|
|
469
|
+
*/
|
|
470
|
+
async approvePullRequest(pr) {
|
|
471
|
+
logger.info(`Approving pull request #${pr.pullRequestId}...`);
|
|
472
|
+
try {
|
|
473
|
+
logger.info(` - Updating reviewer vote on pull request...`);
|
|
474
|
+
const userId = await this.getUserId();
|
|
475
|
+
if ((await this.restApiPut(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}/reviewers/${userId}`, {
|
|
476
|
+
vote: 10,
|
|
477
|
+
isReapprove: true
|
|
478
|
+
}, "7.1"))?.vote !== 10) throw new Error("Failed to approve pull request, vote was not recorded");
|
|
479
|
+
logger.info(` - Pull request was approved successfully.`);
|
|
480
|
+
return true;
|
|
481
|
+
} catch (e) {
|
|
482
|
+
logger.error(`Failed to approve pull request: ${e}`);
|
|
483
|
+
logger.debug(e);
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Abandon a pull request.
|
|
489
|
+
* Requires scope "Code (Write)" (vso.code_write).
|
|
490
|
+
* @param pr
|
|
491
|
+
* @returns
|
|
492
|
+
*/
|
|
493
|
+
async abandonPullRequest(pr) {
|
|
494
|
+
logger.info(`Abandoning pull request #${pr.pullRequestId}...`);
|
|
495
|
+
try {
|
|
496
|
+
const userId = await this.getUserId();
|
|
497
|
+
if (pr.comment) {
|
|
498
|
+
logger.info(` - Adding abandonment reason comment to pull request...`);
|
|
499
|
+
if (!(await this.restApiPost(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}/threads`, {
|
|
500
|
+
status: CommentThreadStatus.Closed,
|
|
501
|
+
comments: [{
|
|
502
|
+
author: { id: userId },
|
|
503
|
+
content: pr.comment,
|
|
504
|
+
commentType: CommentType.System
|
|
505
|
+
}]
|
|
506
|
+
}))?.id) throw new Error("Failed to add comment to pull request, thread was not created");
|
|
507
|
+
}
|
|
508
|
+
logger.info(` - Abandoning pull request...`);
|
|
509
|
+
const abandonedPullRequest = await this.restApiPatch(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}`, {
|
|
510
|
+
status: PullRequestStatus.Abandoned,
|
|
511
|
+
closedBy: { id: userId }
|
|
512
|
+
});
|
|
513
|
+
if (abandonedPullRequest?.status?.toLowerCase() !== "abandoned") throw new Error("Failed to abandon pull request, status was not updated");
|
|
514
|
+
if (pr.deleteSourceBranch) {
|
|
515
|
+
logger.info(` - Deleting source branch...`);
|
|
516
|
+
if ((await this.restApiPost(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/refs`, [{
|
|
517
|
+
name: abandonedPullRequest.sourceRefName,
|
|
518
|
+
oldObjectId: abandonedPullRequest.lastMergeSourceCommit.commitId,
|
|
519
|
+
newObjectId: "0000000000000000000000000000000000000000",
|
|
520
|
+
isLocked: false
|
|
521
|
+
}]))?.value?.[0]?.success !== true) throw new Error("Failed to delete the source branch");
|
|
522
|
+
}
|
|
523
|
+
logger.info(` - Pull request was abandoned successfully.`);
|
|
524
|
+
return true;
|
|
525
|
+
} catch (e) {
|
|
526
|
+
logger.error(`Failed to abandon pull request: ${e}`);
|
|
527
|
+
logger.debug(e);
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async restApiGet(url, params, apiVersion = AzureDevOpsWebApiClient.API_VERSION) {
|
|
532
|
+
params ??= {};
|
|
533
|
+
const queryString = Object.keys(params).map((key) => `${key}=${params[key]}`).join("&");
|
|
534
|
+
const fullUrl = `${url}?api-version=${apiVersion}${queryString ? `&${queryString}` : ""}`;
|
|
535
|
+
return await sendRestApiRequestWithRetry("GET", fullUrl, void 0, async () => {
|
|
536
|
+
return await fetch(fullUrl, {
|
|
537
|
+
method: "GET",
|
|
538
|
+
headers: {
|
|
539
|
+
Accept: "application/json",
|
|
540
|
+
Authorization: `Basic ${Buffer.from(`:${this.accessToken}`).toString("base64")}`
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}, this.debug);
|
|
544
|
+
}
|
|
545
|
+
async restApiPost(url, data, apiVersion = AzureDevOpsWebApiClient.API_VERSION) {
|
|
546
|
+
const fullUrl = `${url}?api-version=${apiVersion}`;
|
|
547
|
+
return await sendRestApiRequestWithRetry("POST", fullUrl, data, async () => {
|
|
548
|
+
return await fetch(fullUrl, {
|
|
549
|
+
method: "POST",
|
|
550
|
+
headers: {
|
|
551
|
+
"Content-Type": "application/json",
|
|
552
|
+
Authorization: `Basic ${Buffer.from(`:${this.accessToken}`).toString("base64")}`
|
|
553
|
+
},
|
|
554
|
+
body: JSON.stringify(data)
|
|
555
|
+
});
|
|
556
|
+
}, this.debug);
|
|
557
|
+
}
|
|
558
|
+
async restApiPut(url, data, apiVersion = AzureDevOpsWebApiClient.API_VERSION) {
|
|
559
|
+
const fullUrl = `${url}?api-version=${apiVersion}`;
|
|
560
|
+
return await sendRestApiRequestWithRetry("PUT", fullUrl, data, async () => {
|
|
561
|
+
return await fetch(fullUrl, {
|
|
562
|
+
method: "PUT",
|
|
563
|
+
headers: {
|
|
564
|
+
"Content-Type": "application/json",
|
|
565
|
+
Authorization: `Basic ${Buffer.from(`:${this.accessToken}`).toString("base64")}`
|
|
566
|
+
},
|
|
567
|
+
body: JSON.stringify(data)
|
|
568
|
+
});
|
|
569
|
+
}, this.debug);
|
|
570
|
+
}
|
|
571
|
+
async restApiPatch(url, data, contentType, apiVersion = AzureDevOpsWebApiClient.API_VERSION) {
|
|
572
|
+
const fullUrl = `${url}?api-version=${apiVersion}`;
|
|
573
|
+
return await sendRestApiRequestWithRetry("PATCH", fullUrl, data, async () => {
|
|
574
|
+
return await fetch(fullUrl, {
|
|
575
|
+
method: "PATCH",
|
|
576
|
+
headers: {
|
|
577
|
+
"Content-Type": contentType || "application/json",
|
|
578
|
+
Authorization: `Basic ${Buffer.from(`:${this.accessToken}`).toString("base64")}`
|
|
579
|
+
},
|
|
580
|
+
body: JSON.stringify(data)
|
|
581
|
+
});
|
|
582
|
+
}, this.debug);
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
function mergeCommitMessage(id, title, description) {
|
|
586
|
+
return `Merged PR ${id}: ${title}\n\n${description}`.slice(0, 3500);
|
|
587
|
+
}
|
|
588
|
+
function isGuid(guid) {
|
|
589
|
+
return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(guid);
|
|
590
|
+
}
|
|
591
|
+
function getIdentityApiUrl(organisationApiUrl) {
|
|
592
|
+
const uri = new URL(organisationApiUrl);
|
|
593
|
+
const hostname = uri.hostname.toLowerCase();
|
|
594
|
+
if (hostname === "dev.azure.com" || hostname.endsWith(".visualstudio.com")) uri.host = "vssps.dev.azure.com";
|
|
595
|
+
return uri.toString();
|
|
596
|
+
}
|
|
597
|
+
async function sendRestApiRequestWithRetry(method, url, payload, requestAsync, isDebug = false, retryCount = 3, retryDelay = 3e3) {
|
|
598
|
+
let body;
|
|
599
|
+
try {
|
|
600
|
+
if (isDebug) logger.debug(`🌎 🠊 [${method}] ${url}`);
|
|
601
|
+
const response = await requestAsync();
|
|
602
|
+
body = await response.text();
|
|
603
|
+
const { status: statusCode, statusText: statusMessage } = response;
|
|
604
|
+
if (isDebug) logger.debug(`🌎 🠈 [${statusCode}] ${statusMessage}`);
|
|
605
|
+
if (statusCode < 200 || statusCode > 299) throw new HttpRequestError(`HTTP ${method} '${url}' failed: ${statusCode} ${statusMessage}`, statusCode);
|
|
606
|
+
return JSON.parse(body);
|
|
607
|
+
} catch (e) {
|
|
608
|
+
const err = e;
|
|
609
|
+
if (retryCount > 1 && isErrorTemporaryFailure(err)) {
|
|
610
|
+
logger.warn(err.message);
|
|
611
|
+
if (isDebug) logger.debug(`⏳ Retrying request in ${retryDelay}ms...`);
|
|
612
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
613
|
+
return sendRestApiRequestWithRetry(method, url, payload, requestAsync, isDebug, retryCount - 1, retryDelay);
|
|
614
|
+
}
|
|
615
|
+
if (isDebug) {
|
|
616
|
+
if (payload) logger.debug(`REQUEST: ${JSON.stringify(payload)}`);
|
|
617
|
+
if (body) logger.debug(`RESPONSE: ${body}`);
|
|
618
|
+
}
|
|
619
|
+
logger.trace(`THROW${e}`);
|
|
620
|
+
throw e;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/azure/config.ts
|
|
626
|
+
/**
|
|
627
|
+
* Parse the dependabot config YAML file to specify update configuration.
|
|
628
|
+
* The file should be located at any of `POSSIBLE_CONFIG_FILE_PATHS`.
|
|
629
|
+
*
|
|
630
|
+
* To view YAML file format, visit
|
|
631
|
+
* https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#allow
|
|
632
|
+
*
|
|
633
|
+
* @returns {DependabotConfig} config - the dependabot configuration
|
|
634
|
+
*/
|
|
635
|
+
async function getDependabotConfig({ url, token, remote, rootDir = process.cwd(), variableFinder }) {
|
|
636
|
+
let configPath;
|
|
637
|
+
let configContents;
|
|
638
|
+
if (remote) {
|
|
639
|
+
logger.debug(`Attempting to fetch configuration file via REST API ...`);
|
|
640
|
+
for (const fp of POSSIBLE_CONFIG_FILE_PATHS) {
|
|
641
|
+
const requestUrl = `${url.url}${url.project}/_apis/git/repositories/${url.repository}/items?path=/${fp}`;
|
|
642
|
+
logger.debug(`GET ${requestUrl}`);
|
|
643
|
+
try {
|
|
644
|
+
const authHeader = `Basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`;
|
|
645
|
+
const response = await fetch(requestUrl, { headers: {
|
|
646
|
+
Authorization: authHeader,
|
|
647
|
+
Accept: "*/*"
|
|
648
|
+
} });
|
|
649
|
+
if (response.ok) {
|
|
650
|
+
logger.debug(`Found configuration file at '${requestUrl}'`);
|
|
651
|
+
configContents = await response.text();
|
|
652
|
+
configPath = fp;
|
|
653
|
+
break;
|
|
654
|
+
} else if (response.status === 404) {
|
|
655
|
+
logger.trace(`No configuration file at '${requestUrl}'`);
|
|
656
|
+
continue;
|
|
657
|
+
} else if (response.status === 401) throw new Error(`No or invalid access token has been provided to access '${requestUrl}'`);
|
|
658
|
+
else if (response.status === 403) throw new Error(`The access token provided does not have permissions to access '${requestUrl}'`);
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (error instanceof Error && error.message.includes("access token")) throw error;
|
|
661
|
+
else throw error;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} else for (const fp of POSSIBLE_CONFIG_FILE_PATHS) {
|
|
665
|
+
const filePath = path.join(rootDir, fp);
|
|
666
|
+
if (existsSync(filePath)) {
|
|
667
|
+
logger.debug(`Found configuration file cloned at ${filePath}`);
|
|
668
|
+
configContents = await readFile(filePath, "utf-8");
|
|
669
|
+
configPath = filePath;
|
|
670
|
+
break;
|
|
671
|
+
} else logger.trace(`No configuration file cloned at ${filePath}`);
|
|
672
|
+
}
|
|
673
|
+
if (!configContents || !configPath || typeof configContents !== "string") throw new Error(`Configuration file not found at possible locations: ${POSSIBLE_CONFIG_FILE_PATHS.join(", ")}`);
|
|
674
|
+
else logger.trace("Configuration file contents read.");
|
|
675
|
+
return await parseDependabotConfig({
|
|
676
|
+
configContents,
|
|
677
|
+
configPath,
|
|
678
|
+
variableFinder
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/azure/url-parts.ts
|
|
684
|
+
function extractUrlParts({ organisationUrl, project, repository }) {
|
|
685
|
+
const url = new URL(organisationUrl);
|
|
686
|
+
const protocol = url.protocol.slice(0, -1);
|
|
687
|
+
let { hostname } = url;
|
|
688
|
+
if (/^(?<prefix>\S+)\.visualstudio\.com$/iu.test(hostname)) hostname = "dev.azure.com";
|
|
689
|
+
const organisation = extractOrganisation(organisationUrl);
|
|
690
|
+
const virtualDirectory = extractVirtualDirectory(url);
|
|
691
|
+
const apiEndpoint = `${protocol}://${hostname}${url.port ? `:${url.port}` : ""}/${virtualDirectory ? `${virtualDirectory}/` : ""}`;
|
|
692
|
+
const escapedProject = encodeURI(project);
|
|
693
|
+
const escapedRepository = encodeURI(repository);
|
|
694
|
+
const repoSlug = `${virtualDirectory ? `${virtualDirectory}/` : ""}${organisation}/${escapedProject}/_git/${escapedRepository}`;
|
|
695
|
+
return {
|
|
696
|
+
url,
|
|
697
|
+
hostname,
|
|
698
|
+
"api-endpoint": apiEndpoint,
|
|
699
|
+
project: escapedProject,
|
|
700
|
+
repository: escapedRepository,
|
|
701
|
+
"repository-slug": repoSlug
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Extract organisation name from organisation URL
|
|
706
|
+
*
|
|
707
|
+
* @param organisationUrl
|
|
708
|
+
*
|
|
709
|
+
* @returns organisation name
|
|
710
|
+
*/
|
|
711
|
+
function extractOrganisation(organisationUrl) {
|
|
712
|
+
const parts = organisationUrl.split("/");
|
|
713
|
+
if (parts.length === 6) return parts[4];
|
|
714
|
+
if (parts.length === 5) return parts[3];
|
|
715
|
+
if (parts.length === 4) return parts[2].split(".")[0];
|
|
716
|
+
throw new Error(`Error parsing organisation from organisation url: '${organisationUrl}'.`);
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Extract virtual directory from organisation URL
|
|
720
|
+
*
|
|
721
|
+
* Virtual Directories are sometimes used in on-premises
|
|
722
|
+
* @param organisationUrl
|
|
723
|
+
*
|
|
724
|
+
* @returns virtual directory
|
|
725
|
+
*
|
|
726
|
+
* @example URLs typically are like this:`https://server.domain.com/tfs/x/` and `tfs` is the virtual directory
|
|
727
|
+
*/
|
|
728
|
+
function extractVirtualDirectory(organisationUrl) {
|
|
729
|
+
const path$1 = organisationUrl.pathname.split("/");
|
|
730
|
+
return path$1.length === 4 ? path$1[1] : void 0;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
//#endregion
|
|
734
|
+
export { AzureDevOpsWebApiClient, CommentThreadStatus, CommentType, DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES, DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER, DEVOPS_PR_PROPERTY_MICROSOFT_GIT_SOURCE_REF_NAME, DependenciesPrPropertySchema, GitPullRequestMergeStrategy, ItemContentType, PullRequestAsyncStatus, PullRequestStatus, VersionControlChangeType, buildPullRequestProperties, extractUrlParts, getDependabotConfig, getPullRequestChangedFilesForOutputData, getPullRequestCloseReasonForOutputData, getPullRequestDependenciesPropertyValueForOutputData, getPullRequestDescription, getPullRequestForDependencyNames, normalizeBranchName, normalizeFilePath, parsePullRequestProperties, sendRestApiRequestWithRetry };
|
|
735
|
+
//# sourceMappingURL=azure.js.map
|