mobbdev 0.0.76 → 0.0.79
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/README.md +8 -2
- package/dist/index.mjs +1146 -298
- package/package.json +16 -15
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { hideBin } from "yargs/helpers";
|
|
|
14
14
|
|
|
15
15
|
// src/types.ts
|
|
16
16
|
var mobbCliCommand = {
|
|
17
|
+
addScmToken: "add-scm-token",
|
|
17
18
|
scan: "scan",
|
|
18
19
|
analyze: "analyze",
|
|
19
20
|
review: "review"
|
|
@@ -24,7 +25,11 @@ import chalk9 from "chalk";
|
|
|
24
25
|
import yargs from "yargs/yargs";
|
|
25
26
|
|
|
26
27
|
// src/args/commands/analyze.ts
|
|
28
|
+
import fs5 from "node:fs";
|
|
29
|
+
|
|
30
|
+
// src/commands/index.ts
|
|
27
31
|
import fs4 from "node:fs";
|
|
32
|
+
import path7 from "node:path";
|
|
28
33
|
|
|
29
34
|
// src/constants.ts
|
|
30
35
|
import path from "node:path";
|
|
@@ -36,6 +41,11 @@ import { z } from "zod";
|
|
|
36
41
|
var debug = Debug("mobbdev:constants");
|
|
37
42
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
38
43
|
dotenv.config({ path: path.join(__dirname, "../.env") });
|
|
44
|
+
var ScmTypes = {
|
|
45
|
+
Github: "GitHub",
|
|
46
|
+
Gitlab: "GitLab",
|
|
47
|
+
AzureDevOps: "Ado"
|
|
48
|
+
};
|
|
39
49
|
var SCANNERS = {
|
|
40
50
|
Checkmarx: "checkmarx",
|
|
41
51
|
Codeql: "codeql",
|
|
@@ -83,7 +93,16 @@ var API_URL = envVariables.API_URL;
|
|
|
83
93
|
var errorMessages = {
|
|
84
94
|
missingCxProjectName: `project name ${chalk.bold(
|
|
85
95
|
"(--cx-project-name)"
|
|
86
|
-
)} is needed if you're using checkmarx
|
|
96
|
+
)} is needed if you're using checkmarx`,
|
|
97
|
+
missingScmType: `SCM type ${chalk.bold(
|
|
98
|
+
"(--scm-type)"
|
|
99
|
+
)} is needed if you're adding an SCM token`,
|
|
100
|
+
invalidScmType: `SCM type ${chalk.bold(
|
|
101
|
+
"(--scm-type)"
|
|
102
|
+
)} is invalid, please use one of: ${Object.values(ScmTypes).join(", ")}`,
|
|
103
|
+
missingToken: `SCM token ${chalk.bold(
|
|
104
|
+
"(--token)"
|
|
105
|
+
)} is needed if you're adding an SCM token`
|
|
87
106
|
};
|
|
88
107
|
|
|
89
108
|
// src/features/analysis/index.ts
|
|
@@ -163,7 +182,7 @@ import fetch3 from "node-fetch";
|
|
|
163
182
|
import open2 from "open";
|
|
164
183
|
import semver from "semver";
|
|
165
184
|
import tmp2 from "tmp";
|
|
166
|
-
import { z as
|
|
185
|
+
import { z as z10 } from "zod";
|
|
167
186
|
|
|
168
187
|
// src/features/analysis/git.ts
|
|
169
188
|
import Debug2 from "debug";
|
|
@@ -216,6 +235,28 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
216
235
|
|
|
217
236
|
// src/features/analysis/graphql/mutations.ts
|
|
218
237
|
import { gql } from "graphql-request";
|
|
238
|
+
var UPDATE_SCM_TOKEN = gql`
|
|
239
|
+
mutation updateScmToken(
|
|
240
|
+
$type: ScmType!
|
|
241
|
+
$token: String!
|
|
242
|
+
$org: String
|
|
243
|
+
$username: String
|
|
244
|
+
$refreshToken: String
|
|
245
|
+
) {
|
|
246
|
+
updateScmToken(
|
|
247
|
+
type: $type
|
|
248
|
+
token: $token
|
|
249
|
+
org: $org
|
|
250
|
+
username: $username
|
|
251
|
+
refreshToken: $refreshToken
|
|
252
|
+
) {
|
|
253
|
+
id
|
|
254
|
+
adoAccessToken
|
|
255
|
+
gitlabAccessToken
|
|
256
|
+
gitHubAccessToken
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
`;
|
|
219
260
|
var UPLOAD_S3_BUCKET_INFO = gql`
|
|
220
261
|
mutation uploadS3BucketInfo($fileName: String!) {
|
|
221
262
|
uploadS3BucketInfo(fileName: $fileName) {
|
|
@@ -329,6 +370,8 @@ var ME = gql2`
|
|
|
329
370
|
email
|
|
330
371
|
githubToken
|
|
331
372
|
gitlabToken
|
|
373
|
+
adoToken
|
|
374
|
+
adoOrg
|
|
332
375
|
}
|
|
333
376
|
}
|
|
334
377
|
`;
|
|
@@ -520,6 +563,14 @@ function subscribe(query, variables, callback, wsClientOptions) {
|
|
|
520
563
|
|
|
521
564
|
// src/features/analysis/graphql/types.ts
|
|
522
565
|
import { z as z2 } from "zod";
|
|
566
|
+
var UpdateScmTokenZ = z2.object({
|
|
567
|
+
updateScmToken: z2.object({
|
|
568
|
+
id: z2.string(),
|
|
569
|
+
gitHubAccessToken: z2.string().nullable(),
|
|
570
|
+
gitlabAccessToken: z2.string().nullable(),
|
|
571
|
+
adoAccessToken: z2.string().nullable()
|
|
572
|
+
})
|
|
573
|
+
});
|
|
523
574
|
var UploadFieldsZ = z2.object({
|
|
524
575
|
bucket: z2.string(),
|
|
525
576
|
"X-Amz-Algorithm": z2.string(),
|
|
@@ -751,6 +802,17 @@ var GQLClient = class {
|
|
|
751
802
|
debug3("create community user failed %o", e);
|
|
752
803
|
}
|
|
753
804
|
}
|
|
805
|
+
async updateScmToken(args) {
|
|
806
|
+
const { type: type2, token, org, username, refreshToken } = args;
|
|
807
|
+
const updateScmTokenResult = await this._client.request(UPDATE_SCM_TOKEN, {
|
|
808
|
+
type: type2,
|
|
809
|
+
token,
|
|
810
|
+
org,
|
|
811
|
+
username,
|
|
812
|
+
refreshToken
|
|
813
|
+
});
|
|
814
|
+
return UpdateScmTokenZ.parse(updateScmTokenResult);
|
|
815
|
+
}
|
|
754
816
|
async uploadS3BucketInfo() {
|
|
755
817
|
const uploadS3BucketInfoResult = await this._client.request(UPLOAD_S3_BUCKET_INFO, {
|
|
756
818
|
fileName: "report.json"
|
|
@@ -902,20 +964,16 @@ var GQLClient = class {
|
|
|
902
964
|
// src/features/analysis/handle_finished_analysis.ts
|
|
903
965
|
import Debug4 from "debug";
|
|
904
966
|
import parseDiff from "parse-diff";
|
|
905
|
-
import { z as
|
|
906
|
-
|
|
907
|
-
// src/features/analysis/scm/constants.ts
|
|
908
|
-
var MOBB_ICON_IMG = "https://svgshare.com/i/12DK.svg";
|
|
909
|
-
var COMMIT_FIX_SVG = `https://app.mobb.ai/gh-action/commit-button.svg`;
|
|
967
|
+
import { z as z9 } from "zod";
|
|
910
968
|
|
|
911
|
-
// src/features/analysis/scm/
|
|
912
|
-
import
|
|
913
|
-
import
|
|
914
|
-
import { z as
|
|
969
|
+
// src/features/analysis/scm/ado.ts
|
|
970
|
+
import querystring2 from "node:querystring";
|
|
971
|
+
import * as api from "azure-devops-node-api";
|
|
972
|
+
import { z as z8 } from "zod";
|
|
915
973
|
|
|
916
974
|
// src/features/analysis/scm/scm.ts
|
|
917
975
|
import { Octokit as Octokit2 } from "@octokit/core";
|
|
918
|
-
import { z as
|
|
976
|
+
import { z as z7 } from "zod";
|
|
919
977
|
|
|
920
978
|
// src/features/analysis/scm/github/encryptSecret.ts
|
|
921
979
|
import sodium from "libsodium-wrappers";
|
|
@@ -933,47 +991,81 @@ import { Octokit } from "octokit";
|
|
|
933
991
|
import { z as z3 } from "zod";
|
|
934
992
|
|
|
935
993
|
// src/features/analysis/scm/urlParser.ts
|
|
936
|
-
|
|
937
|
-
"
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
if (pathname.length
|
|
947
|
-
return
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
994
|
+
function getRepoInfo(pathname, hostname) {
|
|
995
|
+
const hostnameParts = hostname.split(".");
|
|
996
|
+
if (hostnameParts.length === 3 && hostnameParts[1] === "visualstudio" && hostnameParts[2] === "com") {
|
|
997
|
+
if (pathname.length === 2 && pathname[0] === "_git") {
|
|
998
|
+
return {
|
|
999
|
+
organization: hostnameParts[0],
|
|
1000
|
+
projectName: pathname[1],
|
|
1001
|
+
repoName: pathname[1]
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
if (pathname.length === 3 && pathname[1] === "_git") {
|
|
1005
|
+
return {
|
|
1006
|
+
organization: hostnameParts[0],
|
|
1007
|
+
projectName: pathname[0],
|
|
1008
|
+
repoName: pathname[2]
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
952
1011
|
}
|
|
953
|
-
|
|
1012
|
+
if (hostname === "dev.azure.com") {
|
|
1013
|
+
if (pathname.length === 3 && pathname[1] === "_git") {
|
|
1014
|
+
return {
|
|
1015
|
+
organization: pathname[0],
|
|
1016
|
+
projectName: pathname[2],
|
|
1017
|
+
repoName: pathname[2]
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
if (pathname.length === 4 && pathname[2] === "_git") {
|
|
1021
|
+
return {
|
|
1022
|
+
organization: pathname[0],
|
|
1023
|
+
projectName: pathname[1],
|
|
1024
|
+
repoName: pathname[3]
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if (hostname === "github.com") {
|
|
1029
|
+
if (pathname.length === 2) {
|
|
1030
|
+
return {
|
|
1031
|
+
organization: pathname[0],
|
|
1032
|
+
projectName: void 0,
|
|
1033
|
+
repoName: pathname[1]
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
if (hostname === "gitlab.com") {
|
|
1038
|
+
if (pathname.length >= 2) {
|
|
1039
|
+
return {
|
|
1040
|
+
organization: pathname[0],
|
|
1041
|
+
projectName: void 0,
|
|
1042
|
+
repoName: pathname[pathname.length - 1]
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return null;
|
|
1047
|
+
}
|
|
954
1048
|
var NAME_REGEX = /[a-z0-9\-_.+]+/i;
|
|
955
1049
|
var parseScmURL = (scmURL) => {
|
|
956
1050
|
try {
|
|
957
1051
|
const url = new URL(scmURL);
|
|
958
1052
|
const hostname = url.hostname.toLowerCase();
|
|
959
|
-
if (!(hostname in pathnameParsingMap))
|
|
960
|
-
return null;
|
|
961
1053
|
const projectPath = url.pathname.substring(1).replace(/.git$/i, "");
|
|
962
|
-
const repo =
|
|
963
|
-
projectPath.split("/")
|
|
964
|
-
);
|
|
1054
|
+
const repo = getRepoInfo(projectPath.split("/"), hostname);
|
|
965
1055
|
if (!repo)
|
|
966
1056
|
return null;
|
|
967
|
-
const { organization, repoName } = repo;
|
|
1057
|
+
const { organization, repoName, projectName } = repo;
|
|
968
1058
|
if (!organization || !repoName)
|
|
969
1059
|
return null;
|
|
970
1060
|
if (!organization.match(NAME_REGEX) || !repoName.match(NAME_REGEX))
|
|
971
1061
|
return null;
|
|
972
1062
|
return {
|
|
973
|
-
hostname
|
|
1063
|
+
hostname,
|
|
974
1064
|
organization,
|
|
975
1065
|
projectPath,
|
|
976
|
-
repoName
|
|
1066
|
+
repoName,
|
|
1067
|
+
projectName,
|
|
1068
|
+
pathElements: projectPath.split("/")
|
|
977
1069
|
};
|
|
978
1070
|
} catch (e) {
|
|
979
1071
|
return null;
|
|
@@ -1034,7 +1126,7 @@ async function githubValidateParams(url, accessToken) {
|
|
|
1034
1126
|
await oktoKit.rest.users.getAuthenticated();
|
|
1035
1127
|
}
|
|
1036
1128
|
if (url) {
|
|
1037
|
-
const { owner, repo } =
|
|
1129
|
+
const { owner, repo } = parseGithubOwnerAndRepo(url);
|
|
1038
1130
|
await oktoKit.rest.repos.get({ repo, owner });
|
|
1039
1131
|
}
|
|
1040
1132
|
} catch (e) {
|
|
@@ -1056,7 +1148,7 @@ async function getGithubUsername(accessToken) {
|
|
|
1056
1148
|
}
|
|
1057
1149
|
async function getGithubIsUserCollaborator(username, accessToken, repoUrl) {
|
|
1058
1150
|
try {
|
|
1059
|
-
const { owner, repo } =
|
|
1151
|
+
const { owner, repo } = parseGithubOwnerAndRepo(repoUrl);
|
|
1060
1152
|
const oktoKit = getOktoKit({ githubAuthToken: accessToken });
|
|
1061
1153
|
const res = await oktoKit.rest.repos.checkCollaborator({
|
|
1062
1154
|
owner,
|
|
@@ -1072,7 +1164,7 @@ async function getGithubIsUserCollaborator(username, accessToken, repoUrl) {
|
|
|
1072
1164
|
return false;
|
|
1073
1165
|
}
|
|
1074
1166
|
async function getGithubPullRequestStatus(accessToken, repoUrl, prNumber) {
|
|
1075
|
-
const { owner, repo } =
|
|
1167
|
+
const { owner, repo } = parseGithubOwnerAndRepo(repoUrl);
|
|
1076
1168
|
const oktoKit = getOktoKit({ githubAuthToken: accessToken });
|
|
1077
1169
|
const res = await oktoKit.rest.pulls.get({
|
|
1078
1170
|
owner,
|
|
@@ -1088,7 +1180,7 @@ async function getGithubPullRequestStatus(accessToken, repoUrl, prNumber) {
|
|
|
1088
1180
|
return res.data.state;
|
|
1089
1181
|
}
|
|
1090
1182
|
async function getGithubIsRemoteBranch(accessToken, repoUrl, branch) {
|
|
1091
|
-
const { owner, repo } =
|
|
1183
|
+
const { owner, repo } = parseGithubOwnerAndRepo(repoUrl);
|
|
1092
1184
|
const oktoKit = getOktoKit({ githubAuthToken: accessToken });
|
|
1093
1185
|
try {
|
|
1094
1186
|
const res = await oktoKit.rest.repos.getBranch({
|
|
@@ -1132,7 +1224,7 @@ async function getGithubRepoList(accessToken) {
|
|
|
1132
1224
|
}
|
|
1133
1225
|
}
|
|
1134
1226
|
async function getGithubBranchList(accessToken, repoUrl) {
|
|
1135
|
-
const { owner, repo } =
|
|
1227
|
+
const { owner, repo } = parseGithubOwnerAndRepo(repoUrl);
|
|
1136
1228
|
const oktoKit = getOktoKit({ githubAuthToken: accessToken });
|
|
1137
1229
|
const res = await oktoKit.rest.repos.listBranches({
|
|
1138
1230
|
owner,
|
|
@@ -1143,7 +1235,7 @@ async function getGithubBranchList(accessToken, repoUrl) {
|
|
|
1143
1235
|
return res.data.map((branch) => branch.name);
|
|
1144
1236
|
}
|
|
1145
1237
|
async function createPullRequest(options) {
|
|
1146
|
-
const { owner, repo } =
|
|
1238
|
+
const { owner, repo } = parseGithubOwnerAndRepo(options.repoUrl);
|
|
1147
1239
|
const oktoKit = getOktoKit({ githubAuthToken: options.accessToken });
|
|
1148
1240
|
const res = await oktoKit.rest.pulls.create({
|
|
1149
1241
|
owner,
|
|
@@ -1158,7 +1250,7 @@ async function createPullRequest(options) {
|
|
|
1158
1250
|
return res.data.number;
|
|
1159
1251
|
}
|
|
1160
1252
|
async function forkRepo(options) {
|
|
1161
|
-
const { owner, repo } =
|
|
1253
|
+
const { owner, repo } = parseGithubOwnerAndRepo(options.repoUrl);
|
|
1162
1254
|
const oktoKit = getOktoKit({ githubAuthToken: options.accessToken });
|
|
1163
1255
|
const res = await oktoKit.rest.repos.createFork({
|
|
1164
1256
|
owner,
|
|
@@ -1178,11 +1270,11 @@ async function getRepos(oktoKit) {
|
|
|
1178
1270
|
}
|
|
1179
1271
|
async function getGithubRepoDefaultBranch(repoUrl, options) {
|
|
1180
1272
|
const oktoKit = getOktoKit(options);
|
|
1181
|
-
const { owner, repo } =
|
|
1273
|
+
const { owner, repo } = parseGithubOwnerAndRepo(repoUrl);
|
|
1182
1274
|
return (await oktoKit.rest.repos.get({ repo, owner })).data.default_branch;
|
|
1183
1275
|
}
|
|
1184
1276
|
async function getGithubReferenceData({ ref, gitHubUrl }, options) {
|
|
1185
|
-
const { owner, repo } =
|
|
1277
|
+
const { owner, repo } = parseGithubOwnerAndRepo(gitHubUrl);
|
|
1186
1278
|
let res;
|
|
1187
1279
|
try {
|
|
1188
1280
|
const oktoKit = getOktoKit(options);
|
|
@@ -1257,7 +1349,7 @@ async function getCommit({
|
|
|
1257
1349
|
commit_sha: commitSha
|
|
1258
1350
|
});
|
|
1259
1351
|
}
|
|
1260
|
-
function
|
|
1352
|
+
function parseGithubOwnerAndRepo(gitHubUrl) {
|
|
1261
1353
|
gitHubUrl = removeTrailingSlash(gitHubUrl);
|
|
1262
1354
|
const parsingResult = parseScmURL(gitHubUrl);
|
|
1263
1355
|
if (!parsingResult || parsingResult.hostname !== "github.com") {
|
|
@@ -1291,12 +1383,12 @@ async function queryGithubGraphql(query, variables, options) {
|
|
|
1291
1383
|
throw e;
|
|
1292
1384
|
}
|
|
1293
1385
|
}
|
|
1294
|
-
async function getGithubBlameRanges({ ref, gitHubUrl, path:
|
|
1295
|
-
const { owner, repo } =
|
|
1386
|
+
async function getGithubBlameRanges({ ref, gitHubUrl, path: path9 }, options) {
|
|
1387
|
+
const { owner, repo } = parseGithubOwnerAndRepo(gitHubUrl);
|
|
1296
1388
|
const variables = {
|
|
1297
1389
|
owner,
|
|
1298
1390
|
repo,
|
|
1299
|
-
path:
|
|
1391
|
+
path: path9,
|
|
1300
1392
|
ref
|
|
1301
1393
|
};
|
|
1302
1394
|
const res = await queryGithubGraphql(
|
|
@@ -1323,8 +1415,8 @@ async function createPr({
|
|
|
1323
1415
|
body
|
|
1324
1416
|
}, options) {
|
|
1325
1417
|
const oktoKit = getOktoKit(options);
|
|
1326
|
-
const { owner: sourceOwner, repo: sourceRepo } =
|
|
1327
|
-
const { owner, repo } =
|
|
1418
|
+
const { owner: sourceOwner, repo: sourceRepo } = parseGithubOwnerAndRepo(sourceRepoUrl);
|
|
1419
|
+
const { owner, repo } = parseGithubOwnerAndRepo(userRepoUrl);
|
|
1328
1420
|
const [sourceFilePath, secondFilePath] = filesPaths;
|
|
1329
1421
|
const sourceFileContentResponse = await oktoKit.rest.repos.getContent({
|
|
1330
1422
|
owner: sourceOwner,
|
|
@@ -1445,83 +1537,326 @@ function getARepositoryPublicKey(client, params) {
|
|
|
1445
1537
|
return client.request(GET_A_REPOSITORY_PUBLIC_KEY, params);
|
|
1446
1538
|
}
|
|
1447
1539
|
|
|
1540
|
+
// src/features/analysis/scm/gitlab.ts
|
|
1541
|
+
import querystring from "node:querystring";
|
|
1542
|
+
import { Gitlab } from "@gitbeaker/rest";
|
|
1543
|
+
import { z as z4 } from "zod";
|
|
1544
|
+
function removeTrailingSlash2(str) {
|
|
1545
|
+
return str.trim().replace(/\/+$/, "");
|
|
1546
|
+
}
|
|
1547
|
+
var EnvVariablesZod2 = z4.object({
|
|
1548
|
+
GITLAB_API_TOKEN: z4.string().optional()
|
|
1549
|
+
});
|
|
1550
|
+
var { GITLAB_API_TOKEN } = EnvVariablesZod2.parse(process.env);
|
|
1551
|
+
function getGitBeaker(options) {
|
|
1552
|
+
const token = options?.gitlabAuthToken ?? GITLAB_API_TOKEN ?? "";
|
|
1553
|
+
if (token?.startsWith("glpat-") || token === "") {
|
|
1554
|
+
return new Gitlab({ token });
|
|
1555
|
+
}
|
|
1556
|
+
return new Gitlab({ oauthToken: token });
|
|
1557
|
+
}
|
|
1558
|
+
async function gitlabValidateParams({
|
|
1559
|
+
url,
|
|
1560
|
+
accessToken
|
|
1561
|
+
}) {
|
|
1562
|
+
try {
|
|
1563
|
+
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1564
|
+
if (accessToken) {
|
|
1565
|
+
await api2.Users.showCurrentUser();
|
|
1566
|
+
}
|
|
1567
|
+
if (url) {
|
|
1568
|
+
const { projectPath } = parseGitlabOwnerAndRepo(url);
|
|
1569
|
+
await api2.Projects.show(projectPath);
|
|
1570
|
+
}
|
|
1571
|
+
} catch (e) {
|
|
1572
|
+
const error = e;
|
|
1573
|
+
const code = error.code || error.status || error.statusCode || error.response?.status || error.response?.statusCode || error.response?.code;
|
|
1574
|
+
const description = error.description || `${e}`;
|
|
1575
|
+
if (code === 401 || code === 403 || description.includes("401") || description.includes("403")) {
|
|
1576
|
+
throw new InvalidAccessTokenError(`invalid gitlab access token`);
|
|
1577
|
+
}
|
|
1578
|
+
if (code === 404 || description.includes("404") || description.includes("Not Found")) {
|
|
1579
|
+
throw new InvalidRepoUrlError(`invalid gitlab repo URL: ${url}`);
|
|
1580
|
+
}
|
|
1581
|
+
throw e;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
async function getGitlabUsername(accessToken) {
|
|
1585
|
+
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1586
|
+
const res = await api2.Users.showCurrentUser();
|
|
1587
|
+
return res.username;
|
|
1588
|
+
}
|
|
1589
|
+
async function getGitlabIsUserCollaborator({
|
|
1590
|
+
username,
|
|
1591
|
+
accessToken,
|
|
1592
|
+
repoUrl
|
|
1593
|
+
}) {
|
|
1594
|
+
try {
|
|
1595
|
+
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1596
|
+
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1597
|
+
const res = await api2.Projects.show(projectPath);
|
|
1598
|
+
const members = await api2.ProjectMembers.all(res.id, {
|
|
1599
|
+
includeInherited: true
|
|
1600
|
+
});
|
|
1601
|
+
return !!members.find((member) => member.username === username);
|
|
1602
|
+
} catch (e) {
|
|
1603
|
+
return false;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
async function getGitlabMergeRequestStatus({
|
|
1607
|
+
accessToken,
|
|
1608
|
+
repoUrl,
|
|
1609
|
+
mrNumber
|
|
1610
|
+
}) {
|
|
1611
|
+
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1612
|
+
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1613
|
+
const res = await api2.MergeRequests.show(projectPath, mrNumber);
|
|
1614
|
+
switch (res.state) {
|
|
1615
|
+
case "merged" /* merged */:
|
|
1616
|
+
case "opened" /* opened */:
|
|
1617
|
+
case "closed" /* closed */:
|
|
1618
|
+
return res.state;
|
|
1619
|
+
default:
|
|
1620
|
+
throw new Error(`unknown merge request state ${res.state}`);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
async function getGitlabIsRemoteBranch({
|
|
1624
|
+
accessToken,
|
|
1625
|
+
repoUrl,
|
|
1626
|
+
branch
|
|
1627
|
+
}) {
|
|
1628
|
+
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1629
|
+
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1630
|
+
try {
|
|
1631
|
+
const res = await api2.Branches.show(projectPath, branch);
|
|
1632
|
+
return res.name === branch;
|
|
1633
|
+
} catch (e) {
|
|
1634
|
+
return false;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
async function getGitlabRepoList(accessToken) {
|
|
1638
|
+
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1639
|
+
const res = await api2.Projects.all({
|
|
1640
|
+
membership: true,
|
|
1641
|
+
//TODO: a bug in the sorting mechanism of this api call
|
|
1642
|
+
//disallows us to sort by updated_at in descending order
|
|
1643
|
+
//so we have to sort by updated_at in ascending order.
|
|
1644
|
+
//We can wait for the bug to be fixed or call the api
|
|
1645
|
+
//directly with fetch()
|
|
1646
|
+
sort: "asc",
|
|
1647
|
+
orderBy: "updated_at",
|
|
1648
|
+
perPage: 100
|
|
1649
|
+
});
|
|
1650
|
+
return Promise.all(
|
|
1651
|
+
res.map(async (project) => {
|
|
1652
|
+
const proj = await api2.Projects.show(project.id);
|
|
1653
|
+
const owner = proj.namespace.name;
|
|
1654
|
+
const repoLanguages = await api2.Projects.showLanguages(project.id);
|
|
1655
|
+
return {
|
|
1656
|
+
repoName: project.path,
|
|
1657
|
+
repoUrl: project.web_url,
|
|
1658
|
+
repoOwner: owner,
|
|
1659
|
+
repoLanguages: Object.keys(repoLanguages),
|
|
1660
|
+
repoIsPublic: project.visibility === "public",
|
|
1661
|
+
repoUpdatedAt: project.last_activity_at
|
|
1662
|
+
};
|
|
1663
|
+
})
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
async function getGitlabBranchList({
|
|
1667
|
+
accessToken,
|
|
1668
|
+
repoUrl
|
|
1669
|
+
}) {
|
|
1670
|
+
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1671
|
+
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1672
|
+
try {
|
|
1673
|
+
const res = await api2.Branches.all(projectPath, {
|
|
1674
|
+
perPage: 100,
|
|
1675
|
+
pagination: "keyset",
|
|
1676
|
+
orderBy: "updated_at",
|
|
1677
|
+
sort: "dec"
|
|
1678
|
+
});
|
|
1679
|
+
return res.map((branch) => branch.name);
|
|
1680
|
+
} catch (e) {
|
|
1681
|
+
return [];
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
async function createMergeRequest(options) {
|
|
1685
|
+
const { projectPath } = parseGitlabOwnerAndRepo(options.repoUrl);
|
|
1686
|
+
const api2 = getGitBeaker({ gitlabAuthToken: options.accessToken });
|
|
1687
|
+
const res = await api2.MergeRequests.create(
|
|
1688
|
+
projectPath,
|
|
1689
|
+
options.sourceBranchName,
|
|
1690
|
+
options.targetBranchName,
|
|
1691
|
+
options.title,
|
|
1692
|
+
{
|
|
1693
|
+
description: options.body
|
|
1694
|
+
}
|
|
1695
|
+
);
|
|
1696
|
+
return res.iid;
|
|
1697
|
+
}
|
|
1698
|
+
async function getGitlabRepoDefaultBranch(repoUrl, options) {
|
|
1699
|
+
const api2 = getGitBeaker({ gitlabAuthToken: options?.gitlabAuthToken });
|
|
1700
|
+
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1701
|
+
const project = await api2.Projects.show(projectPath);
|
|
1702
|
+
if (!project.default_branch) {
|
|
1703
|
+
throw new Error("no default branch");
|
|
1704
|
+
}
|
|
1705
|
+
return project.default_branch;
|
|
1706
|
+
}
|
|
1707
|
+
async function getGitlabReferenceData({ ref, gitlabUrl }, options) {
|
|
1708
|
+
const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
|
|
1709
|
+
const api2 = getGitBeaker({ gitlabAuthToken: options?.gitlabAuthToken });
|
|
1710
|
+
const results = await Promise.allSettled([
|
|
1711
|
+
(async () => {
|
|
1712
|
+
const res = await api2.Branches.show(projectPath, ref);
|
|
1713
|
+
return {
|
|
1714
|
+
sha: res.commit.id,
|
|
1715
|
+
type: "BRANCH" /* BRANCH */,
|
|
1716
|
+
date: res.commit.committed_date ? new Date(res.commit.committed_date) : void 0
|
|
1717
|
+
};
|
|
1718
|
+
})(),
|
|
1719
|
+
(async () => {
|
|
1720
|
+
const res = await api2.Commits.show(projectPath, ref);
|
|
1721
|
+
return {
|
|
1722
|
+
sha: res.id,
|
|
1723
|
+
type: "COMMIT" /* COMMIT */,
|
|
1724
|
+
date: res.committed_date ? new Date(res.committed_date) : void 0
|
|
1725
|
+
};
|
|
1726
|
+
})(),
|
|
1727
|
+
(async () => {
|
|
1728
|
+
const res = await api2.Tags.show(projectPath, ref);
|
|
1729
|
+
return {
|
|
1730
|
+
sha: res.commit.id,
|
|
1731
|
+
type: "TAG" /* TAG */,
|
|
1732
|
+
date: res.commit.committed_date ? new Date(res.commit.committed_date) : void 0
|
|
1733
|
+
};
|
|
1734
|
+
})()
|
|
1735
|
+
]);
|
|
1736
|
+
const [branchRes, commitRes, tagRes] = results;
|
|
1737
|
+
if (tagRes.status === "fulfilled") {
|
|
1738
|
+
return tagRes.value;
|
|
1739
|
+
}
|
|
1740
|
+
if (branchRes.status === "fulfilled") {
|
|
1741
|
+
return branchRes.value;
|
|
1742
|
+
}
|
|
1743
|
+
if (commitRes.status === "fulfilled") {
|
|
1744
|
+
return commitRes.value;
|
|
1745
|
+
}
|
|
1746
|
+
throw new RefNotFoundError(`ref: ${ref} does not exist`);
|
|
1747
|
+
}
|
|
1748
|
+
function parseGitlabOwnerAndRepo(gitlabUrl) {
|
|
1749
|
+
gitlabUrl = removeTrailingSlash2(gitlabUrl);
|
|
1750
|
+
const parsingResult = parseScmURL(gitlabUrl);
|
|
1751
|
+
if (!parsingResult || parsingResult.hostname !== "gitlab.com") {
|
|
1752
|
+
throw new InvalidUrlPatternError(`invalid gitlab repo Url ${gitlabUrl}`);
|
|
1753
|
+
}
|
|
1754
|
+
const { organization, repoName, projectPath } = parsingResult;
|
|
1755
|
+
return { owner: organization, repo: repoName, projectPath };
|
|
1756
|
+
}
|
|
1757
|
+
async function getGitlabBlameRanges({ ref, gitlabUrl, path: path9 }, options) {
|
|
1758
|
+
const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
|
|
1759
|
+
const api2 = getGitBeaker({ gitlabAuthToken: options?.gitlabAuthToken });
|
|
1760
|
+
const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path9, ref);
|
|
1761
|
+
let lineNumber = 1;
|
|
1762
|
+
return resp.filter((range) => range.lines).map((range) => {
|
|
1763
|
+
const oldLineNumber = lineNumber;
|
|
1764
|
+
if (!range.lines) {
|
|
1765
|
+
throw new Error("range.lines should not be undefined");
|
|
1766
|
+
}
|
|
1767
|
+
lineNumber += range.lines.length;
|
|
1768
|
+
return {
|
|
1769
|
+
startingLine: oldLineNumber,
|
|
1770
|
+
endingLine: lineNumber - 1,
|
|
1771
|
+
login: range.commit.author_email,
|
|
1772
|
+
email: range.commit.author_email,
|
|
1773
|
+
name: range.commit.author_name
|
|
1774
|
+
};
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
var GitlabAuthResultZ = z4.object({
|
|
1778
|
+
access_token: z4.string(),
|
|
1779
|
+
token_type: z4.string(),
|
|
1780
|
+
refresh_token: z4.string()
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1448
1783
|
// src/features/analysis/scm/scmSubmit/index.ts
|
|
1449
1784
|
import fs from "node:fs/promises";
|
|
1450
1785
|
import os from "os";
|
|
1451
1786
|
import path3 from "path";
|
|
1452
1787
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
1453
1788
|
import tmp from "tmp";
|
|
1454
|
-
import { z as
|
|
1789
|
+
import { z as z6 } from "zod";
|
|
1455
1790
|
|
|
1456
1791
|
// src/features/analysis/scm/scmSubmit/types.ts
|
|
1457
|
-
import { z as
|
|
1458
|
-
var BaseSubmitToScmMessageZ =
|
|
1459
|
-
submitFixRequestId:
|
|
1460
|
-
fixes:
|
|
1461
|
-
|
|
1462
|
-
fixId:
|
|
1463
|
-
diff:
|
|
1792
|
+
import { z as z5 } from "zod";
|
|
1793
|
+
var BaseSubmitToScmMessageZ = z5.object({
|
|
1794
|
+
submitFixRequestId: z5.string().uuid(),
|
|
1795
|
+
fixes: z5.array(
|
|
1796
|
+
z5.object({
|
|
1797
|
+
fixId: z5.string().uuid(),
|
|
1798
|
+
diff: z5.string()
|
|
1464
1799
|
})
|
|
1465
1800
|
),
|
|
1466
|
-
commitHash:
|
|
1467
|
-
repoUrl:
|
|
1801
|
+
commitHash: z5.string(),
|
|
1802
|
+
repoUrl: z5.string()
|
|
1468
1803
|
});
|
|
1469
1804
|
var submitToScmMessageType = {
|
|
1470
1805
|
commitToSameBranch: "commitToSameBranch",
|
|
1471
1806
|
submitFixesForDifferentBranch: "submitFixesForDifferentBranch"
|
|
1472
1807
|
};
|
|
1473
1808
|
var CommitToSameBranchParamsZ = BaseSubmitToScmMessageZ.merge(
|
|
1474
|
-
|
|
1475
|
-
type:
|
|
1476
|
-
branch:
|
|
1477
|
-
commitMessage:
|
|
1478
|
-
commitDescription:
|
|
1479
|
-
githubCommentId:
|
|
1809
|
+
z5.object({
|
|
1810
|
+
type: z5.literal(submitToScmMessageType.commitToSameBranch),
|
|
1811
|
+
branch: z5.string(),
|
|
1812
|
+
commitMessage: z5.string(),
|
|
1813
|
+
commitDescription: z5.string().nullish(),
|
|
1814
|
+
githubCommentId: z5.number().nullish()
|
|
1480
1815
|
})
|
|
1481
1816
|
);
|
|
1482
|
-
var SubmitFixesToDifferentBranchParamsZ =
|
|
1483
|
-
type:
|
|
1484
|
-
submitBranch:
|
|
1485
|
-
baseBranch:
|
|
1817
|
+
var SubmitFixesToDifferentBranchParamsZ = z5.object({
|
|
1818
|
+
type: z5.literal(submitToScmMessageType.submitFixesForDifferentBranch),
|
|
1819
|
+
submitBranch: z5.string(),
|
|
1820
|
+
baseBranch: z5.string()
|
|
1486
1821
|
}).merge(BaseSubmitToScmMessageZ);
|
|
1487
|
-
var SubmitFixesMessageZ =
|
|
1822
|
+
var SubmitFixesMessageZ = z5.union([
|
|
1488
1823
|
CommitToSameBranchParamsZ,
|
|
1489
1824
|
SubmitFixesToDifferentBranchParamsZ
|
|
1490
1825
|
]);
|
|
1491
|
-
var FixResponseArrayZ =
|
|
1492
|
-
|
|
1493
|
-
fixId:
|
|
1826
|
+
var FixResponseArrayZ = z5.array(
|
|
1827
|
+
z5.object({
|
|
1828
|
+
fixId: z5.string().uuid()
|
|
1494
1829
|
})
|
|
1495
1830
|
);
|
|
1496
|
-
var SubmitFixesBaseResponseMessageZ =
|
|
1497
|
-
submitFixRequestId:
|
|
1498
|
-
submitBranches:
|
|
1499
|
-
|
|
1500
|
-
branchName:
|
|
1831
|
+
var SubmitFixesBaseResponseMessageZ = z5.object({
|
|
1832
|
+
submitFixRequestId: z5.string().uuid(),
|
|
1833
|
+
submitBranches: z5.array(
|
|
1834
|
+
z5.object({
|
|
1835
|
+
branchName: z5.string(),
|
|
1501
1836
|
fixes: FixResponseArrayZ
|
|
1502
1837
|
})
|
|
1503
1838
|
),
|
|
1504
|
-
error:
|
|
1505
|
-
type:
|
|
1839
|
+
error: z5.object({
|
|
1840
|
+
type: z5.enum([
|
|
1506
1841
|
"InitialRepoAccessError",
|
|
1507
1842
|
"PushBranchError",
|
|
1508
1843
|
"UnknownError"
|
|
1509
1844
|
]),
|
|
1510
|
-
info:
|
|
1511
|
-
message:
|
|
1512
|
-
pushBranchName:
|
|
1845
|
+
info: z5.object({
|
|
1846
|
+
message: z5.string(),
|
|
1847
|
+
pushBranchName: z5.string().optional()
|
|
1513
1848
|
})
|
|
1514
1849
|
}).optional()
|
|
1515
1850
|
});
|
|
1516
|
-
var SubmitFixesToSameBranchResponseMessageZ =
|
|
1517
|
-
type:
|
|
1518
|
-
githubCommentId:
|
|
1851
|
+
var SubmitFixesToSameBranchResponseMessageZ = z5.object({
|
|
1852
|
+
type: z5.literal(submitToScmMessageType.commitToSameBranch),
|
|
1853
|
+
githubCommentId: z5.number().nullish()
|
|
1519
1854
|
}).merge(SubmitFixesBaseResponseMessageZ);
|
|
1520
|
-
var SubmitFixesToDifferentBranchResponseMessageZ =
|
|
1521
|
-
type:
|
|
1522
|
-
githubCommentId:
|
|
1855
|
+
var SubmitFixesToDifferentBranchResponseMessageZ = z5.object({
|
|
1856
|
+
type: z5.literal(submitToScmMessageType.submitFixesForDifferentBranch),
|
|
1857
|
+
githubCommentId: z5.number().optional()
|
|
1523
1858
|
}).merge(SubmitFixesBaseResponseMessageZ);
|
|
1524
|
-
var
|
|
1859
|
+
var SubmitFixesResponseMessageZ = z5.discriminatedUnion("type", [
|
|
1525
1860
|
SubmitFixesToSameBranchResponseMessageZ,
|
|
1526
1861
|
SubmitFixesToDifferentBranchResponseMessageZ
|
|
1527
1862
|
]);
|
|
@@ -1539,32 +1874,40 @@ var isValidBranchName = async (branchName) => {
|
|
|
1539
1874
|
return false;
|
|
1540
1875
|
}
|
|
1541
1876
|
};
|
|
1542
|
-
var FixesZ =
|
|
1877
|
+
var FixesZ = z6.array(z6.object({ fixId: z6.string(), diff: z6.string() })).nonempty();
|
|
1543
1878
|
|
|
1544
1879
|
// src/features/analysis/scm/scm.ts
|
|
1545
1880
|
function getScmLibTypeFromUrl(url) {
|
|
1546
1881
|
if (!url) {
|
|
1547
1882
|
return void 0;
|
|
1548
1883
|
}
|
|
1549
|
-
|
|
1884
|
+
const urlObject = new URL(url);
|
|
1885
|
+
const hostname = urlObject.hostname;
|
|
1886
|
+
if (hostname === "gitlab.com") {
|
|
1550
1887
|
return "GITLAB" /* GITLAB */;
|
|
1551
1888
|
}
|
|
1552
|
-
if (
|
|
1889
|
+
if (hostname === "github.com") {
|
|
1553
1890
|
return "GITHUB" /* GITHUB */;
|
|
1554
1891
|
}
|
|
1892
|
+
if (hostname === "dev.azure.com" || hostname.endsWith(".visualstudio.com")) {
|
|
1893
|
+
return "ADO" /* ADO */;
|
|
1894
|
+
}
|
|
1555
1895
|
return void 0;
|
|
1556
1896
|
}
|
|
1557
1897
|
async function scmCanReachRepo({
|
|
1558
1898
|
repoUrl,
|
|
1559
1899
|
githubToken,
|
|
1560
|
-
gitlabToken
|
|
1900
|
+
gitlabToken,
|
|
1901
|
+
adoToken,
|
|
1902
|
+
scmOrg
|
|
1561
1903
|
}) {
|
|
1562
1904
|
try {
|
|
1563
1905
|
const scmLibType = getScmLibTypeFromUrl(repoUrl);
|
|
1564
1906
|
await SCMLib.init({
|
|
1565
1907
|
url: repoUrl,
|
|
1566
|
-
accessToken: scmLibType === "GITHUB" /* GITHUB */ ? githubToken : scmLibType === "GITLAB" /* GITLAB */ ? gitlabToken : "",
|
|
1567
|
-
scmType: scmLibType
|
|
1908
|
+
accessToken: scmLibType === "GITHUB" /* GITHUB */ ? githubToken : scmLibType === "GITLAB" /* GITLAB */ ? gitlabToken : scmLibType === "ADO" /* ADO */ ? adoToken : "",
|
|
1909
|
+
scmType: scmLibType,
|
|
1910
|
+
scmOrg
|
|
1568
1911
|
});
|
|
1569
1912
|
return true;
|
|
1570
1913
|
} catch (e) {
|
|
@@ -1597,11 +1940,13 @@ var RepoNoTokenAccessError = class extends Error {
|
|
|
1597
1940
|
}
|
|
1598
1941
|
};
|
|
1599
1942
|
var SCMLib = class {
|
|
1600
|
-
constructor(url, accessToken) {
|
|
1943
|
+
constructor(url, accessToken, scmOrg) {
|
|
1601
1944
|
__publicField(this, "url");
|
|
1602
1945
|
__publicField(this, "accessToken");
|
|
1946
|
+
__publicField(this, "scmOrg");
|
|
1603
1947
|
this.accessToken = accessToken;
|
|
1604
1948
|
this.url = url;
|
|
1949
|
+
this.scmOrg = scmOrg;
|
|
1605
1950
|
}
|
|
1606
1951
|
async getUrlWithCredentials() {
|
|
1607
1952
|
if (!this.url) {
|
|
@@ -1612,9 +1957,13 @@ var SCMLib = class {
|
|
|
1612
1957
|
if (!this.accessToken) {
|
|
1613
1958
|
return trimmedUrl;
|
|
1614
1959
|
}
|
|
1615
|
-
const
|
|
1960
|
+
const scmLibType = getScmLibTypeFromUrl(trimmedUrl);
|
|
1961
|
+
if (scmLibType === "ADO" /* ADO */) {
|
|
1962
|
+
return `https://${this.accessToken}@${trimmedUrl.toLowerCase().replace("https://", "")}`;
|
|
1963
|
+
}
|
|
1616
1964
|
const is_http = trimmedUrl.toLowerCase().startsWith("http://");
|
|
1617
1965
|
const is_https = trimmedUrl.toLowerCase().startsWith("https://");
|
|
1966
|
+
const username = await this._getUsernameForAuthUrl();
|
|
1618
1967
|
if (is_http) {
|
|
1619
1968
|
return `http://${username}:${this.accessToken}@${trimmedUrl.toLowerCase().replace("http://", "")}`;
|
|
1620
1969
|
} else if (is_https) {
|
|
@@ -1642,7 +1991,8 @@ var SCMLib = class {
|
|
|
1642
1991
|
static async init({
|
|
1643
1992
|
url,
|
|
1644
1993
|
accessToken,
|
|
1645
|
-
scmType
|
|
1994
|
+
scmType,
|
|
1995
|
+
scmOrg
|
|
1646
1996
|
}) {
|
|
1647
1997
|
let trimmedUrl = void 0;
|
|
1648
1998
|
if (url) {
|
|
@@ -1650,12 +2000,17 @@ var SCMLib = class {
|
|
|
1650
2000
|
}
|
|
1651
2001
|
try {
|
|
1652
2002
|
if ("GITHUB" /* GITHUB */ === scmType) {
|
|
1653
|
-
const scm = new GithubSCMLib(trimmedUrl, accessToken);
|
|
2003
|
+
const scm = new GithubSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
1654
2004
|
await scm.validateParams();
|
|
1655
2005
|
return scm;
|
|
1656
2006
|
}
|
|
1657
2007
|
if ("GITLAB" /* GITLAB */ === scmType) {
|
|
1658
|
-
const scm = new GitlabSCMLib(trimmedUrl, accessToken);
|
|
2008
|
+
const scm = new GitlabSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
2009
|
+
await scm.validateParams();
|
|
2010
|
+
return scm;
|
|
2011
|
+
}
|
|
2012
|
+
if ("ADO" /* ADO */ === scmType) {
|
|
2013
|
+
const scm = new AdoSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
1659
2014
|
await scm.validateParams();
|
|
1660
2015
|
return scm;
|
|
1661
2016
|
}
|
|
@@ -1664,7 +2019,170 @@ var SCMLib = class {
|
|
|
1664
2019
|
throw new RepoNoTokenAccessError("no access to repo");
|
|
1665
2020
|
}
|
|
1666
2021
|
}
|
|
1667
|
-
return new StubSCMLib(trimmedUrl);
|
|
2022
|
+
return new StubSCMLib(trimmedUrl, void 0, void 0);
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
var AdoSCMLib = class extends SCMLib {
|
|
2026
|
+
updatePrComment(_params, _oktokit) {
|
|
2027
|
+
throw new Error("updatePrComment not implemented.");
|
|
2028
|
+
}
|
|
2029
|
+
getPrComment(_commentId) {
|
|
2030
|
+
throw new Error("getPrComment not implemented.");
|
|
2031
|
+
}
|
|
2032
|
+
async forkRepo() {
|
|
2033
|
+
throw new Error("forkRepo not supported yet");
|
|
2034
|
+
}
|
|
2035
|
+
async createOrUpdateRepositorySecret() {
|
|
2036
|
+
throw new Error("createOrUpdateRepositorySecret not supported yet");
|
|
2037
|
+
}
|
|
2038
|
+
async createPullRequestWithNewFile(_sourceRepoUrl, _filesPaths, _userRepoUrl, _title, _body) {
|
|
2039
|
+
throw new Error("createPullRequestWithNewFile not supported yet");
|
|
2040
|
+
}
|
|
2041
|
+
async createSubmitRequest(targetBranchName, sourceBranchName, title, body) {
|
|
2042
|
+
if (!this.accessToken || !this.url) {
|
|
2043
|
+
console.error("no access token or no url");
|
|
2044
|
+
throw new Error("no access token or no url");
|
|
2045
|
+
}
|
|
2046
|
+
return String(
|
|
2047
|
+
await createAdoPullRequest({
|
|
2048
|
+
title,
|
|
2049
|
+
body,
|
|
2050
|
+
targetBranchName,
|
|
2051
|
+
sourceBranchName,
|
|
2052
|
+
repoUrl: this.url,
|
|
2053
|
+
accessToken: this.accessToken,
|
|
2054
|
+
tokenOrg: this.scmOrg
|
|
2055
|
+
})
|
|
2056
|
+
);
|
|
2057
|
+
}
|
|
2058
|
+
async validateParams() {
|
|
2059
|
+
return adoValidateParams({
|
|
2060
|
+
url: this.url,
|
|
2061
|
+
accessToken: this.accessToken,
|
|
2062
|
+
tokenOrg: this.scmOrg
|
|
2063
|
+
});
|
|
2064
|
+
}
|
|
2065
|
+
async getRepoList(scmOrg) {
|
|
2066
|
+
if (!this.accessToken) {
|
|
2067
|
+
console.error("no access token");
|
|
2068
|
+
throw new Error("no access token");
|
|
2069
|
+
}
|
|
2070
|
+
return getAdoRepoList({
|
|
2071
|
+
orgName: scmOrg,
|
|
2072
|
+
tokenOrg: this.scmOrg,
|
|
2073
|
+
accessToken: this.accessToken
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
async getBranchList() {
|
|
2077
|
+
if (!this.accessToken || !this.url) {
|
|
2078
|
+
console.error("no access token or no url");
|
|
2079
|
+
throw new Error("no access token or no url");
|
|
2080
|
+
}
|
|
2081
|
+
return getAdoBranchList({
|
|
2082
|
+
accessToken: this.accessToken,
|
|
2083
|
+
tokenOrg: this.scmOrg,
|
|
2084
|
+
repoUrl: this.url
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
getAuthHeaders() {
|
|
2088
|
+
if (this.accessToken) {
|
|
2089
|
+
if (getAdoTokenType(this.accessToken) === "OAUTH" /* OAUTH */) {
|
|
2090
|
+
return {
|
|
2091
|
+
authorization: `Bearer ${this.accessToken}`
|
|
2092
|
+
};
|
|
2093
|
+
} else {
|
|
2094
|
+
return {
|
|
2095
|
+
authorization: `Basic ${Buffer.from(":" + this.accessToken).toString(
|
|
2096
|
+
"base64"
|
|
2097
|
+
)}`
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
return {};
|
|
2102
|
+
}
|
|
2103
|
+
getDownloadUrl(sha) {
|
|
2104
|
+
if (!this.url) {
|
|
2105
|
+
console.error("no url");
|
|
2106
|
+
throw new Error("no url");
|
|
2107
|
+
}
|
|
2108
|
+
return getAdoDownloadUrl({ repoUrl: this.url, branch: sha });
|
|
2109
|
+
}
|
|
2110
|
+
async _getUsernameForAuthUrl() {
|
|
2111
|
+
throw new Error("_getUsernameForAuthUrl() is not relevant for ADO");
|
|
2112
|
+
}
|
|
2113
|
+
async getIsRemoteBranch(branch) {
|
|
2114
|
+
if (!this.accessToken || !this.url) {
|
|
2115
|
+
console.error("no access token or no url");
|
|
2116
|
+
throw new Error("no access token or no url");
|
|
2117
|
+
}
|
|
2118
|
+
return getAdoIsRemoteBranch({
|
|
2119
|
+
accessToken: this.accessToken,
|
|
2120
|
+
tokenOrg: this.scmOrg,
|
|
2121
|
+
repoUrl: this.url,
|
|
2122
|
+
branch
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
async getUserHasAccessToRepo() {
|
|
2126
|
+
if (!this.accessToken || !this.url) {
|
|
2127
|
+
console.error("no access token or no url");
|
|
2128
|
+
throw new Error("no access token or no url");
|
|
2129
|
+
}
|
|
2130
|
+
return getAdoIsUserCollaborator({
|
|
2131
|
+
accessToken: this.accessToken,
|
|
2132
|
+
tokenOrg: this.scmOrg,
|
|
2133
|
+
repoUrl: this.url
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
async getUsername() {
|
|
2137
|
+
throw new Error("getUsername() is not relevant for ADO");
|
|
2138
|
+
}
|
|
2139
|
+
async getSubmitRequestStatus(scmSubmitRequestId) {
|
|
2140
|
+
if (!this.accessToken || !this.url) {
|
|
2141
|
+
console.error("no access token or no url");
|
|
2142
|
+
throw new Error("no access token or no url");
|
|
2143
|
+
}
|
|
2144
|
+
const state = await getAdoPullRequestStatus({
|
|
2145
|
+
accessToken: this.accessToken,
|
|
2146
|
+
tokenOrg: this.scmOrg,
|
|
2147
|
+
repoUrl: this.url,
|
|
2148
|
+
prNumber: Number(scmSubmitRequestId)
|
|
2149
|
+
});
|
|
2150
|
+
switch (state) {
|
|
2151
|
+
case "completed" /* completed */:
|
|
2152
|
+
return "MERGED" /* MERGED */;
|
|
2153
|
+
case "active" /* active */:
|
|
2154
|
+
return "OPEN" /* OPEN */;
|
|
2155
|
+
case "abandoned" /* abandoned */:
|
|
2156
|
+
return "CLOSED" /* CLOSED */;
|
|
2157
|
+
default:
|
|
2158
|
+
throw new Error(`unknown state ${state}`);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
async getRepoBlameRanges(_ref, _path) {
|
|
2162
|
+
return await getAdoBlameRanges();
|
|
2163
|
+
}
|
|
2164
|
+
async getReferenceData(ref) {
|
|
2165
|
+
if (!this.url) {
|
|
2166
|
+
console.error("no url");
|
|
2167
|
+
throw new Error("no url");
|
|
2168
|
+
}
|
|
2169
|
+
return await getAdoReferenceData({
|
|
2170
|
+
ref,
|
|
2171
|
+
repoUrl: this.url,
|
|
2172
|
+
accessToken: this.accessToken,
|
|
2173
|
+
tokenOrg: this.scmOrg
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
async getRepoDefaultBranch() {
|
|
2177
|
+
if (!this.url) {
|
|
2178
|
+
console.error("no url");
|
|
2179
|
+
throw new Error("no url");
|
|
2180
|
+
}
|
|
2181
|
+
return await getAdoRepoDefaultBranch({
|
|
2182
|
+
repoUrl: this.url,
|
|
2183
|
+
tokenOrg: this.scmOrg,
|
|
2184
|
+
accessToken: this.accessToken
|
|
2185
|
+
});
|
|
1668
2186
|
}
|
|
1669
2187
|
};
|
|
1670
2188
|
var GitlabSCMLib = class extends SCMLib {
|
|
@@ -1707,7 +2225,7 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
1707
2225
|
async createPullRequestWithNewFile(_sourceRepoUrl, _filesPaths, _userRepoUrl, _title, _body) {
|
|
1708
2226
|
throw new Error("not implemented");
|
|
1709
2227
|
}
|
|
1710
|
-
async getRepoList() {
|
|
2228
|
+
async getRepoList(_scmOrg) {
|
|
1711
2229
|
if (!this.accessToken) {
|
|
1712
2230
|
console.error("no access token");
|
|
1713
2231
|
throw new Error("no access token");
|
|
@@ -1795,13 +2313,13 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
1795
2313
|
throw new Error(`unknown state ${state}`);
|
|
1796
2314
|
}
|
|
1797
2315
|
}
|
|
1798
|
-
async getRepoBlameRanges(ref,
|
|
2316
|
+
async getRepoBlameRanges(ref, path9) {
|
|
1799
2317
|
if (!this.url) {
|
|
1800
2318
|
console.error("no url");
|
|
1801
2319
|
throw new Error("no url");
|
|
1802
2320
|
}
|
|
1803
2321
|
return await getGitlabBlameRanges(
|
|
1804
|
-
{ ref, path:
|
|
2322
|
+
{ ref, path: path9, gitlabUrl: this.url },
|
|
1805
2323
|
{
|
|
1806
2324
|
gitlabAuthToken: this.accessToken
|
|
1807
2325
|
}
|
|
@@ -1837,8 +2355,8 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
1837
2355
|
};
|
|
1838
2356
|
var GithubSCMLib = class extends SCMLib {
|
|
1839
2357
|
// we don't always need a url, what's important is that we have an access token
|
|
1840
|
-
constructor(url, accessToken) {
|
|
1841
|
-
super(url, accessToken);
|
|
2358
|
+
constructor(url, accessToken, scmOrg) {
|
|
2359
|
+
super(url, accessToken, scmOrg);
|
|
1842
2360
|
__publicField(this, "oktokit");
|
|
1843
2361
|
this.oktokit = new Octokit2({ auth: accessToken });
|
|
1844
2362
|
}
|
|
@@ -1873,7 +2391,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
1873
2391
|
throw new Error("cannot delete comment without access token or url");
|
|
1874
2392
|
}
|
|
1875
2393
|
const oktokit = _oktokit || this.oktokit;
|
|
1876
|
-
const { owner, repo } =
|
|
2394
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
1877
2395
|
const { data: repositoryPublicKeyResponse } = await getARepositoryPublicKey(
|
|
1878
2396
|
oktokit,
|
|
1879
2397
|
{
|
|
@@ -1914,7 +2432,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
1914
2432
|
throw new Error("cannot post on PR without access token or url");
|
|
1915
2433
|
}
|
|
1916
2434
|
const oktokit = _oktokit || this.oktokit;
|
|
1917
|
-
const { owner, repo } =
|
|
2435
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
1918
2436
|
return postPrComment(oktokit, {
|
|
1919
2437
|
...params,
|
|
1920
2438
|
owner,
|
|
@@ -1926,7 +2444,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
1926
2444
|
throw new Error("cannot update on PR without access token or url");
|
|
1927
2445
|
}
|
|
1928
2446
|
const oktokit = _oktokit || this.oktokit;
|
|
1929
|
-
const { owner, repo } =
|
|
2447
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
1930
2448
|
return updatePrComment(oktokit, {
|
|
1931
2449
|
...params,
|
|
1932
2450
|
owner,
|
|
@@ -1938,7 +2456,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
1938
2456
|
throw new Error("cannot delete comment without access token or url");
|
|
1939
2457
|
}
|
|
1940
2458
|
const oktokit = _oktokit || this.oktokit;
|
|
1941
|
-
const { owner, repo } =
|
|
2459
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
1942
2460
|
return deleteComment(oktokit, {
|
|
1943
2461
|
...params,
|
|
1944
2462
|
owner,
|
|
@@ -1950,7 +2468,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
1950
2468
|
throw new Error("cannot get Pr Comments without access token or url");
|
|
1951
2469
|
}
|
|
1952
2470
|
const oktokit = _oktokit || this.oktokit;
|
|
1953
|
-
const { owner, repo } =
|
|
2471
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
1954
2472
|
return getPrComments(oktokit, {
|
|
1955
2473
|
per_page: 100,
|
|
1956
2474
|
...params,
|
|
@@ -1962,15 +2480,15 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
1962
2480
|
if (!this.accessToken || !this.url) {
|
|
1963
2481
|
throw new Error("cannot get Pr Comments without access token or url");
|
|
1964
2482
|
}
|
|
1965
|
-
const { owner, repo } =
|
|
2483
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
1966
2484
|
const prRes = await getPrDiff(this.oktokit, {
|
|
1967
2485
|
...params,
|
|
1968
2486
|
owner,
|
|
1969
2487
|
repo
|
|
1970
2488
|
});
|
|
1971
|
-
return
|
|
2489
|
+
return z7.string().parse(prRes.data);
|
|
1972
2490
|
}
|
|
1973
|
-
async getRepoList() {
|
|
2491
|
+
async getRepoList(_scmOrg) {
|
|
1974
2492
|
if (!this.accessToken) {
|
|
1975
2493
|
console.error("no access token");
|
|
1976
2494
|
throw new Error("no access token");
|
|
@@ -2042,13 +2560,13 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
2042
2560
|
}
|
|
2043
2561
|
throw new Error(`unknown state ${state}`);
|
|
2044
2562
|
}
|
|
2045
|
-
async getRepoBlameRanges(ref,
|
|
2563
|
+
async getRepoBlameRanges(ref, path9) {
|
|
2046
2564
|
if (!this.url) {
|
|
2047
2565
|
console.error("no url");
|
|
2048
2566
|
throw new Error("no url");
|
|
2049
2567
|
}
|
|
2050
2568
|
return await getGithubBlameRanges(
|
|
2051
|
-
{ ref, path:
|
|
2569
|
+
{ ref, path: path9, gitHubUrl: this.url },
|
|
2052
2570
|
{
|
|
2053
2571
|
githubAuthToken: this.accessToken
|
|
2054
2572
|
}
|
|
@@ -2071,7 +2589,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
2071
2589
|
console.error("no url");
|
|
2072
2590
|
throw new Error("no url");
|
|
2073
2591
|
}
|
|
2074
|
-
const { owner, repo } =
|
|
2592
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
2075
2593
|
return await getPrComment(this.oktokit, {
|
|
2076
2594
|
repo,
|
|
2077
2595
|
owner,
|
|
@@ -2125,9 +2643,9 @@ var StubSCMLib = class extends SCMLib {
|
|
|
2125
2643
|
console.error("createPullRequestWithNewFile() not implemented");
|
|
2126
2644
|
throw new Error("createPullRequestWithNewFile() not implemented");
|
|
2127
2645
|
}
|
|
2128
|
-
async getRepoList() {
|
|
2129
|
-
console.error("
|
|
2130
|
-
throw new Error("
|
|
2646
|
+
async getRepoList(_scmOrg) {
|
|
2647
|
+
console.error("getRepoList() not implemented");
|
|
2648
|
+
throw new Error("getRepoList() not implemented");
|
|
2131
2649
|
}
|
|
2132
2650
|
async getBranchList() {
|
|
2133
2651
|
console.error("getBranchList() not implemented");
|
|
@@ -2167,196 +2685,415 @@ var StubSCMLib = class extends SCMLib {
|
|
|
2167
2685
|
}
|
|
2168
2686
|
};
|
|
2169
2687
|
|
|
2170
|
-
// src/features/analysis/scm/
|
|
2171
|
-
function
|
|
2688
|
+
// src/features/analysis/scm/ado.ts
|
|
2689
|
+
function removeTrailingSlash3(str) {
|
|
2172
2690
|
return str.trim().replace(/\/+$/, "");
|
|
2173
2691
|
}
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2692
|
+
async function _getOrgsForOauthToken({ oauthToken }) {
|
|
2693
|
+
const profileZ = z8.object({
|
|
2694
|
+
displayName: z8.string(),
|
|
2695
|
+
publicAlias: z8.string().min(1),
|
|
2696
|
+
emailAddress: z8.string(),
|
|
2697
|
+
coreRevision: z8.number(),
|
|
2698
|
+
timeStamp: z8.string(),
|
|
2699
|
+
id: z8.string(),
|
|
2700
|
+
revision: z8.number()
|
|
2701
|
+
});
|
|
2702
|
+
const accountsZ = z8.object({
|
|
2703
|
+
count: z8.number(),
|
|
2704
|
+
value: z8.array(
|
|
2705
|
+
z8.object({
|
|
2706
|
+
accountId: z8.string(),
|
|
2707
|
+
accountUri: z8.string(),
|
|
2708
|
+
accountName: z8.string()
|
|
2709
|
+
})
|
|
2710
|
+
)
|
|
2711
|
+
});
|
|
2712
|
+
const profileRes = await fetch(
|
|
2713
|
+
"https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=6.0",
|
|
2714
|
+
{
|
|
2715
|
+
method: "GET",
|
|
2716
|
+
headers: {
|
|
2717
|
+
Authorization: `Bearer ${oauthToken}`
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
);
|
|
2721
|
+
const profileJson = await profileRes.json();
|
|
2722
|
+
const profile = profileZ.parse(profileJson);
|
|
2723
|
+
const accountsRes = await fetch(
|
|
2724
|
+
`https://app.vssps.visualstudio.com/_apis/accounts?memberId=${profile.publicAlias}&api-version=6.0`,
|
|
2725
|
+
{
|
|
2726
|
+
method: "GET",
|
|
2727
|
+
headers: {
|
|
2728
|
+
Authorization: `Bearer ${oauthToken}`
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
);
|
|
2732
|
+
const accountsJson = await accountsRes.json();
|
|
2733
|
+
const accounts = accountsZ.parse(accountsJson);
|
|
2734
|
+
const orgs = accounts.value.map((account) => account.accountName).filter((value, index, array) => array.indexOf(value) === index);
|
|
2735
|
+
return orgs;
|
|
2736
|
+
}
|
|
2737
|
+
function _getPublicAdoClient({ orgName }) {
|
|
2738
|
+
const orgUrl = `https://dev.azure.com/${orgName}`;
|
|
2739
|
+
const authHandler = api.getPersonalAccessTokenHandler("");
|
|
2740
|
+
authHandler.canHandleAuthentication = () => false;
|
|
2741
|
+
authHandler.prepareRequest = (_options) => {
|
|
2742
|
+
return;
|
|
2743
|
+
};
|
|
2744
|
+
const connection = new api.WebApi(orgUrl, authHandler);
|
|
2745
|
+
return connection;
|
|
2746
|
+
}
|
|
2747
|
+
function getAdoTokenType(token) {
|
|
2748
|
+
if (token.includes(".")) {
|
|
2749
|
+
return "OAUTH" /* OAUTH */;
|
|
2182
2750
|
}
|
|
2183
|
-
return
|
|
2751
|
+
return "PAT" /* PAT */;
|
|
2184
2752
|
}
|
|
2185
|
-
async function
|
|
2753
|
+
async function getAdoApiClient({
|
|
2754
|
+
accessToken,
|
|
2755
|
+
tokenOrg,
|
|
2756
|
+
orgName
|
|
2757
|
+
}) {
|
|
2758
|
+
if (!accessToken || tokenOrg && tokenOrg !== orgName) {
|
|
2759
|
+
return _getPublicAdoClient({ orgName });
|
|
2760
|
+
}
|
|
2761
|
+
const orgUrl = `https://dev.azure.com/${orgName}`;
|
|
2762
|
+
if (getAdoTokenType(accessToken) === "OAUTH" /* OAUTH */) {
|
|
2763
|
+
const connection2 = new api.WebApi(orgUrl, api.getBearerHandler(accessToken));
|
|
2764
|
+
return connection2;
|
|
2765
|
+
}
|
|
2766
|
+
const authHandler = api.getPersonalAccessTokenHandler(accessToken);
|
|
2767
|
+
const connection = new api.WebApi(orgUrl, authHandler);
|
|
2768
|
+
return connection;
|
|
2769
|
+
}
|
|
2770
|
+
async function adoValidateParams({
|
|
2186
2771
|
url,
|
|
2187
|
-
accessToken
|
|
2772
|
+
accessToken,
|
|
2773
|
+
tokenOrg
|
|
2188
2774
|
}) {
|
|
2189
2775
|
try {
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2776
|
+
if (!url && accessToken && getAdoTokenType(accessToken) === "OAUTH" /* OAUTH */) {
|
|
2777
|
+
await _getOrgsForOauthToken({ oauthToken: accessToken });
|
|
2778
|
+
return;
|
|
2193
2779
|
}
|
|
2780
|
+
let org = tokenOrg;
|
|
2194
2781
|
if (url) {
|
|
2195
|
-
const {
|
|
2196
|
-
|
|
2782
|
+
const { owner } = parseAdoOwnerAndRepo(url);
|
|
2783
|
+
org = owner;
|
|
2784
|
+
}
|
|
2785
|
+
if (!org) {
|
|
2786
|
+
throw new InvalidRepoUrlError(`invalid ADO ORG ${org}`);
|
|
2197
2787
|
}
|
|
2788
|
+
const api2 = await getAdoApiClient({
|
|
2789
|
+
accessToken,
|
|
2790
|
+
tokenOrg,
|
|
2791
|
+
orgName: org
|
|
2792
|
+
});
|
|
2793
|
+
await api2.connect();
|
|
2198
2794
|
} catch (e) {
|
|
2199
2795
|
const error = e;
|
|
2200
2796
|
const code = error.code || error.status || error.statusCode || error.response?.status || error.response?.statusCode || error.response?.code;
|
|
2201
2797
|
const description = error.description || `${e}`;
|
|
2202
2798
|
if (code === 401 || code === 403 || description.includes("401") || description.includes("403")) {
|
|
2203
|
-
throw new InvalidAccessTokenError(`invalid
|
|
2799
|
+
throw new InvalidAccessTokenError(`invalid ADO access token`);
|
|
2204
2800
|
}
|
|
2205
2801
|
if (code === 404 || description.includes("404") || description.includes("Not Found")) {
|
|
2206
|
-
throw new InvalidRepoUrlError(`invalid
|
|
2802
|
+
throw new InvalidRepoUrlError(`invalid ADO repo URL ${url}`);
|
|
2207
2803
|
}
|
|
2208
2804
|
throw e;
|
|
2209
2805
|
}
|
|
2210
2806
|
}
|
|
2211
|
-
async function
|
|
2212
|
-
const api = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
2213
|
-
const res = await api.Users.showCurrentUser();
|
|
2214
|
-
return res.username;
|
|
2215
|
-
}
|
|
2216
|
-
async function getGitlabIsUserCollaborator({
|
|
2217
|
-
username,
|
|
2807
|
+
async function getAdoIsUserCollaborator({
|
|
2218
2808
|
accessToken,
|
|
2809
|
+
tokenOrg,
|
|
2219
2810
|
repoUrl
|
|
2220
2811
|
}) {
|
|
2221
2812
|
try {
|
|
2222
|
-
const {
|
|
2223
|
-
const
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2813
|
+
const { owner, repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
2814
|
+
const api2 = await getAdoApiClient({
|
|
2815
|
+
accessToken,
|
|
2816
|
+
tokenOrg,
|
|
2817
|
+
orgName: owner
|
|
2227
2818
|
});
|
|
2228
|
-
|
|
2819
|
+
const git = await api2.getGitApi();
|
|
2820
|
+
const branches = await git.getBranches(repo, projectName);
|
|
2821
|
+
if (!branches || branches.length === 0) {
|
|
2822
|
+
throw new InvalidRepoUrlError("no branches");
|
|
2823
|
+
}
|
|
2824
|
+
return true;
|
|
2229
2825
|
} catch (e) {
|
|
2230
2826
|
return false;
|
|
2231
2827
|
}
|
|
2232
2828
|
}
|
|
2233
|
-
|
|
2829
|
+
var adoStatusNumberToEnumMap = {
|
|
2830
|
+
1: "active" /* active */,
|
|
2831
|
+
2: "abandoned" /* abandoned */,
|
|
2832
|
+
3: "completed" /* completed */,
|
|
2833
|
+
4: "all" /* all */
|
|
2834
|
+
};
|
|
2835
|
+
async function getAdoPullRequestStatus({
|
|
2234
2836
|
accessToken,
|
|
2837
|
+
tokenOrg,
|
|
2235
2838
|
repoUrl,
|
|
2236
|
-
|
|
2839
|
+
prNumber
|
|
2237
2840
|
}) {
|
|
2238
|
-
const {
|
|
2239
|
-
const
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2841
|
+
const { owner, repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
2842
|
+
const api2 = await getAdoApiClient({
|
|
2843
|
+
accessToken,
|
|
2844
|
+
tokenOrg,
|
|
2845
|
+
orgName: owner
|
|
2846
|
+
});
|
|
2847
|
+
const git = await api2.getGitApi();
|
|
2848
|
+
const res = await git.getPullRequest(repo, prNumber, projectName);
|
|
2849
|
+
if (!res.status || res.status < 1 || res.status > 3) {
|
|
2850
|
+
throw new Error("bad pr status for ADO");
|
|
2248
2851
|
}
|
|
2852
|
+
return adoStatusNumberToEnumMap[res.status];
|
|
2249
2853
|
}
|
|
2250
|
-
async function
|
|
2854
|
+
async function getAdoIsRemoteBranch({
|
|
2251
2855
|
accessToken,
|
|
2856
|
+
tokenOrg,
|
|
2252
2857
|
repoUrl,
|
|
2253
2858
|
branch
|
|
2254
2859
|
}) {
|
|
2255
|
-
const {
|
|
2256
|
-
const
|
|
2860
|
+
const { owner, repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
2861
|
+
const api2 = await getAdoApiClient({
|
|
2862
|
+
accessToken,
|
|
2863
|
+
tokenOrg,
|
|
2864
|
+
orgName: owner
|
|
2865
|
+
});
|
|
2866
|
+
const git = await api2.getGitApi();
|
|
2257
2867
|
try {
|
|
2258
|
-
const
|
|
2259
|
-
|
|
2868
|
+
const branchStatus = await git.getBranch(repo, branch, projectName);
|
|
2869
|
+
if (!branchStatus || !branchStatus.commit) {
|
|
2870
|
+
throw new InvalidRepoUrlError("no branch status");
|
|
2871
|
+
}
|
|
2872
|
+
return branchStatus.name === branch;
|
|
2260
2873
|
} catch (e) {
|
|
2261
2874
|
return false;
|
|
2262
2875
|
}
|
|
2263
2876
|
}
|
|
2264
|
-
async function
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
const
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2877
|
+
async function getAdoRepoList({
|
|
2878
|
+
orgName,
|
|
2879
|
+
tokenOrg,
|
|
2880
|
+
accessToken
|
|
2881
|
+
}) {
|
|
2882
|
+
let orgs = [];
|
|
2883
|
+
if (getAdoTokenType(accessToken) === "OAUTH" /* OAUTH */) {
|
|
2884
|
+
orgs = await _getOrgsForOauthToken({ oauthToken: accessToken });
|
|
2885
|
+
}
|
|
2886
|
+
if (orgs.length === 0 && !orgName) {
|
|
2887
|
+
throw new Error(`no orgs for ADO`);
|
|
2888
|
+
} else if (orgs.length === 0 && orgName) {
|
|
2889
|
+
orgs = [orgName];
|
|
2890
|
+
}
|
|
2891
|
+
const repos = (await Promise.allSettled(
|
|
2892
|
+
orgs.map(async (org) => {
|
|
2893
|
+
const orgApi = await getAdoApiClient({
|
|
2894
|
+
accessToken,
|
|
2895
|
+
tokenOrg,
|
|
2896
|
+
orgName: org
|
|
2897
|
+
});
|
|
2898
|
+
const gitOrg = await orgApi.getGitApi();
|
|
2899
|
+
const orgRepos = await gitOrg.getRepositories();
|
|
2900
|
+
const repoInfoList = (await Promise.allSettled(
|
|
2901
|
+
orgRepos.map(async (repo) => {
|
|
2902
|
+
if (!repo.name || !repo.remoteUrl || !repo.defaultBranch) {
|
|
2903
|
+
throw new InvalidRepoUrlError("bad repo");
|
|
2904
|
+
}
|
|
2905
|
+
const branch = await gitOrg.getBranch(
|
|
2906
|
+
repo.name,
|
|
2907
|
+
repo.defaultBranch.replace(/^refs\/heads\//, ""),
|
|
2908
|
+
repo.project?.name
|
|
2909
|
+
);
|
|
2910
|
+
return {
|
|
2911
|
+
repoName: repo.name,
|
|
2912
|
+
repoUrl: repo.remoteUrl.replace(
|
|
2913
|
+
/^[hH][tT][tT][pP][sS]:\/\/[^/]+@/,
|
|
2914
|
+
"https://"
|
|
2915
|
+
),
|
|
2916
|
+
repoOwner: org,
|
|
2917
|
+
repoIsPublic: repo.project?.visibility === 2,
|
|
2918
|
+
//2 is public in the ADO API
|
|
2919
|
+
repoLanguages: [],
|
|
2920
|
+
repoUpdatedAt: branch.commit?.committer?.date?.toDateString() || repo.project?.lastUpdateTime?.toDateString() || (/* @__PURE__ */ new Date()).toDateString()
|
|
2921
|
+
};
|
|
2922
|
+
})
|
|
2923
|
+
)).reduce((acc, res) => {
|
|
2924
|
+
if (res.status === "fulfilled") {
|
|
2925
|
+
acc.push(res.value);
|
|
2926
|
+
}
|
|
2927
|
+
return acc;
|
|
2928
|
+
}, []);
|
|
2929
|
+
return repoInfoList;
|
|
2290
2930
|
})
|
|
2291
|
-
)
|
|
2931
|
+
)).reduce((acc, res) => {
|
|
2932
|
+
if (res.status === "fulfilled") {
|
|
2933
|
+
return acc.concat(res.value);
|
|
2934
|
+
}
|
|
2935
|
+
return acc;
|
|
2936
|
+
}, []);
|
|
2937
|
+
return repos;
|
|
2292
2938
|
}
|
|
2293
|
-
|
|
2939
|
+
function getAdoDownloadUrl({
|
|
2940
|
+
repoUrl,
|
|
2941
|
+
branch
|
|
2942
|
+
}) {
|
|
2943
|
+
const { owner, repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
2944
|
+
return `https://dev.azure.com/${owner}/${projectName}/_apis/git/repositories/${repo}/items/items?path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
2945
|
+
}
|
|
2946
|
+
async function getAdoBranchList({
|
|
2294
2947
|
accessToken,
|
|
2948
|
+
tokenOrg,
|
|
2295
2949
|
repoUrl
|
|
2296
2950
|
}) {
|
|
2297
|
-
const {
|
|
2298
|
-
const
|
|
2951
|
+
const { owner, repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
2952
|
+
const api2 = await getAdoApiClient({
|
|
2953
|
+
accessToken,
|
|
2954
|
+
tokenOrg,
|
|
2955
|
+
orgName: owner
|
|
2956
|
+
});
|
|
2957
|
+
const git = await api2.getGitApi();
|
|
2299
2958
|
try {
|
|
2300
|
-
const res = await
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2959
|
+
const res = await git.getBranches(repo, projectName);
|
|
2960
|
+
res.sort((a, b) => {
|
|
2961
|
+
if (!a.commit?.committer?.date || !b.commit?.committer?.date) {
|
|
2962
|
+
return 0;
|
|
2963
|
+
}
|
|
2964
|
+
return b.commit?.committer?.date.getTime() - a.commit?.committer?.date.getTime();
|
|
2305
2965
|
});
|
|
2306
|
-
return res.
|
|
2966
|
+
return res.reduce((acc, branch) => {
|
|
2967
|
+
if (!branch.name) {
|
|
2968
|
+
return acc;
|
|
2969
|
+
}
|
|
2970
|
+
acc.push(branch.name);
|
|
2971
|
+
return acc;
|
|
2972
|
+
}, []);
|
|
2307
2973
|
} catch (e) {
|
|
2308
2974
|
return [];
|
|
2309
2975
|
}
|
|
2310
2976
|
}
|
|
2311
|
-
async function
|
|
2312
|
-
const {
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2977
|
+
async function createAdoPullRequest(options) {
|
|
2978
|
+
const { owner, repo, projectName } = parseAdoOwnerAndRepo(options.repoUrl);
|
|
2979
|
+
const api2 = await getAdoApiClient({
|
|
2980
|
+
accessToken: options.accessToken,
|
|
2981
|
+
tokenOrg: options.tokenOrg,
|
|
2982
|
+
orgName: owner
|
|
2983
|
+
});
|
|
2984
|
+
const git = await api2.getGitApi();
|
|
2985
|
+
const res = await git.createPullRequest(
|
|
2319
2986
|
{
|
|
2987
|
+
sourceRefName: `refs/heads/${options.sourceBranchName}`,
|
|
2988
|
+
targetRefName: `refs/heads/${options.targetBranchName}`,
|
|
2989
|
+
title: options.title,
|
|
2320
2990
|
description: options.body
|
|
2321
|
-
}
|
|
2991
|
+
},
|
|
2992
|
+
repo,
|
|
2993
|
+
projectName
|
|
2322
2994
|
);
|
|
2323
|
-
return res.
|
|
2995
|
+
return res.pullRequestId;
|
|
2324
2996
|
}
|
|
2325
|
-
async function
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2997
|
+
async function getAdoRepoDefaultBranch({
|
|
2998
|
+
repoUrl,
|
|
2999
|
+
tokenOrg,
|
|
3000
|
+
accessToken
|
|
3001
|
+
}) {
|
|
3002
|
+
const { owner, repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
3003
|
+
const api2 = await getAdoApiClient({
|
|
3004
|
+
accessToken,
|
|
3005
|
+
tokenOrg,
|
|
3006
|
+
orgName: owner
|
|
3007
|
+
});
|
|
3008
|
+
const git = await api2.getGitApi();
|
|
3009
|
+
const branches = await git.getBranches(repo, projectName);
|
|
3010
|
+
if (!branches || branches.length === 0) {
|
|
3011
|
+
throw new InvalidRepoUrlError("no branches");
|
|
2331
3012
|
}
|
|
2332
|
-
|
|
3013
|
+
const res = branches.find((branch) => branch.isBaseVersion);
|
|
3014
|
+
if (!res || !res.name) {
|
|
3015
|
+
throw new InvalidRepoUrlError("no default branch");
|
|
3016
|
+
}
|
|
3017
|
+
return res.name;
|
|
2333
3018
|
}
|
|
2334
|
-
async function
|
|
2335
|
-
|
|
2336
|
-
|
|
3019
|
+
async function getAdoReferenceData({
|
|
3020
|
+
ref,
|
|
3021
|
+
repoUrl,
|
|
3022
|
+
accessToken,
|
|
3023
|
+
tokenOrg
|
|
3024
|
+
}) {
|
|
3025
|
+
const { owner, repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
3026
|
+
const api2 = await getAdoApiClient({
|
|
3027
|
+
accessToken,
|
|
3028
|
+
tokenOrg,
|
|
3029
|
+
orgName: owner
|
|
3030
|
+
});
|
|
3031
|
+
if (!projectName) {
|
|
3032
|
+
throw new InvalidUrlPatternError("no project name");
|
|
3033
|
+
}
|
|
3034
|
+
const git = await api2.getGitApi();
|
|
2337
3035
|
const results = await Promise.allSettled([
|
|
2338
3036
|
(async () => {
|
|
2339
|
-
const res = await
|
|
3037
|
+
const res = await git.getBranch(repo, ref, projectName);
|
|
3038
|
+
if (!res.commit || !res.commit.commitId) {
|
|
3039
|
+
throw new InvalidRepoUrlError("no commit on branch");
|
|
3040
|
+
}
|
|
2340
3041
|
return {
|
|
2341
|
-
sha: res.commit.
|
|
3042
|
+
sha: res.commit.commitId,
|
|
2342
3043
|
type: "BRANCH" /* BRANCH */,
|
|
2343
|
-
date: res.commit.
|
|
3044
|
+
date: res.commit.committer?.date || /* @__PURE__ */ new Date()
|
|
2344
3045
|
};
|
|
2345
3046
|
})(),
|
|
2346
3047
|
(async () => {
|
|
2347
|
-
const res = await
|
|
3048
|
+
const res = await git.getCommits(
|
|
3049
|
+
repo,
|
|
3050
|
+
{
|
|
3051
|
+
fromCommitId: ref,
|
|
3052
|
+
toCommitId: ref,
|
|
3053
|
+
$top: 1
|
|
3054
|
+
},
|
|
3055
|
+
projectName
|
|
3056
|
+
);
|
|
3057
|
+
const commit = res[0];
|
|
3058
|
+
if (!commit || !commit.commitId) {
|
|
3059
|
+
throw new Error("no commit");
|
|
3060
|
+
}
|
|
2348
3061
|
return {
|
|
2349
|
-
sha:
|
|
3062
|
+
sha: commit.commitId,
|
|
2350
3063
|
type: "COMMIT" /* COMMIT */,
|
|
2351
|
-
date:
|
|
3064
|
+
date: commit.committer?.date || /* @__PURE__ */ new Date()
|
|
2352
3065
|
};
|
|
2353
3066
|
})(),
|
|
2354
3067
|
(async () => {
|
|
2355
|
-
const res = await
|
|
3068
|
+
const res = await git.getRefs(repo, projectName, `tags/${ref}`);
|
|
3069
|
+
if (!res[0] || !res[0].objectId) {
|
|
3070
|
+
throw new Error("no tag ref");
|
|
3071
|
+
}
|
|
3072
|
+
let objectId = res[0].objectId;
|
|
3073
|
+
try {
|
|
3074
|
+
const tag = await git.getAnnotatedTag(projectName, repo, objectId);
|
|
3075
|
+
if (tag.taggedObject?.objectId) {
|
|
3076
|
+
objectId = tag.taggedObject.objectId;
|
|
3077
|
+
}
|
|
3078
|
+
} catch (e) {
|
|
3079
|
+
}
|
|
3080
|
+
const commitRes2 = await git.getCommits(
|
|
3081
|
+
repo,
|
|
3082
|
+
{
|
|
3083
|
+
fromCommitId: objectId,
|
|
3084
|
+
toCommitId: objectId,
|
|
3085
|
+
$top: 1
|
|
3086
|
+
},
|
|
3087
|
+
projectName
|
|
3088
|
+
);
|
|
3089
|
+
const commit = commitRes2[0];
|
|
3090
|
+
if (!commit) {
|
|
3091
|
+
throw new Error("no commit");
|
|
3092
|
+
}
|
|
2356
3093
|
return {
|
|
2357
|
-
sha:
|
|
3094
|
+
sha: objectId,
|
|
2358
3095
|
type: "TAG" /* TAG */,
|
|
2359
|
-
date:
|
|
3096
|
+
date: commit.committer?.date || /* @__PURE__ */ new Date()
|
|
2360
3097
|
};
|
|
2361
3098
|
})()
|
|
2362
3099
|
]);
|
|
@@ -2372,41 +3109,34 @@ async function getGitlabReferenceData({ ref, gitlabUrl }, options) {
|
|
|
2372
3109
|
}
|
|
2373
3110
|
throw new RefNotFoundError(`ref: ${ref} does not exist`);
|
|
2374
3111
|
}
|
|
2375
|
-
function
|
|
2376
|
-
|
|
2377
|
-
const parsingResult = parseScmURL(
|
|
2378
|
-
if (!parsingResult || parsingResult.hostname !== "
|
|
2379
|
-
throw new InvalidUrlPatternError(`invalid
|
|
3112
|
+
function parseAdoOwnerAndRepo(adoUrl) {
|
|
3113
|
+
adoUrl = removeTrailingSlash3(adoUrl);
|
|
3114
|
+
const parsingResult = parseScmURL(adoUrl);
|
|
3115
|
+
if (!parsingResult || parsingResult.hostname !== "dev.azure.com" && !parsingResult.hostname.endsWith(".visualstudio.com")) {
|
|
3116
|
+
throw new InvalidUrlPatternError(`invalid ADO repo URL: ${adoUrl}`);
|
|
2380
3117
|
}
|
|
2381
|
-
const { organization, repoName, projectPath } = parsingResult;
|
|
2382
|
-
return {
|
|
3118
|
+
const { organization, repoName, projectName, projectPath, pathElements } = parsingResult;
|
|
3119
|
+
return {
|
|
3120
|
+
owner: organization,
|
|
3121
|
+
repo: repoName,
|
|
3122
|
+
projectName,
|
|
3123
|
+
projectPath,
|
|
3124
|
+
pathElements
|
|
3125
|
+
};
|
|
2383
3126
|
}
|
|
2384
|
-
async function
|
|
2385
|
-
|
|
2386
|
-
const api = getGitBeaker({ gitlabAuthToken: options?.gitlabAuthToken });
|
|
2387
|
-
const resp = await api.RepositoryFiles.allFileBlames(projectPath, path8, ref);
|
|
2388
|
-
let lineNumber = 1;
|
|
2389
|
-
return resp.filter((range) => range.lines).map((range) => {
|
|
2390
|
-
const oldLineNumber = lineNumber;
|
|
2391
|
-
if (!range.lines) {
|
|
2392
|
-
throw new Error("range.lines should not be undefined");
|
|
2393
|
-
}
|
|
2394
|
-
lineNumber += range.lines.length;
|
|
2395
|
-
return {
|
|
2396
|
-
startingLine: oldLineNumber,
|
|
2397
|
-
endingLine: lineNumber - 1,
|
|
2398
|
-
login: range.commit.author_email,
|
|
2399
|
-
email: range.commit.author_email,
|
|
2400
|
-
name: range.commit.author_name
|
|
2401
|
-
};
|
|
2402
|
-
});
|
|
3127
|
+
async function getAdoBlameRanges() {
|
|
3128
|
+
return [];
|
|
2403
3129
|
}
|
|
2404
|
-
var
|
|
2405
|
-
access_token:
|
|
2406
|
-
token_type:
|
|
2407
|
-
refresh_token:
|
|
3130
|
+
var AdoAuthResultZ = z8.object({
|
|
3131
|
+
access_token: z8.string().min(1),
|
|
3132
|
+
token_type: z8.string().min(1),
|
|
3133
|
+
refresh_token: z8.string().min(1)
|
|
2408
3134
|
});
|
|
2409
3135
|
|
|
3136
|
+
// src/features/analysis/scm/constants.ts
|
|
3137
|
+
var MOBB_ICON_IMG = "https://svgshare.com/i/12DK.svg";
|
|
3138
|
+
var COMMIT_FIX_SVG = `https://app.mobb.ai/gh-action/commit-button.svg`;
|
|
3139
|
+
|
|
2410
3140
|
// src/features/analysis/scm/utils/get_issue_type.ts
|
|
2411
3141
|
var getIssueType = (issueType) => {
|
|
2412
3142
|
switch (issueType) {
|
|
@@ -2569,7 +3299,7 @@ async function getFixesFromDiff(params) {
|
|
|
2569
3299
|
});
|
|
2570
3300
|
const lineAddedRanges = calculateRanges(fileNumbers);
|
|
2571
3301
|
const fileFilter = {
|
|
2572
|
-
path:
|
|
3302
|
+
path: z9.string().parse(file.to),
|
|
2573
3303
|
ranges: lineAddedRanges.map(([startLine, endLine]) => ({
|
|
2574
3304
|
endLine,
|
|
2575
3305
|
startLine
|
|
@@ -2629,7 +3359,7 @@ async function handleFinishedAnalysis({
|
|
|
2629
3359
|
return Promise.all(
|
|
2630
3360
|
vulnerabilityReportIssueCodeNodes.map(
|
|
2631
3361
|
async (vulnerabilityReportIssueCodeNodes2) => {
|
|
2632
|
-
const { path:
|
|
3362
|
+
const { path: path9, startLine, vulnerabilityReportIssue } = vulnerabilityReportIssueCodeNodes2;
|
|
2633
3363
|
const { fixId } = vulnerabilityReportIssue;
|
|
2634
3364
|
const { fix_by_pk } = await gqlClient.getFix(fixId);
|
|
2635
3365
|
const {
|
|
@@ -2640,7 +3370,7 @@ async function handleFinishedAnalysis({
|
|
|
2640
3370
|
body: "empty",
|
|
2641
3371
|
pull_number: pullRequest,
|
|
2642
3372
|
commit_id: commitSha,
|
|
2643
|
-
path:
|
|
3373
|
+
path: path9,
|
|
2644
3374
|
line: startLine
|
|
2645
3375
|
},
|
|
2646
3376
|
githubActionOctokit
|
|
@@ -2998,8 +3728,8 @@ async function forkSnyk(args, { display }) {
|
|
|
2998
3728
|
}
|
|
2999
3729
|
async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
3000
3730
|
debug7("get snyk report start %s %s", reportPath, repoRoot);
|
|
3001
|
-
const
|
|
3002
|
-
const { message: configMessage } =
|
|
3731
|
+
const config4 = await forkSnyk(["config"], { display: false });
|
|
3732
|
+
const { message: configMessage } = config4;
|
|
3003
3733
|
if (!configMessage.includes("api: ")) {
|
|
3004
3734
|
const snykLoginSpinner = createSpinner3().start();
|
|
3005
3735
|
if (!skipPrompts) {
|
|
@@ -3011,7 +3741,7 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
|
3011
3741
|
snykLoginSpinner.update({
|
|
3012
3742
|
text: "\u{1F513} Waiting for Snyk login to complete"
|
|
3013
3743
|
});
|
|
3014
|
-
debug7("no token in the config %s",
|
|
3744
|
+
debug7("no token in the config %s", config4);
|
|
3015
3745
|
await forkSnyk(["auth"], { display: true });
|
|
3016
3746
|
snykLoginSpinner.success({ text: "\u{1F513} Login to Snyk Successful" });
|
|
3017
3747
|
}
|
|
@@ -3083,8 +3813,6 @@ async function uploadFile({
|
|
|
3083
3813
|
// src/features/analysis/index.ts
|
|
3084
3814
|
var { CliError: CliError2, Spinner: Spinner2, keypress: keypress2, getDirName: getDirName2 } = utils_exports;
|
|
3085
3815
|
var webLoginUrl = `${WEB_APP_URL}/cli-login`;
|
|
3086
|
-
var githubAuthUrl = `${WEB_APP_URL}/github-auth`;
|
|
3087
|
-
var gitlabAuthUrl = `${WEB_APP_URL}/gitlab-auth`;
|
|
3088
3816
|
async function downloadRepo({
|
|
3089
3817
|
repoUrl,
|
|
3090
3818
|
authHeaders,
|
|
@@ -3096,6 +3824,7 @@ async function downloadRepo({
|
|
|
3096
3824
|
const repoSpinner = createSpinner4("\u{1F4BE} Downloading Repo").start();
|
|
3097
3825
|
debug9("download repo %s %s %s", repoUrl, dirname);
|
|
3098
3826
|
const zipFilePath = path6.join(dirname, "repo.zip");
|
|
3827
|
+
debug9("download URL: %s auth headers: %o", downloadUrl, authHeaders);
|
|
3099
3828
|
const response = await fetch3(downloadUrl, {
|
|
3100
3829
|
method: "GET",
|
|
3101
3830
|
headers: {
|
|
@@ -3158,6 +3887,38 @@ async function runAnalysis(params, options) {
|
|
|
3158
3887
|
tmpObj.removeCallback();
|
|
3159
3888
|
}
|
|
3160
3889
|
}
|
|
3890
|
+
function _getTokenAndUrlForScmType({
|
|
3891
|
+
scmLibType,
|
|
3892
|
+
githubToken,
|
|
3893
|
+
gitlabToken,
|
|
3894
|
+
adoToken
|
|
3895
|
+
}) {
|
|
3896
|
+
const githubAuthUrl = `${WEB_APP_URL}/github-auth`;
|
|
3897
|
+
const gitlabAuthUrl = `${WEB_APP_URL}/gitlab-auth`;
|
|
3898
|
+
const adoAuthUrl = `${WEB_APP_URL}/ado-auth`;
|
|
3899
|
+
switch (scmLibType) {
|
|
3900
|
+
case "GITHUB" /* GITHUB */:
|
|
3901
|
+
return {
|
|
3902
|
+
token: githubToken,
|
|
3903
|
+
authUrl: githubAuthUrl
|
|
3904
|
+
};
|
|
3905
|
+
case "GITLAB" /* GITLAB */:
|
|
3906
|
+
return {
|
|
3907
|
+
token: gitlabToken,
|
|
3908
|
+
authUrl: gitlabAuthUrl
|
|
3909
|
+
};
|
|
3910
|
+
case "ADO" /* ADO */:
|
|
3911
|
+
return {
|
|
3912
|
+
token: adoToken,
|
|
3913
|
+
authUrl: adoAuthUrl
|
|
3914
|
+
};
|
|
3915
|
+
default:
|
|
3916
|
+
return {
|
|
3917
|
+
token: void 0,
|
|
3918
|
+
authUrl: void 0
|
|
3919
|
+
};
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3161
3922
|
async function _scan(params, { skipPrompts = false } = {}) {
|
|
3162
3923
|
const {
|
|
3163
3924
|
dirname,
|
|
@@ -3196,26 +3957,35 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
3196
3957
|
throw new Error("repo is required in case srcPath is not provided");
|
|
3197
3958
|
}
|
|
3198
3959
|
const userInfo = await gqlClient.getUserInfo();
|
|
3199
|
-
const { githubToken, gitlabToken } = userInfo;
|
|
3960
|
+
const { githubToken, gitlabToken, adoToken, adoOrg } = userInfo;
|
|
3200
3961
|
const isRepoAvailable = await scmCanReachRepo({
|
|
3201
3962
|
repoUrl: repo,
|
|
3202
3963
|
githubToken,
|
|
3203
|
-
gitlabToken
|
|
3964
|
+
gitlabToken,
|
|
3965
|
+
adoToken,
|
|
3966
|
+
scmOrg: adoOrg
|
|
3204
3967
|
});
|
|
3205
3968
|
const scmLibType = getScmLibTypeFromUrl(repo);
|
|
3206
|
-
const
|
|
3207
|
-
|
|
3969
|
+
const { authUrl: scmAuthUrl, token } = _getTokenAndUrlForScmType({
|
|
3970
|
+
scmLibType,
|
|
3971
|
+
githubToken,
|
|
3972
|
+
gitlabToken,
|
|
3973
|
+
adoToken
|
|
3974
|
+
});
|
|
3975
|
+
let myToken = token;
|
|
3208
3976
|
if (!isRepoAvailable) {
|
|
3209
3977
|
if (ci || !scmLibType || !scmAuthUrl) {
|
|
3210
3978
|
const errorMessage = scmAuthUrl ? `Cannot access repo ${repo}` : `Cannot access repo ${repo} with the provided token, please visit ${scmAuthUrl} to refresh your source control management system token`;
|
|
3211
3979
|
throw new Error(errorMessage);
|
|
3212
3980
|
}
|
|
3213
3981
|
if (scmLibType && scmAuthUrl) {
|
|
3214
|
-
|
|
3982
|
+
myToken = await handleScmIntegration(token, scmLibType, scmAuthUrl) || "";
|
|
3215
3983
|
const isRepoAvailable2 = await scmCanReachRepo({
|
|
3216
3984
|
repoUrl: repo,
|
|
3217
|
-
githubToken:
|
|
3218
|
-
gitlabToken:
|
|
3985
|
+
githubToken: myToken,
|
|
3986
|
+
gitlabToken: myToken,
|
|
3987
|
+
adoToken: myToken,
|
|
3988
|
+
scmOrg: adoOrg
|
|
3219
3989
|
});
|
|
3220
3990
|
if (!isRepoAvailable2) {
|
|
3221
3991
|
throw new Error(
|
|
@@ -3227,7 +3997,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
3227
3997
|
const scm = await SCMLib.init({
|
|
3228
3998
|
url: repo,
|
|
3229
3999
|
accessToken: token,
|
|
3230
|
-
scmType: scmLibType
|
|
4000
|
+
scmType: scmLibType,
|
|
4001
|
+
scmOrg: adoOrg
|
|
3231
4002
|
});
|
|
3232
4003
|
const reference = ref ?? await scm.getRepoDefaultBranch();
|
|
3233
4004
|
const { sha } = await scm.getReferenceData(reference);
|
|
@@ -3270,7 +4041,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
3270
4041
|
gqlClient,
|
|
3271
4042
|
scm,
|
|
3272
4043
|
githubActionOctokit: new Octokit3({ auth: githubActionToken }),
|
|
3273
|
-
scanner:
|
|
4044
|
+
scanner: z10.nativeEnum(SCANNERS).parse(scanner)
|
|
3274
4045
|
})
|
|
3275
4046
|
);
|
|
3276
4047
|
}
|
|
@@ -3282,7 +4053,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
3282
4053
|
try {
|
|
3283
4054
|
const sumbitRes = await gqlClient.submitVulnerabilityReport({
|
|
3284
4055
|
fixReportId: reportUploadInfo.fixReportId,
|
|
3285
|
-
repoUrl:
|
|
4056
|
+
repoUrl: z10.string().parse(repo),
|
|
3286
4057
|
reference,
|
|
3287
4058
|
projectId,
|
|
3288
4059
|
vulnerabilityReportFileName: "report.json",
|
|
@@ -3400,7 +4171,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
3400
4171
|
}
|
|
3401
4172
|
}
|
|
3402
4173
|
async function handleScmIntegration(oldToken, scmLibType2, scmAuthUrl2) {
|
|
3403
|
-
const scmName = scmLibType2 === "GITHUB" /* GITHUB */ ? "Github" : scmLibType2 === "GITLAB" /* GITLAB */ ? "Gitlab" : "";
|
|
4174
|
+
const scmName = scmLibType2 === "GITHUB" /* GITHUB */ ? "Github" : scmLibType2 === "GITLAB" /* GITLAB */ ? "Gitlab" : scmLibType2 === "ADO" /* ADO */ ? "Azure DevOps" : "";
|
|
3404
4175
|
const addScmIntegration = skipPrompts ? true : await scmIntegrationPrompt(scmName);
|
|
3405
4176
|
const scmSpinner = createSpinner4(
|
|
3406
4177
|
`\u{1F517} Waiting for ${scmName} integration...`
|
|
@@ -3513,6 +4284,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
3513
4284
|
|
|
3514
4285
|
// src/commands/index.ts
|
|
3515
4286
|
import chalkAnimation from "chalk-animation";
|
|
4287
|
+
import Configstore2 from "configstore";
|
|
4288
|
+
var { getDirName: getDirName3 } = utils_exports;
|
|
3516
4289
|
async function review(params, { skipPrompts = true } = {}) {
|
|
3517
4290
|
const {
|
|
3518
4291
|
repo,
|
|
@@ -3569,6 +4342,23 @@ async function analyze({
|
|
|
3569
4342
|
{ skipPrompts }
|
|
3570
4343
|
);
|
|
3571
4344
|
}
|
|
4345
|
+
var packageJson2 = JSON.parse(
|
|
4346
|
+
fs4.readFileSync(path7.join(getDirName3(), "../package.json"), "utf8")
|
|
4347
|
+
);
|
|
4348
|
+
var config3 = new Configstore2(packageJson2.name, { apiToken: "" });
|
|
4349
|
+
async function addScmToken(addScmTokenOptions) {
|
|
4350
|
+
const { apiKey, token, organization, scm, username, refreshToken } = addScmTokenOptions;
|
|
4351
|
+
const gqlClient = new GQLClient({
|
|
4352
|
+
apiKey: apiKey || config3.get("apiToken")
|
|
4353
|
+
});
|
|
4354
|
+
await gqlClient.updateScmToken({
|
|
4355
|
+
type: scm,
|
|
4356
|
+
token,
|
|
4357
|
+
org: organization,
|
|
4358
|
+
username,
|
|
4359
|
+
refreshToken
|
|
4360
|
+
});
|
|
4361
|
+
}
|
|
3572
4362
|
async function scan(scanOptions, { skipPrompts = false } = {}) {
|
|
3573
4363
|
const { scanner, ci } = scanOptions;
|
|
3574
4364
|
!ci && await showWelcomeMessage(skipPrompts);
|
|
@@ -3603,7 +4393,7 @@ var repoOption = {
|
|
|
3603
4393
|
alias: "r",
|
|
3604
4394
|
demandOption: true,
|
|
3605
4395
|
type: "string",
|
|
3606
|
-
describe: chalk5.bold("Github / GitLab repository URL")
|
|
4396
|
+
describe: chalk5.bold("Github / GitLab / Azure DevOps repository URL")
|
|
3607
4397
|
};
|
|
3608
4398
|
var projectNameOption = {
|
|
3609
4399
|
type: "string",
|
|
@@ -3645,11 +4435,31 @@ var commitHashOption = {
|
|
|
3645
4435
|
describe: chalk5.bold("Hash of the commit"),
|
|
3646
4436
|
type: "string"
|
|
3647
4437
|
};
|
|
4438
|
+
var scmTypeOption = {
|
|
4439
|
+
describe: chalk5.bold("SCM type (GitHub, GitLab, Ado)"),
|
|
4440
|
+
type: "string"
|
|
4441
|
+
};
|
|
4442
|
+
var scmOrgOption = {
|
|
4443
|
+
describe: chalk5.bold("Organization name in SCM (used in Azure DevOps)"),
|
|
4444
|
+
type: "string"
|
|
4445
|
+
};
|
|
4446
|
+
var scmUsernameOption = {
|
|
4447
|
+
describe: chalk5.bold("Username in SCM (used in GitHub)"),
|
|
4448
|
+
type: "string"
|
|
4449
|
+
};
|
|
4450
|
+
var scmRefreshTokenOption = {
|
|
4451
|
+
describe: chalk5.bold("SCM refresh token (used in GitLab)"),
|
|
4452
|
+
type: "string"
|
|
4453
|
+
};
|
|
4454
|
+
var scmTokenOption = {
|
|
4455
|
+
describe: chalk5.bold("SCM API token"),
|
|
4456
|
+
type: "string"
|
|
4457
|
+
};
|
|
3648
4458
|
|
|
3649
4459
|
// src/args/validation.ts
|
|
3650
4460
|
import chalk6 from "chalk";
|
|
3651
|
-
import
|
|
3652
|
-
import { z as
|
|
4461
|
+
import path8 from "path";
|
|
4462
|
+
import { z as z11 } from "zod";
|
|
3653
4463
|
function throwRepoUrlErrorMessage({
|
|
3654
4464
|
error,
|
|
3655
4465
|
repoUrl,
|
|
@@ -3666,10 +4476,10 @@ Example:
|
|
|
3666
4476
|
)}`;
|
|
3667
4477
|
throw new CliError(formattedErrorMessage);
|
|
3668
4478
|
}
|
|
3669
|
-
var UrlZ =
|
|
3670
|
-
invalid_type_error: "is not a valid GitHub / GitLab URL"
|
|
4479
|
+
var UrlZ = z11.string({
|
|
4480
|
+
invalid_type_error: "is not a valid GitHub / GitLab / ADO URL"
|
|
3671
4481
|
}).refine((data) => !!parseScmURL(data), {
|
|
3672
|
-
message: "is not a valid GitHub / GitLab URL"
|
|
4482
|
+
message: "is not a valid GitHub / GitLab / ADO URL"
|
|
3673
4483
|
});
|
|
3674
4484
|
function validateRepoUrl(args) {
|
|
3675
4485
|
const repoSafeParseResult = UrlZ.safeParse(args.repo);
|
|
@@ -3688,7 +4498,7 @@ function validateRepoUrl(args) {
|
|
|
3688
4498
|
}
|
|
3689
4499
|
var supportExtensions = [".json", ".xml", ".fpr", ".sarif"];
|
|
3690
4500
|
function validateReportFileFormat(reportFile) {
|
|
3691
|
-
if (!supportExtensions.includes(
|
|
4501
|
+
if (!supportExtensions.includes(path8.extname(reportFile))) {
|
|
3692
4502
|
throw new CliError(
|
|
3693
4503
|
`
|
|
3694
4504
|
${chalk6.bold(
|
|
@@ -3726,7 +4536,7 @@ function analyzeBuilder(yargs2) {
|
|
|
3726
4536
|
).help();
|
|
3727
4537
|
}
|
|
3728
4538
|
function validateAnalyzeOptions(argv) {
|
|
3729
|
-
if (!
|
|
4539
|
+
if (!fs5.existsSync(argv.f)) {
|
|
3730
4540
|
throw new CliError(`
|
|
3731
4541
|
Can't access ${chalk7.bold(argv.f)}`);
|
|
3732
4542
|
}
|
|
@@ -3747,7 +4557,7 @@ async function analyzeHandler(args) {
|
|
|
3747
4557
|
}
|
|
3748
4558
|
|
|
3749
4559
|
// src/args/commands/review.ts
|
|
3750
|
-
import
|
|
4560
|
+
import fs6 from "node:fs";
|
|
3751
4561
|
import chalk8 from "chalk";
|
|
3752
4562
|
function reviewBuilder(yargs2) {
|
|
3753
4563
|
return yargs2.option("f", {
|
|
@@ -3777,7 +4587,7 @@ function reviewBuilder(yargs2) {
|
|
|
3777
4587
|
).help();
|
|
3778
4588
|
}
|
|
3779
4589
|
function validateReviewOptions(argv) {
|
|
3780
|
-
if (!
|
|
4590
|
+
if (!fs6.existsSync(argv.f)) {
|
|
3781
4591
|
throw new CliError(`
|
|
3782
4592
|
Can't access ${chalk8.bold(argv.f)}`);
|
|
3783
4593
|
}
|
|
@@ -3813,6 +4623,37 @@ async function scanHandler(args) {
|
|
|
3813
4623
|
await scan(args, { skipPrompts: args.yes });
|
|
3814
4624
|
}
|
|
3815
4625
|
|
|
4626
|
+
// src/args/commands/token.ts
|
|
4627
|
+
function addScmTokenBuilder(args) {
|
|
4628
|
+
return args.option("scm", scmTypeOption).option("token", scmTokenOption).option("organization", scmOrgOption).option("username", scmUsernameOption).option("refresh-token", scmRefreshTokenOption).option("api-key", apiKeyOption).example(
|
|
4629
|
+
"$0 add-scm-token --scm ado --token abcdef0123456 --organization myOrg",
|
|
4630
|
+
"Add your SCM (Github, Gitlab, Azure DevOps) token to Mobb to enable automated fixes."
|
|
4631
|
+
).help().demandOption(["scm", "token"]);
|
|
4632
|
+
}
|
|
4633
|
+
function validateAddScmTokenOptions(argv) {
|
|
4634
|
+
if (!argv.scm) {
|
|
4635
|
+
throw new CliError(errorMessages.missingScmType);
|
|
4636
|
+
}
|
|
4637
|
+
if (!Object.values(ScmTypes).includes(argv.scm)) {
|
|
4638
|
+
throw new CliError(errorMessages.invalidScmType);
|
|
4639
|
+
}
|
|
4640
|
+
if (!argv.token) {
|
|
4641
|
+
throw new CliError(errorMessages.missingToken);
|
|
4642
|
+
}
|
|
4643
|
+
if (argv.scm === ScmTypes.AzureDevOps && !argv.organization) {
|
|
4644
|
+
throw new CliError(
|
|
4645
|
+
"\nError: --organization flag is required for Azure DevOps"
|
|
4646
|
+
);
|
|
4647
|
+
}
|
|
4648
|
+
if (argv.scm === ScmTypes.Github && !argv.username) {
|
|
4649
|
+
throw new CliError("\nError: --username flag is required for GitHub");
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
async function addScmTokenHandler(args) {
|
|
4653
|
+
validateAddScmTokenOptions(args);
|
|
4654
|
+
await addScmToken(args);
|
|
4655
|
+
}
|
|
4656
|
+
|
|
3816
4657
|
// src/args/yargs.ts
|
|
3817
4658
|
var parseArgs = async (args) => {
|
|
3818
4659
|
const yargsInstance = yargs(args);
|
|
@@ -3830,6 +4671,13 @@ var parseArgs = async (args) => {
|
|
|
3830
4671
|
)} ${chalk9.dim("[options]")}
|
|
3831
4672
|
`
|
|
3832
4673
|
).version(false).command(
|
|
4674
|
+
mobbCliCommand.addScmToken,
|
|
4675
|
+
chalk9.bold(
|
|
4676
|
+
"Add your SCM (Github, Gitlab, Azure DevOps) token to Mobb to enable automated fixes."
|
|
4677
|
+
),
|
|
4678
|
+
addScmTokenBuilder,
|
|
4679
|
+
addScmTokenHandler
|
|
4680
|
+
).command(
|
|
3833
4681
|
mobbCliCommand.scan,
|
|
3834
4682
|
chalk9.bold(
|
|
3835
4683
|
"Scan your code for vulnerabilities, get automated fixes right away."
|