@usepipr/runtime 0.4.2 → 0.4.3
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/dist/{commands-EZV-Icbs.mjs → commands-B9qNW4pk.mjs} +1296 -1283
- package/dist/commands-B9qNW4pk.mjs.map +1 -0
- package/dist/commands-DA0Ij4Di.d.mts +27 -0
- package/dist/commands-DA0Ij4Di.d.mts.map +1 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/internal/action-result.d.mts +11 -0
- package/dist/internal/action-result.d.mts.map +1 -0
- package/dist/internal/action-result.mjs +46 -0
- package/dist/internal/action-result.mjs.map +1 -0
- package/dist/internal/docs.mjs +1 -1
- package/dist/internal/testing.d.mts +2 -1
- package/dist/internal/testing.d.mts.map +1 -1
- package/dist/internal/testing.mjs +1 -1
- package/dist/{official-github-workflow-BuZs6bOx.mjs → official-github-workflow-Bjlu-FL3.mjs} +2 -2
- package/dist/{official-github-workflow-BuZs6bOx.mjs.map → official-github-workflow-Bjlu-FL3.mjs.map} +1 -1
- package/dist/{commands-DtdTtej_.d.mts → types-BAis_Lwm.d.mts} +5 -29
- package/dist/types-BAis_Lwm.d.mts.map +1 -0
- package/package.json +7 -3
- package/dist/commands-DtdTtej_.d.mts.map +0 -1
- package/dist/commands-EZV-Icbs.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as resolveReadAtRefRequest, d as isRecord, l as unavailableReadAtRefResult, n as boundedLineSlice, r as parseManifestPath, u as createDiffRangeIndex } from "./runtime-tools-core-D8_WsbL_.mjs";
|
|
2
|
-
import { a as officialInitRecipeWorkflowEnvSecrets, i as officialInitRecipeFiles, r as officialInitRecipeConfigTs, t as renderOfficialGithubWorkflow } from "./official-github-workflow-
|
|
2
|
+
import { a as officialInitRecipeWorkflowEnvSecrets, i as officialInitRecipeFiles, r as officialInitRecipeConfigTs, t as renderOfficialGithubWorkflow } from "./official-github-workflow-Bjlu-FL3.mjs";
|
|
3
3
|
import { t as isPublishableSuggestedFixSelection } from "./suggested-fix-publication-policy-B5Wwuudp.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { chmod, chown, cp, lstat, mkdir, mkdtemp, readdir, rm } from "node:fs/promises";
|
|
@@ -597,7 +597,7 @@ function compareStableSemver(left, right) {
|
|
|
597
597
|
}
|
|
598
598
|
//#endregion
|
|
599
599
|
//#region src/shared/version.ts
|
|
600
|
-
const runtimeVersion = "0.4.
|
|
600
|
+
const runtimeVersion = "0.4.3";
|
|
601
601
|
//#endregion
|
|
602
602
|
//#region src/config/version-compat.ts
|
|
603
603
|
async function resolveConfigVersionCompatibility(options) {
|
|
@@ -928,10 +928,6 @@ function assertRequiredProviderEnv(providers, options) {
|
|
|
928
928
|
if (missing.length > 0) throw new Error(`Missing provider env vars: ${missing.map((provider) => provider.apiKeyEnv).join(", ")}`);
|
|
929
929
|
}
|
|
930
930
|
//#endregion
|
|
931
|
-
//#region src/config/scaffold-versions.ts
|
|
932
|
-
const defaultTypesBunVersion = "1.3.14";
|
|
933
|
-
const defaultTypescriptVersion = "6.0.3";
|
|
934
|
-
//#endregion
|
|
935
931
|
//#region src/config/init.ts
|
|
936
932
|
const supportedOfficialInitAdapters = [
|
|
937
933
|
"github",
|
|
@@ -939,8 +935,8 @@ const supportedOfficialInitAdapters = [
|
|
|
939
935
|
"azure-devops",
|
|
940
936
|
"bitbucket"
|
|
941
937
|
];
|
|
942
|
-
const defaultGitLabImageRef = "ghcr.io/somus/pipr:v0.4.
|
|
943
|
-
const defaultSdkVersion = "0.4.
|
|
938
|
+
const defaultGitLabImageRef = "ghcr.io/somus/pipr:v0.4.3";
|
|
939
|
+
const defaultSdkVersion = "0.4.3";
|
|
944
940
|
function resolveOfficialInitAdapters(adapters) {
|
|
945
941
|
if (adapters === void 0) return ["github"];
|
|
946
942
|
if (adapters.length === 0) return [];
|
|
@@ -974,11 +970,7 @@ async function initOfficialMinimalProject(options) {
|
|
|
974
970
|
const result = await writeTargets(targets, existing, { skipExisting: false });
|
|
975
971
|
if (!minimal) {
|
|
976
972
|
await assertBunAvailable();
|
|
977
|
-
const install = Bun.spawn(
|
|
978
|
-
"bun",
|
|
979
|
-
"install",
|
|
980
|
-
"--ignore-scripts"
|
|
981
|
-
], {
|
|
973
|
+
const install = Bun.spawn(initInstallCommand(), {
|
|
982
974
|
cwd: projectDir,
|
|
983
975
|
env: process.env,
|
|
984
976
|
stdout: "pipe",
|
|
@@ -1000,6 +992,15 @@ async function initOfficialMinimalProject(options) {
|
|
|
1000
992
|
...result
|
|
1001
993
|
};
|
|
1002
994
|
}
|
|
995
|
+
function initInstallCommand(env = process.env) {
|
|
996
|
+
const command = [
|
|
997
|
+
"bun",
|
|
998
|
+
"install",
|
|
999
|
+
"--ignore-scripts"
|
|
1000
|
+
];
|
|
1001
|
+
if (env.PIPR_INTERNAL_INIT_OFFLINE === "1") command.push("--offline");
|
|
1002
|
+
return command;
|
|
1003
|
+
}
|
|
1003
1004
|
async function starterFiles(relativeConfigDir, adapters, recipe, minimal = false) {
|
|
1004
1005
|
const files = [{
|
|
1005
1006
|
relativePath: path.join(relativeConfigDir, "config.ts"),
|
|
@@ -1086,15 +1087,18 @@ function starterBitbucketWebhookEnvironment(recipe) {
|
|
|
1086
1087
|
lines.push("");
|
|
1087
1088
|
return lines.join("\n");
|
|
1088
1089
|
}
|
|
1089
|
-
function
|
|
1090
|
-
return
|
|
1090
|
+
function officialInitPackageManifest(env = process.env) {
|
|
1091
|
+
return {
|
|
1091
1092
|
private: true,
|
|
1092
|
-
dependencies: { "@usepipr/sdk":
|
|
1093
|
+
dependencies: { "@usepipr/sdk": env.PIPR_INTERNAL_INIT_SDK_VERSION ?? defaultSdkVersion },
|
|
1093
1094
|
devDependencies: {
|
|
1094
|
-
"@types/bun":
|
|
1095
|
-
typescript:
|
|
1095
|
+
"@types/bun": env.PIPR_INTERNAL_INIT_TYPES_BUN_VERSION ?? "1.3.14",
|
|
1096
|
+
typescript: env.PIPR_INTERNAL_INIT_TYPESCRIPT_VERSION ?? "6.0.3"
|
|
1096
1097
|
}
|
|
1097
|
-
}
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
function starterPackageJson() {
|
|
1101
|
+
return `${JSON.stringify(officialInitPackageManifest(), null, 2)}\n`;
|
|
1098
1102
|
}
|
|
1099
1103
|
async function writeTargets(targets, existing, options) {
|
|
1100
1104
|
const created = [];
|
|
@@ -1150,996 +1154,172 @@ async function maybeLstat(filePath) {
|
|
|
1150
1154
|
}
|
|
1151
1155
|
}
|
|
1152
1156
|
//#endregion
|
|
1153
|
-
//#region src/
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1157
|
+
//#region src/diff/git.ts
|
|
1158
|
+
function runGit$1(args, cwd, maxBuffer) {
|
|
1159
|
+
const result = Bun.spawnSync(["git", ...args], {
|
|
1160
|
+
cwd,
|
|
1161
|
+
env: process.env,
|
|
1162
|
+
maxBuffer,
|
|
1163
|
+
stderr: "pipe",
|
|
1164
|
+
stdout: "pipe"
|
|
1165
|
+
});
|
|
1166
|
+
if (result.exitCode !== 0) {
|
|
1167
|
+
const failure = result.stderr?.toString().trim() || "unknown error";
|
|
1168
|
+
throw new Error(`git ${args.join(" ")} failed: ${failure}`);
|
|
1160
1169
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
const body = (await response.text()).slice(0, 1024);
|
|
1198
|
-
throw new CodeHostHttpError(redact$1(`Code host request failed (${response.status} ${response.statusText}): ${body}`, secrets), response.status);
|
|
1170
|
+
return result.stdout.toString();
|
|
1171
|
+
}
|
|
1172
|
+
//#endregion
|
|
1173
|
+
//#region src/diff/diff.ts
|
|
1174
|
+
const lockFilePattern = /(^|\/)(bun\.lock|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|Cargo\.lock)$/;
|
|
1175
|
+
const generatedPattern = /(^|\/)(dist|build|coverage|vendor)\//;
|
|
1176
|
+
const maxInlineChangedLines = 1e3;
|
|
1177
|
+
const maxCommentableRangeLines = 5e3;
|
|
1178
|
+
function buildDiffManifest(options) {
|
|
1179
|
+
const mergeBaseSha = runGit$1([
|
|
1180
|
+
"merge-base",
|
|
1181
|
+
options.baseSha,
|
|
1182
|
+
options.headSha
|
|
1183
|
+
], options.cwd).trim();
|
|
1184
|
+
const diffHead = options.includeWorkingTree ? void 0 : options.headSha;
|
|
1185
|
+
const files = parseNameStatus(runGit$1(buildDiffArgs([
|
|
1186
|
+
"--name-status",
|
|
1187
|
+
"-z",
|
|
1188
|
+
"--find-renames"
|
|
1189
|
+
], mergeBaseSha, diffHead), options.cwd));
|
|
1190
|
+
const diffStats = getDiffStats(options.cwd, mergeBaseSha, diffHead);
|
|
1191
|
+
const rawPatch = parseRawPatch(runGit$1(buildUnifiedDiffArgs(mergeBaseSha, diffHead, getPreExcludedFiles(files, diffStats)), options.cwd));
|
|
1192
|
+
const parsedDiff = parseUnifiedDiff(rawPatch.patch, rawPatch.filePaths);
|
|
1193
|
+
for (const file of files) {
|
|
1194
|
+
const stats = diffStats.get(file.path);
|
|
1195
|
+
file.additions = stats?.additions ?? 0;
|
|
1196
|
+
file.deletions = stats?.deletions ?? 0;
|
|
1197
|
+
const preExcludedReason = stats?.excludedReason;
|
|
1198
|
+
const parsedFile = parsedDiff.get(file.path);
|
|
1199
|
+
file.hunks = preExcludedReason ? [] : parsedFile?.hunks ?? [];
|
|
1200
|
+
file.commentableRanges = preExcludedReason ? [] : parsedFile?.commentableRanges ?? [];
|
|
1201
|
+
const excludedReason = preExcludedReason ?? getExcludedReason(file);
|
|
1202
|
+
if (excludedReason) {
|
|
1203
|
+
file.hunks = [];
|
|
1204
|
+
file.commentableRanges = [];
|
|
1205
|
+
file.excludedReason = excludedReason;
|
|
1199
1206
|
}
|
|
1200
|
-
}
|
|
1207
|
+
}
|
|
1208
|
+
return parseDiffManifest({
|
|
1209
|
+
baseSha: options.baseSha,
|
|
1210
|
+
headSha: options.headSha,
|
|
1211
|
+
mergeBaseSha,
|
|
1212
|
+
files
|
|
1213
|
+
});
|
|
1201
1214
|
}
|
|
1202
|
-
function
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
notBefore = 0;
|
|
1216
|
-
return;
|
|
1217
|
-
}
|
|
1215
|
+
function parseNameStatus(output) {
|
|
1216
|
+
const fields = output.split("\0");
|
|
1217
|
+
const files = [];
|
|
1218
|
+
let index = 0;
|
|
1219
|
+
while (index < fields.length) {
|
|
1220
|
+
const rawStatus = fields[index++];
|
|
1221
|
+
if (!rawStatus) continue;
|
|
1222
|
+
const firstPath = fields[index++] ?? "";
|
|
1223
|
+
const status = parseFileStatus(rawStatus);
|
|
1224
|
+
if (status === "renamed") {
|
|
1225
|
+
const secondPath = fields[index++] ?? "";
|
|
1226
|
+
files.push(baseFile(secondPath || firstPath, status, firstPath));
|
|
1227
|
+
continue;
|
|
1218
1228
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1229
|
+
files.push(baseFile(firstPath, status));
|
|
1230
|
+
}
|
|
1231
|
+
return files;
|
|
1232
|
+
}
|
|
1233
|
+
function parseUnifiedDiff(diff, filePaths) {
|
|
1234
|
+
const state = createDiffParserState(filePaths);
|
|
1235
|
+
for (const line of diff.split("\n")) parseUnifiedDiffLine(state, line);
|
|
1236
|
+
finishActiveHunk(state);
|
|
1237
|
+
return state.filesByPath;
|
|
1238
|
+
}
|
|
1239
|
+
function getDiffStats(cwd, baseSha, headSha) {
|
|
1240
|
+
const output = runGit$1(buildDiffArgs([
|
|
1241
|
+
"--numstat",
|
|
1242
|
+
"-z",
|
|
1243
|
+
"--find-renames"
|
|
1244
|
+
], baseSha, headSha), cwd);
|
|
1245
|
+
const stats = /* @__PURE__ */ new Map();
|
|
1246
|
+
for (const stat of parseNumstat(output)) {
|
|
1247
|
+
if (stat.binary) {
|
|
1248
|
+
stats.set(stat.path, {
|
|
1249
|
+
additions: 0,
|
|
1250
|
+
deletions: 0,
|
|
1251
|
+
excludedReason: "binary diff"
|
|
1225
1252
|
});
|
|
1226
|
-
|
|
1227
|
-
return waitInFlight;
|
|
1228
|
-
},
|
|
1229
|
-
update(retryAfter) {
|
|
1230
|
-
const delay = retryAfterMilliseconds(retryAfter);
|
|
1231
|
-
if (delay > 0) notBefore = Math.max(notBefore, Date.now() + delay);
|
|
1253
|
+
continue;
|
|
1232
1254
|
}
|
|
1233
|
-
|
|
1255
|
+
stats.set(stat.path, {
|
|
1256
|
+
additions: stat.additions,
|
|
1257
|
+
deletions: stat.deletions,
|
|
1258
|
+
excludedReason: stat.additions + stat.deletions > maxInlineChangedLines ? "oversized diff" : void 0
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
return stats;
|
|
1234
1262
|
}
|
|
1235
|
-
function
|
|
1236
|
-
|
|
1263
|
+
function getPreExcludedFiles(files, stats) {
|
|
1264
|
+
const excluded = /* @__PURE__ */ new Map();
|
|
1265
|
+
for (const [filePath, stat] of stats) if (stat.excludedReason) excluded.set(filePath, stat.excludedReason);
|
|
1266
|
+
for (const file of files) {
|
|
1267
|
+
const excludedReason = excluded.get(file.path) ?? getPathExcludedReason(file);
|
|
1268
|
+
if (!excludedReason) continue;
|
|
1269
|
+
excluded.set(file.path, excludedReason);
|
|
1270
|
+
if (file.previousPath) excluded.set(file.previousPath, excludedReason);
|
|
1271
|
+
}
|
|
1272
|
+
return excluded;
|
|
1237
1273
|
}
|
|
1238
|
-
function
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1274
|
+
function buildUnifiedDiffArgs(baseSha, headSha, excludedFiles) {
|
|
1275
|
+
const args = buildDiffArgs([
|
|
1276
|
+
"--raw",
|
|
1277
|
+
"-z",
|
|
1278
|
+
"--patch",
|
|
1279
|
+
"--submodule=short",
|
|
1280
|
+
"--unified=80",
|
|
1281
|
+
"--find-renames"
|
|
1282
|
+
], baseSha, headSha);
|
|
1283
|
+
if (excludedFiles.size === 0) return args;
|
|
1284
|
+
return [
|
|
1285
|
+
...args,
|
|
1286
|
+
"--",
|
|
1287
|
+
".",
|
|
1288
|
+
...[...excludedFiles.keys()].map((filePath) => `:(exclude,literal)${filePath}`)
|
|
1289
|
+
];
|
|
1244
1290
|
}
|
|
1245
|
-
function
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1291
|
+
function buildDiffArgs(options, baseSha, headSha) {
|
|
1292
|
+
return headSha ? [
|
|
1293
|
+
"diff",
|
|
1294
|
+
...options,
|
|
1295
|
+
baseSha,
|
|
1296
|
+
headSha
|
|
1297
|
+
] : [
|
|
1298
|
+
"diff",
|
|
1299
|
+
...options,
|
|
1300
|
+
baseSha
|
|
1301
|
+
];
|
|
1249
1302
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
...uniqueName ? { uniqueName } : {}
|
|
1271
|
-
};
|
|
1272
|
-
});
|
|
1273
|
-
const commitSchema = z.looseObject({ commitId: z.string().min(1) });
|
|
1274
|
-
const forkRepositorySchema = z.looseObject({ remoteUrl: z.string().min(1) });
|
|
1275
|
-
const pullRequestSchema$1 = z.looseObject({
|
|
1276
|
-
pullRequestId: z.number().int().positive(),
|
|
1277
|
-
isDraft: z.boolean().optional(),
|
|
1278
|
-
title: z.string(),
|
|
1279
|
-
description: z.string().nullish(),
|
|
1280
|
-
url: z.string().optional(),
|
|
1281
|
-
sourceRefName: z.string().min(1),
|
|
1282
|
-
targetRefName: z.string().min(1),
|
|
1283
|
-
createdBy: identitySchema.optional(),
|
|
1284
|
-
lastMergeSourceCommit: commitSchema,
|
|
1285
|
-
lastMergeTargetCommit: commitSchema,
|
|
1286
|
-
forkSource: z.looseObject({ repository: forkRepositorySchema }).optional(),
|
|
1287
|
-
repository: z.looseObject({
|
|
1288
|
-
id: z.string().min(1),
|
|
1289
|
-
name: z.string().min(1),
|
|
1290
|
-
url: z.string().optional(),
|
|
1291
|
-
project: z.looseObject({
|
|
1292
|
-
id: z.string().min(1),
|
|
1293
|
-
name: z.string().min(1)
|
|
1294
|
-
})
|
|
1295
|
-
})
|
|
1296
|
-
});
|
|
1297
|
-
const repositorySchema$1 = z.looseObject({
|
|
1298
|
-
id: z.string().min(1),
|
|
1299
|
-
name: z.string().min(1),
|
|
1300
|
-
project: z.looseObject({
|
|
1301
|
-
id: z.string().min(1),
|
|
1302
|
-
name: z.string().min(1)
|
|
1303
|
-
})
|
|
1304
|
-
});
|
|
1305
|
-
const iterationSchema = z.looseObject({
|
|
1306
|
-
id: z.number().int().positive(),
|
|
1307
|
-
sourceRefCommit: commitSchema
|
|
1308
|
-
});
|
|
1309
|
-
const iterationChangeSchema = z.looseObject({
|
|
1310
|
-
changeTrackingId: z.number().int(),
|
|
1311
|
-
changeType: z.string(),
|
|
1312
|
-
item: z.looseObject({
|
|
1313
|
-
path: z.string().min(1),
|
|
1314
|
-
originalPath: z.string().min(1).optional()
|
|
1315
|
-
})
|
|
1316
|
-
});
|
|
1317
|
-
const threadCommentSchema = z.looseObject({
|
|
1318
|
-
id: z.union([z.number(), z.string()]).transform(String),
|
|
1319
|
-
parentCommentId: z.number().int().optional(),
|
|
1320
|
-
content: z.string().default(""),
|
|
1321
|
-
author: identitySchema.optional(),
|
|
1322
|
-
isDeleted: z.boolean().optional()
|
|
1323
|
-
});
|
|
1324
|
-
const pointSchema = z.looseObject({
|
|
1325
|
-
line: z.number().int(),
|
|
1326
|
-
offset: z.number().int()
|
|
1327
|
-
});
|
|
1328
|
-
const threadSchema = z.looseObject({
|
|
1329
|
-
id: z.union([z.number(), z.string()]).transform(String),
|
|
1330
|
-
status: z.string().optional(),
|
|
1331
|
-
comments: z.array(threadCommentSchema).default([]),
|
|
1332
|
-
threadContext: z.looseObject({
|
|
1333
|
-
filePath: z.string().optional(),
|
|
1334
|
-
leftFileStart: pointSchema.nullish(),
|
|
1335
|
-
leftFileEnd: pointSchema.nullish(),
|
|
1336
|
-
rightFileStart: pointSchema.nullish(),
|
|
1337
|
-
rightFileEnd: pointSchema.nullish()
|
|
1338
|
-
}).nullish(),
|
|
1339
|
-
pullRequestThreadContext: z.looseObject({
|
|
1340
|
-
changeTrackingId: z.number().int().optional(),
|
|
1341
|
-
iterationContext: z.looseObject({
|
|
1342
|
-
firstComparingIteration: z.number().int(),
|
|
1343
|
-
secondComparingIteration: z.number().int()
|
|
1344
|
-
}).optional()
|
|
1345
|
-
}).nullish()
|
|
1346
|
-
});
|
|
1347
|
-
const statusSchema$1 = z.looseObject({ id: z.union([z.number(), z.string()]).transform(String) });
|
|
1348
|
-
const aclSchema = collectionSchema(z.looseObject({
|
|
1349
|
-
inheritPermissions: z.boolean().default(true),
|
|
1350
|
-
acesDictionary: z.record(z.string(), z.looseObject({
|
|
1351
|
-
allow: z.number().int().default(0),
|
|
1352
|
-
deny: z.number().int().default(0),
|
|
1353
|
-
extendedInfo: z.looseObject({
|
|
1354
|
-
effectiveAllow: z.number().int().default(0),
|
|
1355
|
-
effectiveDeny: z.number().int().default(0)
|
|
1356
|
-
}).optional()
|
|
1357
|
-
}))
|
|
1358
|
-
}));
|
|
1359
|
-
function createAzureDevOpsClient(env = process.env, fetch = globalThis.fetch, options = {}) {
|
|
1360
|
-
const organization = env.AZURE_DEVOPS_ORGANIZATION ?? organizationFromCollectionUri$1(env.SYSTEM_COLLECTIONURI);
|
|
1361
|
-
const project = env.AZURE_DEVOPS_PROJECT ?? env.SYSTEM_TEAMPROJECT;
|
|
1362
|
-
const pat = env.AZURE_DEVOPS_TOKEN;
|
|
1363
|
-
const bearerToken = env.AZURE_DEVOPS_BEARER_TOKEN ?? env.SYSTEM_ACCESSTOKEN;
|
|
1364
|
-
if (!organization) throw new Error("AZURE_DEVOPS_ORGANIZATION is required for Azure DevOps API calls");
|
|
1365
|
-
if (!project) throw new Error("AZURE_DEVOPS_PROJECT or SYSTEM_TEAMPROJECT is required for Azure DevOps API calls");
|
|
1366
|
-
if (!pat && !bearerToken) throw new Error("AZURE_DEVOPS_TOKEN, AZURE_DEVOPS_BEARER_TOKEN, or SYSTEM_ACCESSTOKEN is required for Azure DevOps API calls");
|
|
1367
|
-
const headers = pat ? { Authorization: `Basic ${Buffer.from(`:${pat}`).toString("base64")}` } : { Authorization: `Bearer ${bearerToken}` };
|
|
1368
|
-
const successThrottle = createCodeHostSuccessThrottle(options.sleep);
|
|
1369
|
-
const api = createCodeHostHttpClient({
|
|
1370
|
-
baseUrl: `https://dev.azure.com/${encodeURIComponent(organization)}/${encodeURIComponent(project)}/_apis/`,
|
|
1371
|
-
headers,
|
|
1372
|
-
fetch,
|
|
1373
|
-
successThrottle
|
|
1374
|
-
});
|
|
1375
|
-
const organizationApi = createCodeHostHttpClient({
|
|
1376
|
-
baseUrl: `https://dev.azure.com/${encodeURIComponent(organization)}/_apis/`,
|
|
1377
|
-
headers,
|
|
1378
|
-
fetch,
|
|
1379
|
-
successThrottle
|
|
1380
|
-
});
|
|
1381
|
-
const identityApi = createCodeHostHttpClient({
|
|
1382
|
-
baseUrl: `https://vssps.dev.azure.com/${encodeURIComponent(organization)}/_apis/`,
|
|
1383
|
-
headers,
|
|
1384
|
-
fetch,
|
|
1385
|
-
successThrottle
|
|
1386
|
-
});
|
|
1387
|
-
const pullRequestPath = (repositoryId, changeNumber) => `git/repositories/${encodeURIComponent(repositoryId)}/pullRequests/${changeNumber}`;
|
|
1388
|
-
return {
|
|
1389
|
-
organization,
|
|
1390
|
-
project,
|
|
1391
|
-
async currentUser() {
|
|
1392
|
-
return (await organizationApi.json("connectionData?connectOptions=1&lastChangeId=-1&lastChangeId64=-1", z.looseObject({ authenticatedUser: identitySchema }))).authenticatedUser;
|
|
1393
|
-
},
|
|
1394
|
-
async getRepository(repository) {
|
|
1395
|
-
const value = await api.json(withApiVersion(`git/repositories/${encodeURIComponent(repository)}`), repositorySchema$1);
|
|
1396
|
-
return {
|
|
1397
|
-
id: value.id,
|
|
1398
|
-
name: value.name,
|
|
1399
|
-
projectId: value.project.id,
|
|
1400
|
-
project: value.project.name
|
|
1401
|
-
};
|
|
1402
|
-
},
|
|
1403
|
-
async getRepositoryPermission(actor, projectId, repositoryId) {
|
|
1404
|
-
const candidates = (await identityApi.json(`identities?searchFilter=General&filterValue=${encodeURIComponent(actor)}&queryMembership=ExpandedUp&api-version=7.1`, collectionSchema(z.looseObject({
|
|
1405
|
-
descriptor: z.string().min(1),
|
|
1406
|
-
isActive: z.boolean().optional(),
|
|
1407
|
-
isContainer: z.boolean().optional(),
|
|
1408
|
-
memberOf: z.array(z.union([z.string().min(1), z.looseObject({ descriptor: z.string().min(1) })])).default([])
|
|
1409
|
-
})))).value.filter((identity) => identity.isActive !== false && identity.isContainer !== true);
|
|
1410
|
-
if (candidates.length !== 1) return "none";
|
|
1411
|
-
const identity = candidates[0];
|
|
1412
|
-
if (!identity) return "none";
|
|
1413
|
-
const descriptors = [identity.descriptor, ...identity.memberOf.map((group) => typeof group === "string" ? group : group.descriptor)];
|
|
1414
|
-
const loadAcls = (token) => {
|
|
1415
|
-
const query = new URLSearchParams({
|
|
1416
|
-
token,
|
|
1417
|
-
descriptors: descriptors.join(","),
|
|
1418
|
-
includeExtendedInfo: "true",
|
|
1419
|
-
recurse: "false",
|
|
1420
|
-
"api-version": "7.1"
|
|
1421
|
-
});
|
|
1422
|
-
return organizationApi.json(`accesscontrollists/2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87?${query}`, aclSchema);
|
|
1423
|
-
};
|
|
1424
|
-
const [projectAcls, repositoryAcls] = await Promise.all([loadAcls(`repoV2/${projectId}`), loadAcls(`repoV2/${projectId}/${repositoryId}`)]);
|
|
1425
|
-
return azurePermissionFromAcls(repositoryAcls.value.every((acl) => acl.inheritPermissions) ? [...projectAcls.value, ...repositoryAcls.value] : repositoryAcls.value);
|
|
1426
|
-
},
|
|
1427
|
-
getPullRequest: (repositoryId, changeNumber) => api.json(withApiVersion(pullRequestPath(repositoryId, changeNumber)), pullRequestSchema$1),
|
|
1428
|
-
async listIterations(repositoryId, changeNumber) {
|
|
1429
|
-
return (await api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/iterations`), collectionSchema(iterationSchema))).value.map((iteration) => ({
|
|
1430
|
-
id: iteration.id,
|
|
1431
|
-
headSha: iteration.sourceRefCommit.commitId
|
|
1432
|
-
}));
|
|
1433
|
-
},
|
|
1434
|
-
async loadChange(options) {
|
|
1435
|
-
if (options.organization !== organization || options.project !== project) throw new Error("Azure DevOps client coordinates do not match its configured organization and project");
|
|
1436
|
-
const pullRequest = await this.getPullRequest(options.repositoryId, options.changeNumber);
|
|
1437
|
-
const iterations = await this.listIterations(options.repositoryId, options.changeNumber);
|
|
1438
|
-
const headSha = pullRequest.lastMergeSourceCommit.commitId;
|
|
1439
|
-
const iteration = iterations.findLast((candidate) => candidate.headSha === headSha);
|
|
1440
|
-
if (!iteration) throw new Error(`Azure DevOps has no pull request iteration for head ${headSha}`);
|
|
1441
|
-
const sourceRef = branchName(pullRequest.sourceRefName);
|
|
1442
|
-
const targetRef = branchName(pullRequest.targetRefName);
|
|
1443
|
-
return {
|
|
1444
|
-
repository: {
|
|
1445
|
-
slug: `${organization}/${pullRequest.repository.project.name}/${pullRequest.repository.name}`,
|
|
1446
|
-
url: repositoryWebUrl(organization, pullRequest.repository.project.name, pullRequest.repository.name)
|
|
1447
|
-
},
|
|
1448
|
-
coordinates: {
|
|
1449
|
-
provider: "azure-devops",
|
|
1450
|
-
organization,
|
|
1451
|
-
project: pullRequest.repository.project.name,
|
|
1452
|
-
projectId: pullRequest.repository.project.id,
|
|
1453
|
-
repositoryId: pullRequest.repository.id
|
|
1454
|
-
},
|
|
1455
|
-
change: {
|
|
1456
|
-
number: pullRequest.pullRequestId,
|
|
1457
|
-
isDraft: pullRequest.isDraft,
|
|
1458
|
-
title: pullRequest.title,
|
|
1459
|
-
description: pullRequest.description ?? "",
|
|
1460
|
-
url: `${repositoryWebUrl(organization, pullRequest.repository.project.name, pullRequest.repository.name)}/pullrequest/${pullRequest.pullRequestId}`,
|
|
1461
|
-
author: pullRequest.createdBy?.uniqueName ? { login: pullRequest.createdBy.uniqueName } : void 0,
|
|
1462
|
-
base: {
|
|
1463
|
-
sha: pullRequest.lastMergeTargetCommit.commitId,
|
|
1464
|
-
ref: targetRef
|
|
1465
|
-
},
|
|
1466
|
-
head: {
|
|
1467
|
-
sha: headSha,
|
|
1468
|
-
ref: sourceRef,
|
|
1469
|
-
...pullRequest.forkSource?.repository.remoteUrl ? { url: pullRequest.forkSource.repository.remoteUrl } : {}
|
|
1470
|
-
},
|
|
1471
|
-
...pullRequest.forkSource ? { isFork: true } : {}
|
|
1472
|
-
},
|
|
1473
|
-
iterationId: iteration.id
|
|
1474
|
-
};
|
|
1475
|
-
},
|
|
1476
|
-
async listIterationChanges(repositoryId, changeNumber, iterationId) {
|
|
1477
|
-
const changes = [];
|
|
1478
|
-
let skip = 0;
|
|
1479
|
-
let top = 2e3;
|
|
1480
|
-
for (;;) {
|
|
1481
|
-
const path = `${pullRequestPath(repositoryId, changeNumber)}/iterations/${iterationId}/changes?compareTo=0&$skip=${skip}&$top=${top}`;
|
|
1482
|
-
const page = await api.json(withApiVersion(path), z.looseObject({
|
|
1483
|
-
changeEntries: z.array(iterationChangeSchema),
|
|
1484
|
-
nextSkip: z.number().int().nonnegative().optional(),
|
|
1485
|
-
nextTop: z.number().int().nonnegative().max(2e3).optional()
|
|
1486
|
-
}));
|
|
1487
|
-
changes.push(...page.changeEntries.map((change) => ({
|
|
1488
|
-
changeTrackingId: change.changeTrackingId,
|
|
1489
|
-
changeType: change.changeType,
|
|
1490
|
-
path: trimLeadingSlash(change.item.path),
|
|
1491
|
-
...change.item.originalPath ? { originalPath: trimLeadingSlash(change.item.originalPath) } : {}
|
|
1492
|
-
})));
|
|
1493
|
-
if (!page.nextSkip) return changes;
|
|
1494
|
-
skip = page.nextSkip;
|
|
1495
|
-
if (page.nextTop) top = page.nextTop;
|
|
1496
|
-
}
|
|
1497
|
-
},
|
|
1498
|
-
async listThreads(repositoryId, changeNumber) {
|
|
1499
|
-
return (await api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads`), collectionSchema(threadSchema))).value;
|
|
1500
|
-
},
|
|
1501
|
-
createThread: (repositoryId, changeNumber, body) => api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads`), threadSchema, jsonRequest$2("POST", body)),
|
|
1502
|
-
updateComment: (repositoryId, changeNumber, threadId, commentId, content) => api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(commentId)}`), threadCommentSchema, jsonRequest$2("PATCH", { content })),
|
|
1503
|
-
createThreadComment: (repositoryId, changeNumber, threadId, body) => api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads/${encodeURIComponent(threadId)}/comments`), threadCommentSchema, jsonRequest$2("POST", body)),
|
|
1504
|
-
updateThreadStatus: (repositoryId, changeNumber, threadId, status) => api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads/${encodeURIComponent(threadId)}`), threadSchema, jsonRequest$2("PATCH", { status })),
|
|
1505
|
-
async createStatus(repositoryId, changeNumber, body) {
|
|
1506
|
-
return (await api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/statuses`), statusSchema$1, jsonRequest$2("POST", body))).id;
|
|
1507
|
-
}
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
|
-
function azureDevOpsStatusState(state) {
|
|
1511
|
-
switch (state) {
|
|
1512
|
-
case "pending": return "pending";
|
|
1513
|
-
case "success": return "succeeded";
|
|
1514
|
-
case "failure": return "failed";
|
|
1515
|
-
case "neutral": return "notApplicable";
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
function azureRepositoryPermission(bits) {
|
|
1519
|
-
if ((bits & 2) === 0) return "none";
|
|
1520
|
-
if ((bits & 8192) !== 0) return "admin";
|
|
1521
|
-
if ((bits & 2048) !== 0) return "maintain";
|
|
1522
|
-
if ((bits & 4) !== 0) return "write";
|
|
1523
|
-
if ((bits & 16384) !== 0) return "triage";
|
|
1524
|
-
return "read";
|
|
1525
|
-
}
|
|
1526
|
-
function azurePermissionFromAcls(acls) {
|
|
1527
|
-
let allow = 0;
|
|
1528
|
-
let deny = 0;
|
|
1529
|
-
for (const acl of acls) for (const ace of Object.values(acl.acesDictionary)) {
|
|
1530
|
-
allow |= ace.extendedInfo?.effectiveAllow ?? ace.allow;
|
|
1531
|
-
deny |= ace.extendedInfo?.effectiveDeny ?? ace.deny;
|
|
1532
|
-
}
|
|
1533
|
-
return azureRepositoryPermission(allow & ~deny);
|
|
1534
|
-
}
|
|
1535
|
-
function collectionSchema(item) {
|
|
1536
|
-
return z.looseObject({
|
|
1537
|
-
count: z.number().int().nonnegative(),
|
|
1538
|
-
value: z.array(item)
|
|
1539
|
-
});
|
|
1540
|
-
}
|
|
1541
|
-
function withApiVersion(path) {
|
|
1542
|
-
return `${path}${path.includes("?") ? "&" : "?"}api-version=7.1`;
|
|
1543
|
-
}
|
|
1544
|
-
function jsonRequest$2(method, body) {
|
|
1545
|
-
return {
|
|
1546
|
-
method,
|
|
1547
|
-
headers: { "Content-Type": "application/json" },
|
|
1548
|
-
body: JSON.stringify(body)
|
|
1549
|
-
};
|
|
1550
|
-
}
|
|
1551
|
-
function organizationFromCollectionUri$1(value) {
|
|
1552
|
-
return value ? azureOrganizationFromUrl(value) : void 0;
|
|
1553
|
-
}
|
|
1554
|
-
function branchName(ref) {
|
|
1555
|
-
return ref.replace(/^refs\/heads\//, "");
|
|
1556
|
-
}
|
|
1557
|
-
function repositoryWebUrl(organization, project, repository) {
|
|
1558
|
-
return `https://dev.azure.com/${encodeURIComponent(organization)}/${encodeURIComponent(project)}/_git/${encodeURIComponent(repository)}`;
|
|
1559
|
-
}
|
|
1560
|
-
function trimLeadingSlash(value) {
|
|
1561
|
-
return value.replace(/^\/+/, "");
|
|
1562
|
-
}
|
|
1563
|
-
//#endregion
|
|
1564
|
-
//#region src/hosts/bitbucket/schema.ts
|
|
1565
|
-
const bitbucketRepositorySchema = z.looseObject({
|
|
1566
|
-
uuid: z.string().min(1),
|
|
1567
|
-
name: z.string().min(1),
|
|
1568
|
-
full_name: z.string().min(1),
|
|
1569
|
-
slug: z.string().min(1).optional(),
|
|
1570
|
-
links: z.looseObject({ html: z.looseObject({ href: z.string().url() }) })
|
|
1571
|
-
}).transform((repository) => ({
|
|
1572
|
-
...repository,
|
|
1573
|
-
slug: repository.slug ?? repository.full_name.split("/").at(-1) ?? repository.full_name
|
|
1574
|
-
}));
|
|
1575
|
-
//#endregion
|
|
1576
|
-
//#region src/hosts/bitbucket/client.ts
|
|
1577
|
-
const userSchema$1 = z.looseObject({
|
|
1578
|
-
uuid: z.string().optional(),
|
|
1579
|
-
nickname: z.string().optional(),
|
|
1580
|
-
display_name: z.string().optional()
|
|
1581
|
-
});
|
|
1582
|
-
const endpointSchema = z.looseObject({
|
|
1583
|
-
branch: z.looseObject({ name: z.string().min(1) }),
|
|
1584
|
-
commit: z.looseObject({ hash: z.string().min(1) }),
|
|
1585
|
-
repository: bitbucketRepositorySchema
|
|
1586
|
-
});
|
|
1587
|
-
const pullRequestSchema = z.looseObject({
|
|
1588
|
-
id: z.number().int().positive(),
|
|
1589
|
-
draft: z.boolean().optional(),
|
|
1590
|
-
title: z.string(),
|
|
1591
|
-
description: z.string().default(""),
|
|
1592
|
-
author: userSchema$1.optional(),
|
|
1593
|
-
source: endpointSchema,
|
|
1594
|
-
destination: endpointSchema,
|
|
1595
|
-
links: z.looseObject({ html: z.looseObject({ href: z.string().url() }) })
|
|
1596
|
-
});
|
|
1597
|
-
const inlineSchema = z.looseObject({
|
|
1598
|
-
path: z.string().optional(),
|
|
1599
|
-
from: z.number().int().nullable().optional(),
|
|
1600
|
-
to: z.number().int().nullable().optional(),
|
|
1601
|
-
start_from: z.number().int().nullable().optional(),
|
|
1602
|
-
start_to: z.number().int().nullable().optional()
|
|
1603
|
-
});
|
|
1604
|
-
const commentSchema = z.looseObject({
|
|
1605
|
-
id: z.union([z.number(), z.string()]).transform(String),
|
|
1606
|
-
content: z.looseObject({ raw: z.string().default("") }),
|
|
1607
|
-
user: userSchema$1.optional(),
|
|
1608
|
-
parent: z.looseObject({ id: z.union([z.number(), z.string()]).transform(String) }).optional(),
|
|
1609
|
-
inline: inlineSchema.optional(),
|
|
1610
|
-
deleted: z.boolean().optional(),
|
|
1611
|
-
resolution: z.looseObject({}).optional()
|
|
1612
|
-
});
|
|
1613
|
-
function createBitbucketClient(env = process.env, fetch = globalThis.fetch) {
|
|
1614
|
-
const workspace = env.BITBUCKET_WORKSPACE;
|
|
1615
|
-
const repository = env.BITBUCKET_REPO_SLUG;
|
|
1616
|
-
const token = env.BITBUCKET_API_TOKEN;
|
|
1617
|
-
const email = env.BITBUCKET_EMAIL;
|
|
1618
|
-
if (!workspace) throw new Error("BITBUCKET_WORKSPACE is required for Bitbucket Cloud API calls");
|
|
1619
|
-
if (!repository) throw new Error("BITBUCKET_REPO_SLUG is required for Bitbucket Cloud API calls");
|
|
1620
|
-
if (!token || !email) throw new Error("BITBUCKET_EMAIL and BITBUCKET_API_TOKEN are required");
|
|
1621
|
-
const authorization = `Basic ${Buffer.from(`${email}:${token}`).toString("base64")}`;
|
|
1622
|
-
const repositoryApiPath = `/2.0/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repository)}/`;
|
|
1623
|
-
const api = createCodeHostHttpClient({
|
|
1624
|
-
baseUrl: `https://api.bitbucket.org${repositoryApiPath}`,
|
|
1625
|
-
headers: { Authorization: authorization },
|
|
1626
|
-
fetch
|
|
1627
|
-
});
|
|
1628
|
-
const rootApi = createCodeHostHttpClient({
|
|
1629
|
-
baseUrl: "https://api.bitbucket.org/2.0/",
|
|
1630
|
-
headers: { Authorization: authorization },
|
|
1631
|
-
fetch
|
|
1632
|
-
});
|
|
1633
|
-
const prPath = (id) => `pullrequests/${id}`;
|
|
1634
|
-
return {
|
|
1635
|
-
workspace,
|
|
1636
|
-
repository,
|
|
1637
|
-
async currentUser() {
|
|
1638
|
-
const value = await rootApi.json("user", userSchema$1);
|
|
1639
|
-
return {
|
|
1640
|
-
uuid: value.uuid,
|
|
1641
|
-
nickname: value.nickname,
|
|
1642
|
-
displayName: value.display_name
|
|
1643
|
-
};
|
|
1644
|
-
},
|
|
1645
|
-
async getRepository() {
|
|
1646
|
-
const value = await api.json("", bitbucketRepositorySchema);
|
|
1647
|
-
return {
|
|
1648
|
-
uuid: value.uuid,
|
|
1649
|
-
slug: value.slug,
|
|
1650
|
-
fullName: value.full_name,
|
|
1651
|
-
url: value.links.html.href
|
|
1652
|
-
};
|
|
1653
|
-
},
|
|
1654
|
-
async getRepositoryPermission(actor, repositoryUuid) {
|
|
1655
|
-
const permissionEmail = env.BITBUCKET_PERMISSION_EMAIL;
|
|
1656
|
-
const permissionToken = env.BITBUCKET_PERMISSION_API_TOKEN;
|
|
1657
|
-
if (!permissionEmail || !permissionToken) throw new Error("BITBUCKET_PERMISSION_EMAIL and BITBUCKET_PERMISSION_API_TOKEN are required for Bitbucket permission checks");
|
|
1658
|
-
const permissionApi = createCodeHostHttpClient({
|
|
1659
|
-
baseUrl: "https://api.bitbucket.org/2.0/",
|
|
1660
|
-
headers: { Authorization: `Basic ${Buffer.from(`${permissionEmail}:${permissionToken}`).toString("base64")}` },
|
|
1661
|
-
fetch
|
|
1662
|
-
});
|
|
1663
|
-
const query = encodeURIComponent(`repository.uuid="${escapeBitbucketQueryValue(repositoryUuid)}" AND user.nickname="${escapeBitbucketQueryValue(actor)}"`);
|
|
1664
|
-
return (await permissionApi.json(`workspaces/${encodeURIComponent(workspace)}/permissions/repositories?q=${query}&pagelen=100`, pagedSchema(z.looseObject({
|
|
1665
|
-
permission: z.enum([
|
|
1666
|
-
"read",
|
|
1667
|
-
"write",
|
|
1668
|
-
"admin"
|
|
1669
|
-
]),
|
|
1670
|
-
user: userSchema$1
|
|
1671
|
-
})))).values.find((entry) => entry.user.nickname === actor)?.permission ?? "none";
|
|
1672
|
-
},
|
|
1673
|
-
getPullRequest: (id) => api.json(prPath(id), pullRequestSchema),
|
|
1674
|
-
async loadChange(options) {
|
|
1675
|
-
if (options.workspace !== workspace || options.repository !== repository) throw new Error("Bitbucket client coordinates do not match the requested repository");
|
|
1676
|
-
const pullRequest = await this.getPullRequest(options.changeNumber);
|
|
1677
|
-
return {
|
|
1678
|
-
repository: {
|
|
1679
|
-
slug: pullRequest.destination.repository.full_name,
|
|
1680
|
-
url: pullRequest.destination.repository.links.html.href
|
|
1681
|
-
},
|
|
1682
|
-
coordinates: {
|
|
1683
|
-
provider: "bitbucket",
|
|
1684
|
-
workspace,
|
|
1685
|
-
repository,
|
|
1686
|
-
repositoryUuid: pullRequest.destination.repository.uuid
|
|
1687
|
-
},
|
|
1688
|
-
change: {
|
|
1689
|
-
number: pullRequest.id,
|
|
1690
|
-
isDraft: pullRequest.draft,
|
|
1691
|
-
title: pullRequest.title,
|
|
1692
|
-
description: pullRequest.description,
|
|
1693
|
-
url: pullRequest.links.html.href,
|
|
1694
|
-
author: pullRequest.author?.nickname ? { login: pullRequest.author.nickname } : void 0,
|
|
1695
|
-
base: {
|
|
1696
|
-
sha: pullRequest.destination.commit.hash,
|
|
1697
|
-
ref: pullRequest.destination.branch.name,
|
|
1698
|
-
url: pullRequest.destination.repository.links.html.href
|
|
1699
|
-
},
|
|
1700
|
-
head: {
|
|
1701
|
-
sha: pullRequest.source.commit.hash,
|
|
1702
|
-
ref: pullRequest.source.branch.name,
|
|
1703
|
-
url: pullRequest.source.repository.links.html.href
|
|
1704
|
-
},
|
|
1705
|
-
isFork: pullRequest.source.repository.uuid !== pullRequest.destination.repository.uuid
|
|
1706
|
-
}
|
|
1707
|
-
};
|
|
1708
|
-
},
|
|
1709
|
-
async listComments(id) {
|
|
1710
|
-
return (await listAll(api, `${prPath(id)}/comments`, commentSchema, repositoryApiPath)).filter((comment) => !comment.deleted);
|
|
1711
|
-
},
|
|
1712
|
-
createComment: (id, body) => api.json(`${prPath(id)}/comments`, commentSchema, jsonRequest$1("POST", body)),
|
|
1713
|
-
updateComment: (id, commentId, content) => api.json(`${prPath(id)}/comments/${encodeURIComponent(commentId)}`, commentSchema, jsonRequest$1("PUT", { content: { raw: content } })),
|
|
1714
|
-
async replyToComment(id, commentId, content) {
|
|
1715
|
-
const parentId = positiveCommentId(commentId);
|
|
1716
|
-
return await api.json(`${prPath(id)}/comments`, commentSchema, jsonRequest$1("POST", {
|
|
1717
|
-
content: { raw: content },
|
|
1718
|
-
parent: { id: parentId }
|
|
1719
|
-
}));
|
|
1720
|
-
},
|
|
1721
|
-
async resolveComment(id, commentId) {
|
|
1722
|
-
positiveCommentId(commentId);
|
|
1723
|
-
await api.json(`${prPath(id)}/comments/${encodeURIComponent(commentId)}/resolve`, z.unknown(), { method: "POST" });
|
|
1724
|
-
},
|
|
1725
|
-
async setStatus(sha, key, body) {
|
|
1726
|
-
return (await api.json(`commit/${encodeURIComponent(sha)}/statuses/build`, z.looseObject({ key: z.string().default(key) }), jsonRequest$1("POST", {
|
|
1727
|
-
...body,
|
|
1728
|
-
key
|
|
1729
|
-
}))).key;
|
|
1730
|
-
}
|
|
1731
|
-
};
|
|
1732
|
-
}
|
|
1733
|
-
function positiveCommentId(value) {
|
|
1734
|
-
const id = Number(value);
|
|
1735
|
-
if (!Number.isSafeInteger(id) || id <= 0) throw new Error("Bitbucket comment ID must be a positive integer");
|
|
1736
|
-
return id;
|
|
1737
|
-
}
|
|
1738
|
-
function bitbucketStatusState(state) {
|
|
1739
|
-
if (state === "pending") return "INPROGRESS";
|
|
1740
|
-
if (state === "failure") return "FAILED";
|
|
1741
|
-
if (state === "neutral") return "STOPPED";
|
|
1742
|
-
return "SUCCESSFUL";
|
|
1743
|
-
}
|
|
1744
|
-
function escapeBitbucketQueryValue(value) {
|
|
1745
|
-
return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
|
|
1746
|
-
}
|
|
1747
|
-
function pagedSchema(item) {
|
|
1748
|
-
return z.looseObject({
|
|
1749
|
-
values: z.array(item),
|
|
1750
|
-
next: z.string().url().optional()
|
|
1751
|
-
});
|
|
1752
|
-
}
|
|
1753
|
-
async function listAll(api, path, schema, allowedPathPrefix) {
|
|
1754
|
-
const values = [];
|
|
1755
|
-
let next = path;
|
|
1756
|
-
while (next) {
|
|
1757
|
-
const page = await api.json(next, pagedSchema(schema));
|
|
1758
|
-
values.push(...page.values);
|
|
1759
|
-
if (page.next) {
|
|
1760
|
-
const url = new URL(page.next);
|
|
1761
|
-
if (url.origin !== "https://api.bitbucket.org" || !url.pathname.startsWith(allowedPathPrefix)) throw new Error("Bitbucket pagination URL must stay inside the configured repository API");
|
|
1762
|
-
}
|
|
1763
|
-
next = page.next;
|
|
1764
|
-
}
|
|
1765
|
-
return values;
|
|
1766
|
-
}
|
|
1767
|
-
function jsonRequest$1(method, body) {
|
|
1768
|
-
return {
|
|
1769
|
-
method,
|
|
1770
|
-
headers: { "Content-Type": "application/json" },
|
|
1771
|
-
body: JSON.stringify(body)
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
//#endregion
|
|
1775
|
-
//#region src/hosts/gitlab/client.ts
|
|
1776
|
-
const userSchema = z.looseObject({
|
|
1777
|
-
id: z.number().int().positive(),
|
|
1778
|
-
username: z.string().min(1)
|
|
1779
|
-
});
|
|
1780
|
-
const noteSchema = z.looseObject({
|
|
1781
|
-
id: z.union([z.number(), z.string()]).transform(String),
|
|
1782
|
-
body: z.string(),
|
|
1783
|
-
author: userSchema.optional()
|
|
1784
|
-
});
|
|
1785
|
-
const positionSchema = z.looseObject({
|
|
1786
|
-
new_path: z.string().optional(),
|
|
1787
|
-
old_path: z.string().optional(),
|
|
1788
|
-
new_line: z.number().int().positive().nullish().transform((value) => value ?? void 0),
|
|
1789
|
-
old_line: z.number().int().positive().nullish().transform((value) => value ?? void 0),
|
|
1790
|
-
line_range: z.looseObject({
|
|
1791
|
-
start: z.looseObject({
|
|
1792
|
-
new_line: z.number().int().positive().nullish(),
|
|
1793
|
-
old_line: z.number().int().positive().nullish()
|
|
1794
|
-
}),
|
|
1795
|
-
end: z.looseObject({
|
|
1796
|
-
new_line: z.number().int().positive().nullish(),
|
|
1797
|
-
old_line: z.number().int().positive().nullish()
|
|
1798
|
-
})
|
|
1799
|
-
}).optional()
|
|
1800
|
-
});
|
|
1801
|
-
const discussionNoteSchema = noteSchema.extend({
|
|
1802
|
-
resolvable: z.boolean().optional(),
|
|
1803
|
-
resolved: z.boolean().optional(),
|
|
1804
|
-
position: positionSchema.nullable().optional()
|
|
1805
|
-
});
|
|
1806
|
-
const discussionSchema = z.looseObject({
|
|
1807
|
-
id: z.string().min(1),
|
|
1808
|
-
individual_note: z.boolean().optional(),
|
|
1809
|
-
notes: z.array(discussionNoteSchema)
|
|
1810
|
-
});
|
|
1811
|
-
const diffRefsSchema = z.looseObject({
|
|
1812
|
-
base_sha: z.string().min(1),
|
|
1813
|
-
start_sha: z.string().min(1),
|
|
1814
|
-
head_sha: z.string().min(1)
|
|
1815
|
-
});
|
|
1816
|
-
const mergeRequestResponseSchema = z.looseObject({
|
|
1817
|
-
iid: z.number().int().positive(),
|
|
1818
|
-
title: z.string().default(""),
|
|
1819
|
-
description: z.string().nullable().optional(),
|
|
1820
|
-
web_url: z.string().url().optional(),
|
|
1821
|
-
source_branch: z.string().min(1),
|
|
1822
|
-
target_branch: z.string().min(1),
|
|
1823
|
-
source_project_id: z.number().int().positive(),
|
|
1824
|
-
target_project_id: z.number().int().positive(),
|
|
1825
|
-
sha: z.string().min(1),
|
|
1826
|
-
draft: z.boolean().optional(),
|
|
1827
|
-
author: userSchema.optional(),
|
|
1828
|
-
references: z.looseObject({ full: z.string().optional() }).optional(),
|
|
1829
|
-
diff_refs: diffRefsSchema.nullable().optional()
|
|
1830
|
-
});
|
|
1831
|
-
const mergeRequestSchema = mergeRequestResponseSchema.extend({ diff_refs: diffRefsSchema });
|
|
1832
|
-
const memberSchema = z.looseObject({ access_level: z.number().int().min(0) });
|
|
1833
|
-
const statusSchema = z.looseObject({ id: z.union([z.number(), z.string()]).optional() });
|
|
1834
|
-
function createGitLabClient(env = process.env, fetch = globalThis.fetch, sleep = (milliseconds) => Bun.sleep(milliseconds)) {
|
|
1835
|
-
const token = env.GITLAB_TOKEN ?? env.CI_JOB_TOKEN;
|
|
1836
|
-
if (!token) throw new Error("GITLAB_TOKEN or CI_JOB_TOKEN is required for GitLab API calls");
|
|
1837
|
-
const headers = env.GITLAB_TOKEN ? { "PRIVATE-TOKEN": token } : { "JOB-TOKEN": token };
|
|
1838
|
-
const clientOptions = {
|
|
1839
|
-
baseUrl: `${(env.CI_API_V4_URL ?? "https://gitlab.com/api/v4").replace(/\/$/, "")}/`,
|
|
1840
|
-
headers,
|
|
1841
|
-
fetch,
|
|
1842
|
-
sleep: async (milliseconds) => void await sleep(milliseconds)
|
|
1843
|
-
};
|
|
1844
|
-
const api = createCodeHostHttpClient(clientOptions);
|
|
1845
|
-
const statusApi = createCodeHostHttpClient({
|
|
1846
|
-
...clientOptions,
|
|
1847
|
-
retryNonIdempotentStatuses: [409]
|
|
1848
|
-
});
|
|
1849
|
-
return {
|
|
1850
|
-
async getProject(project) {
|
|
1851
|
-
const value = await api.json(`projects/${encodeURIComponent(project)}`, z.looseObject({
|
|
1852
|
-
id: z.union([z.number(), z.string()]).transform(String),
|
|
1853
|
-
path_with_namespace: z.string().min(1)
|
|
1854
|
-
}));
|
|
1855
|
-
return {
|
|
1856
|
-
id: value.id,
|
|
1857
|
-
path: value.path_with_namespace
|
|
1858
|
-
};
|
|
1859
|
-
},
|
|
1860
|
-
currentUser: () => api.json("user", userSchema),
|
|
1861
|
-
async loadChange(options) {
|
|
1862
|
-
const mergeRequest = await this.getMergeRequest(options.projectId, options.changeNumber);
|
|
1863
|
-
return {
|
|
1864
|
-
repository: {
|
|
1865
|
-
slug: options.projectPath,
|
|
1866
|
-
url: projectWebUrl(mergeRequest.web_url)
|
|
1867
|
-
},
|
|
1868
|
-
coordinates: {
|
|
1869
|
-
provider: "gitlab",
|
|
1870
|
-
projectId: String(mergeRequest.target_project_id),
|
|
1871
|
-
projectPath: options.projectPath
|
|
1872
|
-
},
|
|
1873
|
-
change: {
|
|
1874
|
-
number: mergeRequest.iid,
|
|
1875
|
-
title: mergeRequest.title,
|
|
1876
|
-
description: mergeRequest.description ?? "",
|
|
1877
|
-
url: mergeRequest.web_url,
|
|
1878
|
-
author: mergeRequest.author ? { login: mergeRequest.author.username } : void 0,
|
|
1879
|
-
base: {
|
|
1880
|
-
sha: mergeRequest.diff_refs.start_sha,
|
|
1881
|
-
ref: mergeRequest.target_branch
|
|
1882
|
-
},
|
|
1883
|
-
head: {
|
|
1884
|
-
sha: mergeRequest.diff_refs.head_sha,
|
|
1885
|
-
ref: mergeRequest.source_branch
|
|
1886
|
-
},
|
|
1887
|
-
isFork: mergeRequest.source_project_id !== mergeRequest.target_project_id,
|
|
1888
|
-
isDraft: mergeRequest.draft
|
|
1889
|
-
}
|
|
1890
|
-
};
|
|
1891
|
-
},
|
|
1892
|
-
async getMergeRequest(projectId, changeNumber) {
|
|
1893
|
-
const requestPath = `projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}`;
|
|
1894
|
-
for (let attempt = 0;; attempt += 1) {
|
|
1895
|
-
const response = await api.json(requestPath, mergeRequestResponseSchema);
|
|
1896
|
-
const prepared = mergeRequestSchema.safeParse(response);
|
|
1897
|
-
if (prepared.success) return prepared.data;
|
|
1898
|
-
if (attempt >= 4) throw new Error("GitLab merge request diff refs were not prepared after 5 attempts");
|
|
1899
|
-
await sleep(250 * 2 ** attempt);
|
|
1900
|
-
}
|
|
1901
|
-
},
|
|
1902
|
-
async getRepositoryPermission(projectId, username) {
|
|
1903
|
-
const user = (await api.json(`users?username=${encodeURIComponent(username)}`, z.array(userSchema))).find((candidate) => candidate.username === username);
|
|
1904
|
-
if (!user) return "none";
|
|
1905
|
-
try {
|
|
1906
|
-
return accessLevelPermission((await api.json(`projects/${encodeURIComponent(projectId)}/members/all/${user.id}`, memberSchema)).access_level);
|
|
1907
|
-
} catch (error) {
|
|
1908
|
-
if (error instanceof CodeHostHttpError && error.status === 404) return "none";
|
|
1909
|
-
throw error;
|
|
1910
|
-
}
|
|
1911
|
-
},
|
|
1912
|
-
listNotes: (projectId, changeNumber) => paginated(api, `projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/notes`, noteSchema),
|
|
1913
|
-
createNote: (projectId, changeNumber, body) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/notes`, noteSchema, jsonRequest("POST", { body })),
|
|
1914
|
-
updateNote: (projectId, changeNumber, noteId, body) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/notes/${encodeURIComponent(noteId)}`, noteSchema, jsonRequest("PUT", { body })),
|
|
1915
|
-
listDiscussions: (projectId, changeNumber) => paginated(api, `projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions`, discussionSchema),
|
|
1916
|
-
getDiscussion: (projectId, changeNumber, discussionId) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions/${encodeURIComponent(discussionId)}`, discussionSchema),
|
|
1917
|
-
async findReplyParent(projectId, changeNumber, noteId, discussionId) {
|
|
1918
|
-
if (!discussionId) return void 0;
|
|
1919
|
-
const discussion = await this.getDiscussion(projectId, changeNumber, discussionId);
|
|
1920
|
-
if (!discussion.notes.some((note) => note.id === noteId) || discussion.notes[0]?.id === noteId) return;
|
|
1921
|
-
return discussion.notes[0]?.id;
|
|
1922
|
-
},
|
|
1923
|
-
createDiscussion: (projectId, changeNumber, body, position) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions`, discussionSchema, jsonRequest("POST", {
|
|
1924
|
-
body,
|
|
1925
|
-
position
|
|
1926
|
-
})),
|
|
1927
|
-
replyDiscussion: (projectId, changeNumber, discussionId, body) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions/${encodeURIComponent(discussionId)}/notes`, noteSchema, jsonRequest("POST", { body })),
|
|
1928
|
-
async resolveDiscussion(projectId, changeNumber, discussionId) {
|
|
1929
|
-
await api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions/${encodeURIComponent(discussionId)}`, discussionSchema, jsonRequest("PUT", { resolved: true }));
|
|
1930
|
-
},
|
|
1931
|
-
async setStatus(projectId, sha, name, state, description) {
|
|
1932
|
-
const status = await statusApi.json(`projects/${encodeURIComponent(projectId)}/statuses/${encodeURIComponent(sha)}`, statusSchema, jsonRequest("POST", {
|
|
1933
|
-
state: gitLabStatusState(state),
|
|
1934
|
-
name,
|
|
1935
|
-
description: boundedStatusDescription(description)
|
|
1936
|
-
}));
|
|
1937
|
-
return String(status.id ?? name);
|
|
1938
|
-
}
|
|
1939
|
-
};
|
|
1940
|
-
}
|
|
1941
|
-
const MAX_PAGINATION_PAGES = 100;
|
|
1942
|
-
async function paginated(client, path, schema) {
|
|
1943
|
-
const values = [];
|
|
1944
|
-
for (let page = 1; page <= MAX_PAGINATION_PAGES; page += 1) {
|
|
1945
|
-
const separator = path.includes("?") ? "&" : "?";
|
|
1946
|
-
const batch = await client.json(`${path}${separator}per_page=100&page=${page}`, z.array(schema));
|
|
1947
|
-
values.push(...batch);
|
|
1948
|
-
if (batch.length < 100) return values;
|
|
1949
|
-
}
|
|
1950
|
-
throw new Error(`GitLab pagination exceeded ${MAX_PAGINATION_PAGES} pages for ${path}`);
|
|
1951
|
-
}
|
|
1952
|
-
function jsonRequest(method, body) {
|
|
1953
|
-
return {
|
|
1954
|
-
method,
|
|
1955
|
-
headers: { "Content-Type": "application/json" },
|
|
1956
|
-
body: JSON.stringify(body)
|
|
1957
|
-
};
|
|
1958
|
-
}
|
|
1959
|
-
function accessLevelPermission(level) {
|
|
1960
|
-
if (level >= 50) return "admin";
|
|
1961
|
-
if (level >= 40) return "maintain";
|
|
1962
|
-
if (level >= 30) return "write";
|
|
1963
|
-
if (level >= 15) return "triage";
|
|
1964
|
-
if (level >= 10) return "read";
|
|
1965
|
-
return "none";
|
|
1966
|
-
}
|
|
1967
|
-
function boundedStatusDescription(description) {
|
|
1968
|
-
return description && description.length > 255 ? `${description.slice(0, 252).trimEnd()}...` : description;
|
|
1969
|
-
}
|
|
1970
|
-
function gitLabStatusState(state) {
|
|
1971
|
-
switch (state) {
|
|
1972
|
-
case "pending": return "pending";
|
|
1973
|
-
case "success": return "success";
|
|
1974
|
-
case "failure": return "failed";
|
|
1975
|
-
case "neutral": return "skipped";
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
|
-
function projectWebUrl(mergeRequestUrl) {
|
|
1979
|
-
return mergeRequestUrl?.replace(/\/-\/merge_requests\/\d+$/, "");
|
|
1980
|
-
}
|
|
1981
|
-
//#endregion
|
|
1982
|
-
//#region src/diff/git.ts
|
|
1983
|
-
function runGit$1(args, cwd, maxBuffer) {
|
|
1984
|
-
const result = Bun.spawnSync(["git", ...args], {
|
|
1985
|
-
cwd,
|
|
1986
|
-
env: process.env,
|
|
1987
|
-
maxBuffer,
|
|
1988
|
-
stderr: "pipe",
|
|
1989
|
-
stdout: "pipe"
|
|
1990
|
-
});
|
|
1991
|
-
if (result.exitCode !== 0) {
|
|
1992
|
-
const failure = result.stderr?.toString().trim() || "unknown error";
|
|
1993
|
-
throw new Error(`git ${args.join(" ")} failed: ${failure}`);
|
|
1994
|
-
}
|
|
1995
|
-
return result.stdout.toString();
|
|
1996
|
-
}
|
|
1997
|
-
//#endregion
|
|
1998
|
-
//#region src/diff/diff.ts
|
|
1999
|
-
const lockFilePattern = /(^|\/)(bun\.lock|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|Cargo\.lock)$/;
|
|
2000
|
-
const generatedPattern = /(^|\/)(dist|build|coverage|vendor)\//;
|
|
2001
|
-
const maxInlineChangedLines = 1e3;
|
|
2002
|
-
const maxCommentableRangeLines = 5e3;
|
|
2003
|
-
function buildDiffManifest(options) {
|
|
2004
|
-
const mergeBaseSha = runGit$1([
|
|
2005
|
-
"merge-base",
|
|
2006
|
-
options.baseSha,
|
|
2007
|
-
options.headSha
|
|
2008
|
-
], options.cwd).trim();
|
|
2009
|
-
const diffHead = options.includeWorkingTree ? void 0 : options.headSha;
|
|
2010
|
-
const nameStatus = runGit$1(buildDiffArgs([
|
|
2011
|
-
"--name-status",
|
|
2012
|
-
"-z",
|
|
2013
|
-
"--find-renames"
|
|
2014
|
-
], mergeBaseSha, diffHead), options.cwd);
|
|
2015
|
-
const diffStats = getDiffStats(options.cwd, mergeBaseSha, diffHead);
|
|
2016
|
-
const rawPatch = parseRawPatch(runGit$1(buildUnifiedDiffArgs(mergeBaseSha, diffHead, getPreExcludedFiles(diffStats)), options.cwd));
|
|
2017
|
-
const files = parseNameStatus(nameStatus);
|
|
2018
|
-
const parsedDiff = parseUnifiedDiff(rawPatch.patch, rawPatch.filePaths);
|
|
2019
|
-
for (const file of files) {
|
|
2020
|
-
const stats = diffStats.get(file.path);
|
|
2021
|
-
file.additions = stats?.additions ?? 0;
|
|
2022
|
-
file.deletions = stats?.deletions ?? 0;
|
|
2023
|
-
const preExcludedReason = stats?.excludedReason;
|
|
2024
|
-
const parsedFile = parsedDiff.get(file.path);
|
|
2025
|
-
file.hunks = preExcludedReason ? [] : parsedFile?.hunks ?? [];
|
|
2026
|
-
file.commentableRanges = preExcludedReason ? [] : parsedFile?.commentableRanges ?? [];
|
|
2027
|
-
const excludedReason = preExcludedReason ?? getExcludedReason(file);
|
|
2028
|
-
if (excludedReason) {
|
|
2029
|
-
file.hunks = [];
|
|
2030
|
-
file.commentableRanges = [];
|
|
2031
|
-
file.excludedReason = excludedReason;
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
return parseDiffManifest({
|
|
2035
|
-
baseSha: options.baseSha,
|
|
2036
|
-
headSha: options.headSha,
|
|
2037
|
-
mergeBaseSha,
|
|
2038
|
-
files
|
|
2039
|
-
});
|
|
2040
|
-
}
|
|
2041
|
-
function parseNameStatus(output) {
|
|
2042
|
-
const fields = output.split("\0");
|
|
2043
|
-
const files = [];
|
|
2044
|
-
let index = 0;
|
|
2045
|
-
while (index < fields.length) {
|
|
2046
|
-
const rawStatus = fields[index++];
|
|
2047
|
-
if (!rawStatus) continue;
|
|
2048
|
-
const firstPath = fields[index++] ?? "";
|
|
2049
|
-
const status = parseFileStatus(rawStatus);
|
|
2050
|
-
if (status === "renamed") {
|
|
2051
|
-
const secondPath = fields[index++] ?? "";
|
|
2052
|
-
files.push(baseFile(secondPath || firstPath, status, firstPath));
|
|
2053
|
-
continue;
|
|
2054
|
-
}
|
|
2055
|
-
files.push(baseFile(firstPath, status));
|
|
2056
|
-
}
|
|
2057
|
-
return files;
|
|
2058
|
-
}
|
|
2059
|
-
function parseUnifiedDiff(diff, filePaths) {
|
|
2060
|
-
const state = createDiffParserState(filePaths);
|
|
2061
|
-
for (const line of diff.split("\n")) parseUnifiedDiffLine(state, line);
|
|
2062
|
-
finishActiveHunk(state);
|
|
2063
|
-
return state.filesByPath;
|
|
2064
|
-
}
|
|
2065
|
-
function getDiffStats(cwd, baseSha, headSha) {
|
|
2066
|
-
const output = runGit$1(buildDiffArgs([
|
|
2067
|
-
"--numstat",
|
|
2068
|
-
"-z",
|
|
2069
|
-
"--find-renames"
|
|
2070
|
-
], baseSha, headSha), cwd);
|
|
2071
|
-
const stats = /* @__PURE__ */ new Map();
|
|
2072
|
-
for (const stat of parseNumstat(output)) {
|
|
2073
|
-
if (stat.binary) {
|
|
2074
|
-
stats.set(stat.path, {
|
|
2075
|
-
additions: 0,
|
|
2076
|
-
deletions: 0,
|
|
2077
|
-
excludedReason: "binary diff"
|
|
2078
|
-
});
|
|
2079
|
-
continue;
|
|
2080
|
-
}
|
|
2081
|
-
stats.set(stat.path, {
|
|
2082
|
-
additions: stat.additions,
|
|
2083
|
-
deletions: stat.deletions,
|
|
2084
|
-
excludedReason: stat.additions + stat.deletions > maxInlineChangedLines ? "oversized diff" : void 0
|
|
2085
|
-
});
|
|
2086
|
-
}
|
|
2087
|
-
return stats;
|
|
2088
|
-
}
|
|
2089
|
-
function getPreExcludedFiles(stats) {
|
|
2090
|
-
const excluded = /* @__PURE__ */ new Map();
|
|
2091
|
-
for (const [filePath, stat] of stats) if (stat.excludedReason) excluded.set(filePath, stat.excludedReason);
|
|
2092
|
-
return excluded;
|
|
2093
|
-
}
|
|
2094
|
-
function buildUnifiedDiffArgs(baseSha, headSha, excludedFiles) {
|
|
2095
|
-
const args = buildDiffArgs([
|
|
2096
|
-
"--raw",
|
|
2097
|
-
"-z",
|
|
2098
|
-
"--patch",
|
|
2099
|
-
"--submodule=short",
|
|
2100
|
-
"--unified=80",
|
|
2101
|
-
"--find-renames"
|
|
2102
|
-
], baseSha, headSha);
|
|
2103
|
-
if (excludedFiles.size === 0) return args;
|
|
2104
|
-
return [
|
|
2105
|
-
...args,
|
|
2106
|
-
"--",
|
|
2107
|
-
".",
|
|
2108
|
-
...[...excludedFiles.keys()].map((filePath) => `:(exclude,literal)${filePath}`)
|
|
2109
|
-
];
|
|
2110
|
-
}
|
|
2111
|
-
function buildDiffArgs(options, baseSha, headSha) {
|
|
2112
|
-
return headSha ? [
|
|
2113
|
-
"diff",
|
|
2114
|
-
...options,
|
|
2115
|
-
baseSha,
|
|
2116
|
-
headSha
|
|
2117
|
-
] : [
|
|
2118
|
-
"diff",
|
|
2119
|
-
...options,
|
|
2120
|
-
baseSha
|
|
2121
|
-
];
|
|
2122
|
-
}
|
|
2123
|
-
function parseNumstat(output) {
|
|
2124
|
-
const fields = output.split("\0");
|
|
2125
|
-
const stats = [];
|
|
2126
|
-
let index = 0;
|
|
2127
|
-
while (index < fields.length) {
|
|
2128
|
-
const header = parseNumstatHeader(fields[index++] ?? "");
|
|
2129
|
-
if (!header) continue;
|
|
2130
|
-
const resolvedPath = resolveNumstatPath(header.path, fields, index);
|
|
2131
|
-
index = resolvedPath.nextIndex;
|
|
2132
|
-
const filePath = resolvedPath.path;
|
|
2133
|
-
if (!filePath) continue;
|
|
2134
|
-
const binary = header.rawAdditions === "-" || header.rawDeletions === "-";
|
|
2135
|
-
stats.push({
|
|
2136
|
-
path: filePath,
|
|
2137
|
-
additions: binary ? 0 : Number(header.rawAdditions),
|
|
2138
|
-
deletions: binary ? 0 : Number(header.rawDeletions),
|
|
2139
|
-
binary
|
|
2140
|
-
});
|
|
2141
|
-
}
|
|
2142
|
-
return stats;
|
|
1303
|
+
function parseNumstat(output) {
|
|
1304
|
+
const fields = output.split("\0");
|
|
1305
|
+
const stats = [];
|
|
1306
|
+
let index = 0;
|
|
1307
|
+
while (index < fields.length) {
|
|
1308
|
+
const header = parseNumstatHeader(fields[index++] ?? "");
|
|
1309
|
+
if (!header) continue;
|
|
1310
|
+
const resolvedPath = resolveNumstatPath(header.path, fields, index);
|
|
1311
|
+
index = resolvedPath.nextIndex;
|
|
1312
|
+
const filePath = resolvedPath.path;
|
|
1313
|
+
if (!filePath) continue;
|
|
1314
|
+
const binary = header.rawAdditions === "-" || header.rawDeletions === "-";
|
|
1315
|
+
stats.push({
|
|
1316
|
+
path: filePath,
|
|
1317
|
+
additions: binary ? 0 : Number(header.rawAdditions),
|
|
1318
|
+
deletions: binary ? 0 : Number(header.rawDeletions),
|
|
1319
|
+
binary
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
return stats;
|
|
2143
1323
|
}
|
|
2144
1324
|
function resolveNumstatPath(path, fields, nextIndex) {
|
|
2145
1325
|
if (path) return {
|
|
@@ -2202,11 +1382,15 @@ function parseFileStatus(rawStatus) {
|
|
|
2202
1382
|
return "modified";
|
|
2203
1383
|
}
|
|
2204
1384
|
function getExcludedReason(file) {
|
|
1385
|
+
const pathExcludedReason = getPathExcludedReason(file);
|
|
1386
|
+
if (pathExcludedReason) return pathExcludedReason;
|
|
1387
|
+
if (file.additions + file.deletions > maxInlineChangedLines) return "oversized diff";
|
|
1388
|
+
if (commentableRangeLineCount(file.commentableRanges) > maxCommentableRangeLines) return "oversized diff";
|
|
1389
|
+
}
|
|
1390
|
+
function getPathExcludedReason(file) {
|
|
2205
1391
|
if (file.status === "removed") return "removed file";
|
|
2206
1392
|
if (lockFilePattern.test(file.path)) return "lock file";
|
|
2207
1393
|
if (generatedPattern.test(file.path)) return "generated or build output";
|
|
2208
|
-
if (file.additions + file.deletions > maxInlineChangedLines) return "oversized diff";
|
|
2209
|
-
if (commentableRangeLineCount(file.commentableRanges) > maxCommentableRangeLines) return "oversized diff";
|
|
2210
1394
|
}
|
|
2211
1395
|
function commentableRangeLineCount(ranges) {
|
|
2212
1396
|
return ranges.reduce((total, range) => total + range.endLine - range.startLine + 1, 0);
|
|
@@ -3693,7 +2877,7 @@ function createRuntimeLog(options) {
|
|
|
3693
2877
|
},
|
|
3694
2878
|
text(level, event, text) {
|
|
3695
2879
|
if (level === "debug" && !debugEnabled) return;
|
|
3696
|
-
emitRecord(sink, secrets, level, event, void 0, redact(text, secrets));
|
|
2880
|
+
emitRecord(sink, secrets, level, event, void 0, redact$1(text, secrets));
|
|
3697
2881
|
},
|
|
3698
2882
|
textSnippet(level, event, text, snippetOptions) {
|
|
3699
2883
|
if (level === "debug" && !debugEnabled) return;
|
|
@@ -3703,7 +2887,7 @@ function createRuntimeLog(options) {
|
|
|
3703
2887
|
return formatTextSnippet(text, secrets, snippetOptions);
|
|
3704
2888
|
},
|
|
3705
2889
|
async group(name, run) {
|
|
3706
|
-
return await sink.group(redact(name, secrets), run);
|
|
2890
|
+
return await sink.group(redact$1(name, secrets), run);
|
|
3707
2891
|
},
|
|
3708
2892
|
addSecret(value) {
|
|
3709
2893
|
addSecret(secrets, value);
|
|
@@ -3726,25 +2910,25 @@ function boundedLogSnippet(text, options) {
|
|
|
3726
2910
|
function addSecret(secrets, value) {
|
|
3727
2911
|
if (value && value.length >= 4) secrets.add(value);
|
|
3728
2912
|
}
|
|
3729
|
-
function redact(message, secrets) {
|
|
2913
|
+
function redact$1(message, secrets) {
|
|
3730
2914
|
let redacted = message;
|
|
3731
2915
|
for (const secret of secrets) redacted = redacted.split(secret).join("***");
|
|
3732
2916
|
return redacted;
|
|
3733
2917
|
}
|
|
3734
2918
|
function compactFields(fields, secrets) {
|
|
3735
2919
|
const compact = {};
|
|
3736
|
-
for (const [key, value] of Object.entries(fields ?? {})) if (typeof value === "string") compact[key] = redact(value, secrets);
|
|
3737
|
-
else if (Array.isArray(value)) compact[key] = value.map((item) => redact(item, secrets));
|
|
2920
|
+
for (const [key, value] of Object.entries(fields ?? {})) if (typeof value === "string") compact[key] = redact$1(value, secrets);
|
|
2921
|
+
else if (Array.isArray(value)) compact[key] = value.map((item) => redact$1(item, secrets));
|
|
3738
2922
|
else if (value !== void 0) compact[key] = value;
|
|
3739
2923
|
return compact;
|
|
3740
2924
|
}
|
|
3741
2925
|
function formatTextSnippet(text, secrets, options) {
|
|
3742
|
-
return boundedLogSnippet(redact(text, secrets), options);
|
|
2926
|
+
return boundedLogSnippet(redact$1(text, secrets), options);
|
|
3743
2927
|
}
|
|
3744
2928
|
function emitRecord(sink, secrets, level, event, fields, text) {
|
|
3745
2929
|
sink.log({
|
|
3746
2930
|
level,
|
|
3747
|
-
event: redact(event, secrets),
|
|
2931
|
+
event: redact$1(event, secrets),
|
|
3748
2932
|
fields: compactFields(fields, secrets),
|
|
3749
2933
|
text
|
|
3750
2934
|
});
|
|
@@ -5981,300 +5165,711 @@ async function runTaskRuntime(options) {
|
|
|
5981
5165
|
const verifier = await runSynchronizeVerifier({
|
|
5982
5166
|
options,
|
|
5983
5167
|
config,
|
|
5984
|
-
provider,
|
|
5985
|
-
diffManifest,
|
|
5986
|
-
priorReviewState,
|
|
5987
|
-
runId,
|
|
5988
|
-
piRunSink: runtimeOptions.piRunSink
|
|
5168
|
+
provider,
|
|
5169
|
+
diffManifest,
|
|
5170
|
+
priorReviewState,
|
|
5171
|
+
runId,
|
|
5172
|
+
piRunSink: runtimeOptions.piRunSink
|
|
5173
|
+
});
|
|
5174
|
+
const stats = reviewStatsForRuns(piRuns, Date.now() - runtimeStarted);
|
|
5175
|
+
const redactedPublication = redactReviewPublication({
|
|
5176
|
+
main,
|
|
5177
|
+
validated,
|
|
5178
|
+
threadActions: verifier.threadActions,
|
|
5179
|
+
taskChecks,
|
|
5180
|
+
redactor: options.secretRedactor
|
|
5181
|
+
});
|
|
5182
|
+
const publishing = buildCommentPublishingPlan({
|
|
5183
|
+
event: options.event,
|
|
5184
|
+
main: redactedPublication.main,
|
|
5185
|
+
validated: redactedPublication.validated,
|
|
5186
|
+
manifest: diffManifest,
|
|
5187
|
+
maxInlineComments: config.publication.maxInlineComments,
|
|
5188
|
+
maxStoredFindings: config.publication.maxStoredFindings,
|
|
5189
|
+
showHeader: config.publication.showHeader,
|
|
5190
|
+
showFooter: config.publication.showFooter,
|
|
5191
|
+
showStats: config.publication.showStats,
|
|
5192
|
+
priorReviewState: verifier.priorReviewState,
|
|
5193
|
+
threadActions: redactedPublication.threadActions,
|
|
5194
|
+
metadata: {
|
|
5195
|
+
runtimeVersion,
|
|
5196
|
+
configVersion: options.versionCompatibility?.configVersion,
|
|
5197
|
+
trustedConfigSha: options.trustedConfigSha,
|
|
5198
|
+
trustedConfigHash: options.trustedConfigHash,
|
|
5199
|
+
reviewedHeadSha: options.event.change.head.sha,
|
|
5200
|
+
providerModels: output.providerModels.length + verifier.providerModels.length > 0 ? uniq([...output.providerModels, ...verifier.providerModels]) : [provider.model],
|
|
5201
|
+
selectedTasks,
|
|
5202
|
+
failedTasks: [],
|
|
5203
|
+
validFindings: validated.validFindings.length,
|
|
5204
|
+
droppedFindings: validated.droppedFindings.length,
|
|
5205
|
+
...stats ? { stats } : {}
|
|
5206
|
+
}
|
|
5207
|
+
});
|
|
5208
|
+
const publicationPlan = publishing.publicationPlan;
|
|
5209
|
+
publishTaskChecks(options.checkSink, redactedPublication.taskChecks);
|
|
5210
|
+
options.log?.info("review validated", {
|
|
5211
|
+
validFindings: validated.validFindings.length,
|
|
5212
|
+
droppedFindings: validated.droppedFindings.length,
|
|
5213
|
+
inlineDrafts: publishing.inlineCommentDrafts.length,
|
|
5214
|
+
threadActions: verifier.threadActions.length
|
|
5215
|
+
});
|
|
5216
|
+
return {
|
|
5217
|
+
kind: "review",
|
|
5218
|
+
provider,
|
|
5219
|
+
diffManifest,
|
|
5220
|
+
review: redactedPublication.validated.review,
|
|
5221
|
+
validated: redactedPublication.validated,
|
|
5222
|
+
publicationPlan,
|
|
5223
|
+
mainComment: publicationPlan.mainComment,
|
|
5224
|
+
inlineCommentDrafts: publishing.inlineCommentDrafts,
|
|
5225
|
+
taskChecks: redactedPublication.taskChecks,
|
|
5226
|
+
repairAttempted: output.repairAttempted
|
|
5227
|
+
};
|
|
5228
|
+
}
|
|
5229
|
+
function publishFailedRunTaskChecks(options, taskChecks) {
|
|
5230
|
+
const redacted = redactCommandPublication({
|
|
5231
|
+
body: "",
|
|
5232
|
+
taskChecks,
|
|
5233
|
+
redactor: options.secretRedactor
|
|
5234
|
+
});
|
|
5235
|
+
publishTaskChecks(options.checkSink, redacted.taskChecks);
|
|
5236
|
+
}
|
|
5237
|
+
function commandResponseResultFromOutput(options) {
|
|
5238
|
+
const commandResponse = options.output.commandResponse;
|
|
5239
|
+
if (!commandResponse) return;
|
|
5240
|
+
if (!options.commandInvocation) throw new Error("ctx.command.reply(...) is only available for command-triggered tasks");
|
|
5241
|
+
return commandResponseRuntimeResult({
|
|
5242
|
+
...options,
|
|
5243
|
+
commandResponse,
|
|
5244
|
+
commandInvocation: options.commandInvocation
|
|
5245
|
+
});
|
|
5246
|
+
}
|
|
5247
|
+
function assertReviewCommentOutput(output, hasCommandInvocation) {
|
|
5248
|
+
if (output.comment) return;
|
|
5249
|
+
throw new Error(hasCommandInvocation ? "ctx.comment(...) or ctx.command.reply(...) must be called exactly once per selected run" : "ctx.comment(...) must be called exactly once per selected run");
|
|
5250
|
+
}
|
|
5251
|
+
async function runSynchronizeVerifier(options) {
|
|
5252
|
+
if (options.options.event.rawAction !== "synchronize") return {
|
|
5253
|
+
priorReviewState: options.priorReviewState,
|
|
5254
|
+
threadActions: [],
|
|
5255
|
+
providerModels: []
|
|
5256
|
+
};
|
|
5257
|
+
const config = options.config;
|
|
5258
|
+
return await runInternalVerifier({
|
|
5259
|
+
workspace: options.options.workspace,
|
|
5260
|
+
config,
|
|
5261
|
+
event: options.options.event,
|
|
5262
|
+
provider: options.provider,
|
|
5263
|
+
verifierProvider: resolveProvider(config, config.publication.autoResolve.model ?? config.defaultProvider),
|
|
5264
|
+
plan: options.options.plan,
|
|
5265
|
+
env: options.options.env,
|
|
5266
|
+
piExecutable: options.options.piExecutable,
|
|
5267
|
+
piRunner: options.options.piRunner,
|
|
5268
|
+
log: options.options.log,
|
|
5269
|
+
diffManifest: options.diffManifest,
|
|
5270
|
+
priorReviewState: options.priorReviewState,
|
|
5271
|
+
threadContexts: await options.options.loadInlineThreadContexts?.() ?? [],
|
|
5272
|
+
mode: { kind: "synchronize" },
|
|
5273
|
+
runId: options.runId,
|
|
5274
|
+
piRunSink: options.piRunSink
|
|
5989
5275
|
});
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5276
|
+
}
|
|
5277
|
+
function createTaskContext(options) {
|
|
5278
|
+
const repositorySlugParts = options.event.repository.slug.split("/");
|
|
5279
|
+
let taskContext;
|
|
5280
|
+
taskContext = {
|
|
5281
|
+
run: { id: options.runId },
|
|
5282
|
+
repository: {
|
|
5283
|
+
root: options.workspace,
|
|
5284
|
+
owner: repositorySlugParts.length > 1 ? repositorySlugParts[0] : void 0,
|
|
5285
|
+
name: repositorySlugParts.at(-1) ?? "repo"
|
|
5286
|
+
},
|
|
5287
|
+
change: {
|
|
5288
|
+
number: options.event.change.number,
|
|
5289
|
+
title: options.event.change.title,
|
|
5290
|
+
description: options.event.change.description,
|
|
5291
|
+
url: options.event.change.url,
|
|
5292
|
+
author: options.event.change.author,
|
|
5293
|
+
base: options.event.change.base,
|
|
5294
|
+
head: options.event.change.head,
|
|
5295
|
+
isFork: options.event.change.isFork,
|
|
5296
|
+
async diffManifest(manifestOptions) {
|
|
5297
|
+
const key = JSON.stringify(manifestOptions ?? {});
|
|
5298
|
+
const cached = options.manifestCache.get(key);
|
|
5299
|
+
if (cached) return cloneDiffManifest(cached);
|
|
5300
|
+
const manifest = projectDiffManifest(options.diffManifest, manifestOptions);
|
|
5301
|
+
options.manifestCache.set(key, manifest);
|
|
5302
|
+
return cloneDiffManifest(manifest);
|
|
5303
|
+
},
|
|
5304
|
+
async changedFiles() {
|
|
5305
|
+
return options.diffManifest.files.map((file) => ({
|
|
5306
|
+
path: file.path,
|
|
5307
|
+
previousPath: file.previousPath,
|
|
5308
|
+
status: file.status
|
|
5309
|
+
}));
|
|
5310
|
+
},
|
|
5311
|
+
async currentHeadSha() {
|
|
5312
|
+
return options.event.change.head.sha;
|
|
5313
|
+
}
|
|
5314
|
+
},
|
|
5315
|
+
platform: { id: options.event.platform.id },
|
|
5316
|
+
command: options.commandInvocation ? {
|
|
5317
|
+
name: options.commandInvocation.name,
|
|
5318
|
+
line: options.commandInvocation.line,
|
|
5319
|
+
arguments: { ...options.commandInvocation.arguments },
|
|
5320
|
+
async reply(markdown) {
|
|
5321
|
+
collectCommandResponse(options.output, markdown, options.taskName);
|
|
5322
|
+
}
|
|
5323
|
+
} : void 0,
|
|
5324
|
+
secret(secret) {
|
|
5325
|
+
return resolveTaskSecret(secret, options);
|
|
5326
|
+
},
|
|
5327
|
+
pi: { async run(agent, input, runOptions) {
|
|
5328
|
+
const result = await runReviewAgent({
|
|
5329
|
+
agent,
|
|
5330
|
+
input,
|
|
5331
|
+
runOptions,
|
|
5332
|
+
runtime: {
|
|
5333
|
+
...options,
|
|
5334
|
+
taskContext,
|
|
5335
|
+
runId: options.runId,
|
|
5336
|
+
piRunSink: options.piRunSink
|
|
5337
|
+
}
|
|
5338
|
+
});
|
|
5339
|
+
options.output.providerModels.push(...result.providerModels);
|
|
5340
|
+
if (result.repairAttempted) options.output.repairAttempted = true;
|
|
5341
|
+
trackResultFindingScope(options.output, result.value, runOptions?.paths);
|
|
5342
|
+
return agentOutputForTaskContext(agent, result.value);
|
|
5343
|
+
} },
|
|
5344
|
+
review: { async prior() {
|
|
5345
|
+
return priorReviewForTask(options.priorMainComment, options.priorReviewState);
|
|
5346
|
+
} },
|
|
5347
|
+
check: createCheckHandle(options.output),
|
|
5348
|
+
async comment(value) {
|
|
5349
|
+
collectComment(options.output, value, options.taskName);
|
|
5350
|
+
},
|
|
5351
|
+
log: options.taskLog ?? console
|
|
5352
|
+
};
|
|
5353
|
+
return taskContext;
|
|
5354
|
+
}
|
|
5355
|
+
function agentOutputForTaskContext(_agent, value) {
|
|
5356
|
+
return value;
|
|
5357
|
+
}
|
|
5358
|
+
function resolveTaskSecret(secret, options) {
|
|
5359
|
+
if (secret.kind !== "pipr.secret" || typeof secret.name !== "string") throw new Error("ctx.secret(...) requires a pipr.secret reference");
|
|
5360
|
+
const value = (options.env ?? process.env)[secret.name];
|
|
5361
|
+
if (!value) throw new Error(`Missing secret env var: ${secret.name}`);
|
|
5362
|
+
options.log?.addSecret(value);
|
|
5363
|
+
options.secretRedactor?.addSecret(value);
|
|
5364
|
+
return value;
|
|
5365
|
+
}
|
|
5366
|
+
function commandResponseRuntimeResult(options) {
|
|
5367
|
+
const redacted = redactCommandPublication({
|
|
5368
|
+
body: options.commandResponse.value,
|
|
5369
|
+
taskChecks: options.taskChecks,
|
|
5996
5370
|
redactor: options.secretRedactor
|
|
5997
5371
|
});
|
|
5998
|
-
|
|
5372
|
+
return {
|
|
5373
|
+
kind: "command-response",
|
|
5374
|
+
provider: options.provider,
|
|
5375
|
+
diffManifest: options.diffManifest,
|
|
5376
|
+
taskChecks: redacted.taskChecks,
|
|
5377
|
+
repairAttempted: options.output.repairAttempted,
|
|
5378
|
+
commandResponse: {
|
|
5379
|
+
commandName: options.commandInvocation.name,
|
|
5380
|
+
line: options.commandInvocation.line,
|
|
5381
|
+
arguments: options.commandInvocation.arguments,
|
|
5382
|
+
body: redacted.body
|
|
5383
|
+
}
|
|
5384
|
+
};
|
|
5385
|
+
}
|
|
5386
|
+
function publishTaskChecks(sink, checks) {
|
|
5387
|
+
for (const check of checks) sink?.setTaskResult(check);
|
|
5388
|
+
}
|
|
5389
|
+
function skippedTaskRuntimeResult(options) {
|
|
5390
|
+
const reason = options.reason ?? (options.taskName ? `Task '${options.taskName}' was not registered` : "No tasks matched the change request event");
|
|
5391
|
+
const review = {
|
|
5392
|
+
summary: { body: reason },
|
|
5393
|
+
inlineFindings: []
|
|
5394
|
+
};
|
|
5395
|
+
const validated = {
|
|
5396
|
+
review,
|
|
5397
|
+
validFindings: [],
|
|
5398
|
+
droppedFindings: []
|
|
5399
|
+
};
|
|
5400
|
+
const publicationPlan = buildCommentPublishingPlan({
|
|
5999
5401
|
event: options.event,
|
|
6000
|
-
main:
|
|
6001
|
-
validated
|
|
6002
|
-
manifest: diffManifest,
|
|
6003
|
-
maxInlineComments: config.publication.maxInlineComments,
|
|
6004
|
-
maxStoredFindings: config.publication.maxStoredFindings,
|
|
6005
|
-
showHeader: config.publication.showHeader,
|
|
6006
|
-
showFooter: config.publication.showFooter,
|
|
6007
|
-
showStats: config.publication.showStats,
|
|
6008
|
-
priorReviewState: verifier.priorReviewState,
|
|
6009
|
-
threadActions: redactedPublication.threadActions,
|
|
5402
|
+
main: reason,
|
|
5403
|
+
validated,
|
|
5404
|
+
manifest: options.diffManifest,
|
|
5405
|
+
maxInlineComments: options.config.publication.maxInlineComments,
|
|
5406
|
+
maxStoredFindings: options.config.publication.maxStoredFindings,
|
|
5407
|
+
showHeader: options.config.publication.showHeader,
|
|
5408
|
+
showFooter: options.config.publication.showFooter,
|
|
5409
|
+
showStats: options.config.publication.showStats,
|
|
6010
5410
|
metadata: {
|
|
6011
5411
|
runtimeVersion,
|
|
6012
5412
|
configVersion: options.versionCompatibility?.configVersion,
|
|
6013
5413
|
trustedConfigSha: options.trustedConfigSha,
|
|
6014
5414
|
trustedConfigHash: options.trustedConfigHash,
|
|
6015
5415
|
reviewedHeadSha: options.event.change.head.sha,
|
|
6016
|
-
providerModels:
|
|
6017
|
-
selectedTasks,
|
|
5416
|
+
providerModels: [options.provider.model],
|
|
5417
|
+
selectedTasks: [],
|
|
6018
5418
|
failedTasks: [],
|
|
6019
|
-
validFindings:
|
|
6020
|
-
droppedFindings:
|
|
6021
|
-
|
|
5419
|
+
validFindings: 0,
|
|
5420
|
+
droppedFindings: 0
|
|
5421
|
+
}
|
|
5422
|
+
}).publicationPlan;
|
|
5423
|
+
return {
|
|
5424
|
+
kind: "skipped",
|
|
5425
|
+
skipReason: reason,
|
|
5426
|
+
provider: options.provider,
|
|
5427
|
+
diffManifest: options.diffManifest,
|
|
5428
|
+
review,
|
|
5429
|
+
validated,
|
|
5430
|
+
publicationPlan,
|
|
5431
|
+
mainComment: publicationPlan.mainComment,
|
|
5432
|
+
inlineCommentDrafts: [],
|
|
5433
|
+
taskChecks: [],
|
|
5434
|
+
repairAttempted: false
|
|
5435
|
+
};
|
|
5436
|
+
}
|
|
5437
|
+
//#endregion
|
|
5438
|
+
//#region src/shared/secret-redactor-core.ts
|
|
5439
|
+
const redactedSecret = "[redacted secret]";
|
|
5440
|
+
const minimumSecretLength = 4;
|
|
5441
|
+
function createSecretRedactor(initialSecrets) {
|
|
5442
|
+
const secrets = new Set(initialSecrets.filter((value) => value.length >= minimumSecretLength));
|
|
5443
|
+
return {
|
|
5444
|
+
addSecret(value) {
|
|
5445
|
+
if (value && value.length >= minimumSecretLength) secrets.add(value);
|
|
5446
|
+
},
|
|
5447
|
+
redact(value) {
|
|
5448
|
+
const ordered = [...secrets].sort((left, right) => right.length - left.length);
|
|
5449
|
+
let next = value;
|
|
5450
|
+
for (const secret of ordered) next = next.replaceAll(secret, redactedSecret);
|
|
5451
|
+
return {
|
|
5452
|
+
value: next,
|
|
5453
|
+
detected: next !== value
|
|
5454
|
+
};
|
|
5455
|
+
}
|
|
5456
|
+
};
|
|
5457
|
+
}
|
|
5458
|
+
//#endregion
|
|
5459
|
+
//#region src/shared/secret-redactor.ts
|
|
5460
|
+
function createKnownSecretRedactor(options) {
|
|
5461
|
+
return createSecretRedactor(sensitiveEnvironmentValues(options?.env ?? process.env));
|
|
5462
|
+
}
|
|
5463
|
+
//#endregion
|
|
5464
|
+
//#region src/hosts/http.ts
|
|
5465
|
+
var CodeHostHttpError = class extends Error {
|
|
5466
|
+
status;
|
|
5467
|
+
constructor(message, status) {
|
|
5468
|
+
super(message);
|
|
5469
|
+
this.name = "CodeHostHttpError";
|
|
5470
|
+
this.status = status;
|
|
5471
|
+
}
|
|
5472
|
+
};
|
|
5473
|
+
function createCodeHostHttpClient(options) {
|
|
5474
|
+
const fetchRequest = options.fetch ?? fetch;
|
|
5475
|
+
const sleep = options.sleep ?? ((milliseconds) => Bun.sleep(milliseconds));
|
|
5476
|
+
const successThrottle = options.successThrottle ?? createCodeHostSuccessThrottle(sleep);
|
|
5477
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
5478
|
+
const requestTimeoutMilliseconds = options.requestTimeoutMilliseconds ?? 3e4;
|
|
5479
|
+
const secrets = [...new Headers(options.headers).values()].flatMap((value) => [value, value.split(/\s+/).at(-1)].filter((candidate) => candidate !== void 0 && candidate.length >= 8));
|
|
5480
|
+
return { async json(path, schema, init = {}) {
|
|
5481
|
+
const method = init.method?.toUpperCase() ?? "GET";
|
|
5482
|
+
for (let attempt = 0;; attempt += 1) {
|
|
5483
|
+
await successThrottle.wait();
|
|
5484
|
+
const timeoutSignal = AbortSignal.timeout(requestTimeoutMilliseconds);
|
|
5485
|
+
const response = await fetchRequest(new URL(path, options.baseUrl), {
|
|
5486
|
+
...init,
|
|
5487
|
+
headers: {
|
|
5488
|
+
...Object.fromEntries(new Headers(options.headers)),
|
|
5489
|
+
...Object.fromEntries(new Headers(init.headers))
|
|
5490
|
+
},
|
|
5491
|
+
signal: init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal
|
|
5492
|
+
});
|
|
5493
|
+
const retryAfter = retryAfterMilliseconds(response.headers.get("Retry-After"));
|
|
5494
|
+
if (response.ok) {
|
|
5495
|
+
successThrottle.update(response.headers.get("Retry-After"));
|
|
5496
|
+
return schema.parse(await response.json());
|
|
5497
|
+
}
|
|
5498
|
+
if (shouldRetryCodeHostRequest({
|
|
5499
|
+
method,
|
|
5500
|
+
attempt,
|
|
5501
|
+
maxRetries,
|
|
5502
|
+
response,
|
|
5503
|
+
retryStatuses: options.retryNonIdempotentStatuses
|
|
5504
|
+
})) {
|
|
5505
|
+
await sleep(retryAfter || 250 * 2 ** attempt);
|
|
5506
|
+
continue;
|
|
5507
|
+
}
|
|
5508
|
+
const body = (await response.text()).slice(0, 1024);
|
|
5509
|
+
throw new CodeHostHttpError(redact(`Code host request failed (${response.status} ${response.statusText}): ${body}`, secrets), response.status);
|
|
6022
5510
|
}
|
|
6023
|
-
}
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
5511
|
+
} };
|
|
5512
|
+
}
|
|
5513
|
+
function createCodeHostSuccessThrottle(sleep = (milliseconds) => Bun.sleep(milliseconds)) {
|
|
5514
|
+
let notBefore = 0;
|
|
5515
|
+
let waitInFlight;
|
|
5516
|
+
const waitUntilReady = async () => {
|
|
5517
|
+
while (true) {
|
|
5518
|
+
const deadline = notBefore;
|
|
5519
|
+
const remaining = deadline - Date.now();
|
|
5520
|
+
if (remaining <= 0) {
|
|
5521
|
+
if (notBefore === deadline) notBefore = 0;
|
|
5522
|
+
return;
|
|
5523
|
+
}
|
|
5524
|
+
await sleep(remaining);
|
|
5525
|
+
if (notBefore <= deadline) {
|
|
5526
|
+
notBefore = 0;
|
|
5527
|
+
return;
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
};
|
|
6032
5531
|
return {
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
5532
|
+
wait() {
|
|
5533
|
+
if (waitInFlight) return waitInFlight;
|
|
5534
|
+
const currentWait = waitUntilReady().finally(() => {
|
|
5535
|
+
if (waitInFlight === currentWait) waitInFlight = void 0;
|
|
5536
|
+
});
|
|
5537
|
+
waitInFlight = currentWait;
|
|
5538
|
+
return waitInFlight;
|
|
5539
|
+
},
|
|
5540
|
+
update(retryAfter) {
|
|
5541
|
+
const delay = retryAfterMilliseconds(retryAfter);
|
|
5542
|
+
if (delay > 0) notBefore = Math.max(notBefore, Date.now() + delay);
|
|
5543
|
+
}
|
|
6043
5544
|
};
|
|
6044
5545
|
}
|
|
6045
|
-
function
|
|
6046
|
-
|
|
6047
|
-
body: "",
|
|
6048
|
-
taskChecks,
|
|
6049
|
-
redactor: options.secretRedactor
|
|
6050
|
-
});
|
|
6051
|
-
publishTaskChecks(options.checkSink, redacted.taskChecks);
|
|
5546
|
+
function shouldRetryCodeHostRequest(options) {
|
|
5547
|
+
return (options.method === "GET" || options.method === "HEAD" || options.retryStatuses?.includes(options.response.status) === true) && options.attempt < options.maxRetries && (options.response.status === 429 || options.response.status >= 500 || options.retryStatuses?.includes(options.response.status) === true);
|
|
6052
5548
|
}
|
|
6053
|
-
function
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
if (
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
commandResponse,
|
|
6060
|
-
commandInvocation: options.commandInvocation
|
|
6061
|
-
});
|
|
5549
|
+
function retryAfterMilliseconds(value) {
|
|
5550
|
+
if (!value) return 0;
|
|
5551
|
+
const seconds = Number(value);
|
|
5552
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
5553
|
+
const date = Date.parse(value);
|
|
5554
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : 0;
|
|
6062
5555
|
}
|
|
6063
|
-
function
|
|
6064
|
-
|
|
6065
|
-
|
|
5556
|
+
function redact(value, secrets) {
|
|
5557
|
+
let redacted = value;
|
|
5558
|
+
for (const secret of secrets) redacted = redacted.split(secret).join("***");
|
|
5559
|
+
return redacted;
|
|
6066
5560
|
}
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
5561
|
+
//#endregion
|
|
5562
|
+
//#region src/hosts/azure-devops/coordinates.ts
|
|
5563
|
+
function azureOrganizationFromUrl(value) {
|
|
5564
|
+
const url = new URL(value);
|
|
5565
|
+
return url.hostname === "dev.azure.com" ? url.pathname.split("/").filter(Boolean)[0] : url.hostname.split(".")[0];
|
|
5566
|
+
}
|
|
5567
|
+
//#endregion
|
|
5568
|
+
//#region src/hosts/azure-devops/client.ts
|
|
5569
|
+
const identitySchema = z.looseObject({
|
|
5570
|
+
displayName: z.string().optional(),
|
|
5571
|
+
providerDisplayName: z.string().optional(),
|
|
5572
|
+
uniqueName: z.string().optional(),
|
|
5573
|
+
id: z.string().optional(),
|
|
5574
|
+
properties: z.looseObject({ Account: z.looseObject({ $value: z.string().optional() }).optional() }).optional()
|
|
5575
|
+
}).transform((identity) => {
|
|
5576
|
+
const displayName = identity.displayName ?? identity.providerDisplayName;
|
|
5577
|
+
const uniqueName = identity.uniqueName ?? identity.properties?.Account?.$value;
|
|
5578
|
+
return {
|
|
5579
|
+
...identity,
|
|
5580
|
+
...displayName ? { displayName } : {},
|
|
5581
|
+
...uniqueName ? { uniqueName } : {}
|
|
6072
5582
|
};
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
5583
|
+
});
|
|
5584
|
+
const commitSchema = z.looseObject({ commitId: z.string().min(1) });
|
|
5585
|
+
const forkRepositorySchema = z.looseObject({ remoteUrl: z.string().min(1) });
|
|
5586
|
+
const pullRequestSchema$1 = z.looseObject({
|
|
5587
|
+
pullRequestId: z.number().int().positive(),
|
|
5588
|
+
isDraft: z.boolean().optional(),
|
|
5589
|
+
title: z.string(),
|
|
5590
|
+
description: z.string().nullish(),
|
|
5591
|
+
url: z.string().optional(),
|
|
5592
|
+
sourceRefName: z.string().min(1),
|
|
5593
|
+
targetRefName: z.string().min(1),
|
|
5594
|
+
createdBy: identitySchema.optional(),
|
|
5595
|
+
lastMergeSourceCommit: commitSchema,
|
|
5596
|
+
lastMergeTargetCommit: commitSchema,
|
|
5597
|
+
forkSource: z.looseObject({ repository: forkRepositorySchema }).optional(),
|
|
5598
|
+
repository: z.looseObject({
|
|
5599
|
+
id: z.string().min(1),
|
|
5600
|
+
name: z.string().min(1),
|
|
5601
|
+
url: z.string().optional(),
|
|
5602
|
+
project: z.looseObject({
|
|
5603
|
+
id: z.string().min(1),
|
|
5604
|
+
name: z.string().min(1)
|
|
5605
|
+
})
|
|
5606
|
+
})
|
|
5607
|
+
});
|
|
5608
|
+
const repositorySchema$1 = z.looseObject({
|
|
5609
|
+
id: z.string().min(1),
|
|
5610
|
+
name: z.string().min(1),
|
|
5611
|
+
project: z.looseObject({
|
|
5612
|
+
id: z.string().min(1),
|
|
5613
|
+
name: z.string().min(1)
|
|
5614
|
+
})
|
|
5615
|
+
});
|
|
5616
|
+
const iterationSchema = z.looseObject({
|
|
5617
|
+
id: z.number().int().positive(),
|
|
5618
|
+
sourceRefCommit: commitSchema
|
|
5619
|
+
});
|
|
5620
|
+
const iterationChangeSchema = z.looseObject({
|
|
5621
|
+
changeTrackingId: z.number().int(),
|
|
5622
|
+
changeType: z.string(),
|
|
5623
|
+
item: z.looseObject({
|
|
5624
|
+
path: z.string().min(1),
|
|
5625
|
+
originalPath: z.string().min(1).optional()
|
|
5626
|
+
})
|
|
5627
|
+
});
|
|
5628
|
+
const threadCommentSchema = z.looseObject({
|
|
5629
|
+
id: z.union([z.number(), z.string()]).transform(String),
|
|
5630
|
+
parentCommentId: z.number().int().optional(),
|
|
5631
|
+
content: z.string().default(""),
|
|
5632
|
+
author: identitySchema.optional(),
|
|
5633
|
+
isDeleted: z.boolean().optional()
|
|
5634
|
+
});
|
|
5635
|
+
const pointSchema = z.looseObject({
|
|
5636
|
+
line: z.number().int(),
|
|
5637
|
+
offset: z.number().int()
|
|
5638
|
+
});
|
|
5639
|
+
const threadSchema = z.looseObject({
|
|
5640
|
+
id: z.union([z.number(), z.string()]).transform(String),
|
|
5641
|
+
status: z.string().optional(),
|
|
5642
|
+
comments: z.array(threadCommentSchema).default([]),
|
|
5643
|
+
threadContext: z.looseObject({
|
|
5644
|
+
filePath: z.string().optional(),
|
|
5645
|
+
leftFileStart: pointSchema.nullish(),
|
|
5646
|
+
leftFileEnd: pointSchema.nullish(),
|
|
5647
|
+
rightFileStart: pointSchema.nullish(),
|
|
5648
|
+
rightFileEnd: pointSchema.nullish()
|
|
5649
|
+
}).nullish(),
|
|
5650
|
+
pullRequestThreadContext: z.looseObject({
|
|
5651
|
+
changeTrackingId: z.number().int().optional(),
|
|
5652
|
+
iterationContext: z.looseObject({
|
|
5653
|
+
firstComparingIteration: z.number().int(),
|
|
5654
|
+
secondComparingIteration: z.number().int()
|
|
5655
|
+
}).optional()
|
|
5656
|
+
}).nullish()
|
|
5657
|
+
});
|
|
5658
|
+
const statusSchema$1 = z.looseObject({ id: z.union([z.number(), z.string()]).transform(String) });
|
|
5659
|
+
const aclSchema = collectionSchema(z.looseObject({
|
|
5660
|
+
inheritPermissions: z.boolean().default(true),
|
|
5661
|
+
acesDictionary: z.record(z.string(), z.looseObject({
|
|
5662
|
+
allow: z.number().int().default(0),
|
|
5663
|
+
deny: z.number().int().default(0),
|
|
5664
|
+
extendedInfo: z.looseObject({
|
|
5665
|
+
effectiveAllow: z.number().int().default(0),
|
|
5666
|
+
effectiveDeny: z.number().int().default(0)
|
|
5667
|
+
}).optional()
|
|
5668
|
+
}))
|
|
5669
|
+
}));
|
|
5670
|
+
function createAzureDevOpsClient(env = process.env, fetch = globalThis.fetch, options = {}) {
|
|
5671
|
+
const organization = env.AZURE_DEVOPS_ORGANIZATION ?? organizationFromCollectionUri$1(env.SYSTEM_COLLECTIONURI);
|
|
5672
|
+
const project = env.AZURE_DEVOPS_PROJECT ?? env.SYSTEM_TEAMPROJECT;
|
|
5673
|
+
const pat = env.AZURE_DEVOPS_TOKEN;
|
|
5674
|
+
const bearerToken = env.AZURE_DEVOPS_BEARER_TOKEN ?? env.SYSTEM_ACCESSTOKEN;
|
|
5675
|
+
if (!organization) throw new Error("AZURE_DEVOPS_ORGANIZATION is required for Azure DevOps API calls");
|
|
5676
|
+
if (!project) throw new Error("AZURE_DEVOPS_PROJECT or SYSTEM_TEAMPROJECT is required for Azure DevOps API calls");
|
|
5677
|
+
if (!pat && !bearerToken) throw new Error("AZURE_DEVOPS_TOKEN, AZURE_DEVOPS_BEARER_TOKEN, or SYSTEM_ACCESSTOKEN is required for Azure DevOps API calls");
|
|
5678
|
+
const headers = pat ? { Authorization: `Basic ${Buffer.from(`:${pat}`).toString("base64")}` } : { Authorization: `Bearer ${bearerToken}` };
|
|
5679
|
+
const successThrottle = createCodeHostSuccessThrottle(options.sleep);
|
|
5680
|
+
const api = createCodeHostHttpClient({
|
|
5681
|
+
baseUrl: `https://dev.azure.com/${encodeURIComponent(organization)}/${encodeURIComponent(project)}/_apis/`,
|
|
5682
|
+
headers,
|
|
5683
|
+
fetch,
|
|
5684
|
+
successThrottle
|
|
6091
5685
|
});
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
5686
|
+
const organizationApi = createCodeHostHttpClient({
|
|
5687
|
+
baseUrl: `https://dev.azure.com/${encodeURIComponent(organization)}/_apis/`,
|
|
5688
|
+
headers,
|
|
5689
|
+
fetch,
|
|
5690
|
+
successThrottle
|
|
5691
|
+
});
|
|
5692
|
+
const identityApi = createCodeHostHttpClient({
|
|
5693
|
+
baseUrl: `https://vssps.dev.azure.com/${encodeURIComponent(organization)}/_apis/`,
|
|
5694
|
+
headers,
|
|
5695
|
+
fetch,
|
|
5696
|
+
successThrottle
|
|
5697
|
+
});
|
|
5698
|
+
const pullRequestPath = (repositoryId, changeNumber) => `git/repositories/${encodeURIComponent(repositoryId)}/pullRequests/${changeNumber}`;
|
|
5699
|
+
return {
|
|
5700
|
+
organization,
|
|
5701
|
+
project,
|
|
5702
|
+
async currentUser() {
|
|
5703
|
+
return (await organizationApi.json("connectionData?connectOptions=1&lastChangeId=-1&lastChangeId64=-1", z.looseObject({ authenticatedUser: identitySchema }))).authenticatedUser;
|
|
6102
5704
|
},
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
isFork: options.event.change.isFork,
|
|
6112
|
-
async diffManifest(manifestOptions) {
|
|
6113
|
-
const key = JSON.stringify(manifestOptions ?? {});
|
|
6114
|
-
const cached = options.manifestCache.get(key);
|
|
6115
|
-
if (cached) return cloneDiffManifest(cached);
|
|
6116
|
-
const manifest = projectDiffManifest(options.diffManifest, manifestOptions);
|
|
6117
|
-
options.manifestCache.set(key, manifest);
|
|
6118
|
-
return cloneDiffManifest(manifest);
|
|
6119
|
-
},
|
|
6120
|
-
async changedFiles() {
|
|
6121
|
-
return options.diffManifest.files.map((file) => ({
|
|
6122
|
-
path: file.path,
|
|
6123
|
-
previousPath: file.previousPath,
|
|
6124
|
-
status: file.status
|
|
6125
|
-
}));
|
|
6126
|
-
},
|
|
6127
|
-
async currentHeadSha() {
|
|
6128
|
-
return options.event.change.head.sha;
|
|
6129
|
-
}
|
|
5705
|
+
async getRepository(repository) {
|
|
5706
|
+
const value = await api.json(withApiVersion(`git/repositories/${encodeURIComponent(repository)}`), repositorySchema$1);
|
|
5707
|
+
return {
|
|
5708
|
+
id: value.id,
|
|
5709
|
+
name: value.name,
|
|
5710
|
+
projectId: value.project.id,
|
|
5711
|
+
project: value.project.name
|
|
5712
|
+
};
|
|
6130
5713
|
},
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
5714
|
+
async getRepositoryPermission(actor, projectId, repositoryId) {
|
|
5715
|
+
const candidates = (await identityApi.json(`identities?searchFilter=General&filterValue=${encodeURIComponent(actor)}&queryMembership=ExpandedUp&api-version=7.1`, collectionSchema(z.looseObject({
|
|
5716
|
+
descriptor: z.string().min(1),
|
|
5717
|
+
isActive: z.boolean().optional(),
|
|
5718
|
+
isContainer: z.boolean().optional(),
|
|
5719
|
+
memberOf: z.array(z.union([z.string().min(1), z.looseObject({ descriptor: z.string().min(1) })])).default([])
|
|
5720
|
+
})))).value.filter((identity) => identity.isActive !== false && identity.isContainer !== true);
|
|
5721
|
+
if (candidates.length !== 1) return "none";
|
|
5722
|
+
const identity = candidates[0];
|
|
5723
|
+
if (!identity) return "none";
|
|
5724
|
+
const descriptors = [identity.descriptor, ...identity.memberOf.map((group) => typeof group === "string" ? group : group.descriptor)];
|
|
5725
|
+
const loadAcls = (token) => {
|
|
5726
|
+
const query = new URLSearchParams({
|
|
5727
|
+
token,
|
|
5728
|
+
descriptors: descriptors.join(","),
|
|
5729
|
+
includeExtendedInfo: "true",
|
|
5730
|
+
recurse: "false",
|
|
5731
|
+
"api-version": "7.1"
|
|
5732
|
+
});
|
|
5733
|
+
return organizationApi.json(`accesscontrollists/2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87?${query}`, aclSchema);
|
|
5734
|
+
};
|
|
5735
|
+
const [projectAcls, repositoryAcls] = await Promise.all([loadAcls(`repoV2/${projectId}`), loadAcls(`repoV2/${projectId}/${repositoryId}`)]);
|
|
5736
|
+
return azurePermissionFromAcls(repositoryAcls.value.every((acl) => acl.inheritPermissions) ? [...projectAcls.value, ...repositoryAcls.value] : repositoryAcls.value);
|
|
5737
|
+
},
|
|
5738
|
+
getPullRequest: (repositoryId, changeNumber) => api.json(withApiVersion(pullRequestPath(repositoryId, changeNumber)), pullRequestSchema$1),
|
|
5739
|
+
async listIterations(repositoryId, changeNumber) {
|
|
5740
|
+
return (await api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/iterations`), collectionSchema(iterationSchema))).value.map((iteration) => ({
|
|
5741
|
+
id: iteration.id,
|
|
5742
|
+
headSha: iteration.sourceRefCommit.commitId
|
|
5743
|
+
}));
|
|
5744
|
+
},
|
|
5745
|
+
async loadChange(options) {
|
|
5746
|
+
if (options.organization !== organization || options.project !== project) throw new Error("Azure DevOps client coordinates do not match its configured organization and project");
|
|
5747
|
+
const pullRequest = await this.getPullRequest(options.repositoryId, options.changeNumber);
|
|
5748
|
+
const iterations = await this.listIterations(options.repositoryId, options.changeNumber);
|
|
5749
|
+
const headSha = pullRequest.lastMergeSourceCommit.commitId;
|
|
5750
|
+
const iteration = iterations.findLast((candidate) => candidate.headSha === headSha);
|
|
5751
|
+
if (!iteration) throw new Error(`Azure DevOps has no pull request iteration for head ${headSha}`);
|
|
5752
|
+
const sourceRef = branchName(pullRequest.sourceRefName);
|
|
5753
|
+
const targetRef = branchName(pullRequest.targetRefName);
|
|
5754
|
+
return {
|
|
5755
|
+
repository: {
|
|
5756
|
+
slug: `${organization}/${pullRequest.repository.project.name}/${pullRequest.repository.name}`,
|
|
5757
|
+
url: repositoryWebUrl(organization, pullRequest.repository.project.name, pullRequest.repository.name)
|
|
5758
|
+
},
|
|
5759
|
+
coordinates: {
|
|
5760
|
+
provider: "azure-devops",
|
|
5761
|
+
organization,
|
|
5762
|
+
project: pullRequest.repository.project.name,
|
|
5763
|
+
projectId: pullRequest.repository.project.id,
|
|
5764
|
+
repositoryId: pullRequest.repository.id
|
|
5765
|
+
},
|
|
5766
|
+
change: {
|
|
5767
|
+
number: pullRequest.pullRequestId,
|
|
5768
|
+
isDraft: pullRequest.isDraft,
|
|
5769
|
+
title: pullRequest.title,
|
|
5770
|
+
description: pullRequest.description ?? "",
|
|
5771
|
+
url: `${repositoryWebUrl(organization, pullRequest.repository.project.name, pullRequest.repository.name)}/pullrequest/${pullRequest.pullRequestId}`,
|
|
5772
|
+
author: pullRequest.createdBy?.uniqueName ? { login: pullRequest.createdBy.uniqueName } : void 0,
|
|
5773
|
+
base: {
|
|
5774
|
+
sha: pullRequest.lastMergeTargetCommit.commitId,
|
|
5775
|
+
ref: targetRef
|
|
5776
|
+
},
|
|
5777
|
+
head: {
|
|
5778
|
+
sha: headSha,
|
|
5779
|
+
ref: sourceRef,
|
|
5780
|
+
...pullRequest.forkSource?.repository.remoteUrl ? { url: pullRequest.forkSource.repository.remoteUrl } : {}
|
|
5781
|
+
},
|
|
5782
|
+
...pullRequest.forkSource ? { isFork: true } : {}
|
|
5783
|
+
},
|
|
5784
|
+
iterationId: iteration.id
|
|
5785
|
+
};
|
|
5786
|
+
},
|
|
5787
|
+
async listIterationChanges(repositoryId, changeNumber, iterationId) {
|
|
5788
|
+
const changes = [];
|
|
5789
|
+
let skip = 0;
|
|
5790
|
+
let top = 2e3;
|
|
5791
|
+
for (;;) {
|
|
5792
|
+
const path = `${pullRequestPath(repositoryId, changeNumber)}/iterations/${iterationId}/changes?compareTo=0&$skip=${skip}&$top=${top}`;
|
|
5793
|
+
const page = await api.json(withApiVersion(path), z.looseObject({
|
|
5794
|
+
changeEntries: z.array(iterationChangeSchema),
|
|
5795
|
+
nextSkip: z.number().int().nonnegative().optional(),
|
|
5796
|
+
nextTop: z.number().int().nonnegative().max(2e3).optional()
|
|
5797
|
+
}));
|
|
5798
|
+
changes.push(...page.changeEntries.map((change) => ({
|
|
5799
|
+
changeTrackingId: change.changeTrackingId,
|
|
5800
|
+
changeType: change.changeType,
|
|
5801
|
+
path: trimLeadingSlash(change.item.path),
|
|
5802
|
+
...change.item.originalPath ? { originalPath: trimLeadingSlash(change.item.originalPath) } : {}
|
|
5803
|
+
})));
|
|
5804
|
+
if (!page.nextSkip) return changes;
|
|
5805
|
+
skip = page.nextSkip;
|
|
5806
|
+
if (page.nextTop) top = page.nextTop;
|
|
6138
5807
|
}
|
|
6139
|
-
} : void 0,
|
|
6140
|
-
secret(secret) {
|
|
6141
|
-
return resolveTaskSecret(secret, options);
|
|
6142
5808
|
},
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
agent,
|
|
6146
|
-
input,
|
|
6147
|
-
runOptions,
|
|
6148
|
-
runtime: {
|
|
6149
|
-
...options,
|
|
6150
|
-
taskContext,
|
|
6151
|
-
runId: options.runId,
|
|
6152
|
-
piRunSink: options.piRunSink
|
|
6153
|
-
}
|
|
6154
|
-
});
|
|
6155
|
-
options.output.providerModels.push(...result.providerModels);
|
|
6156
|
-
if (result.repairAttempted) options.output.repairAttempted = true;
|
|
6157
|
-
trackResultFindingScope(options.output, result.value, runOptions?.paths);
|
|
6158
|
-
return agentOutputForTaskContext(agent, result.value);
|
|
6159
|
-
} },
|
|
6160
|
-
review: { async prior() {
|
|
6161
|
-
return priorReviewForTask(options.priorMainComment, options.priorReviewState);
|
|
6162
|
-
} },
|
|
6163
|
-
check: createCheckHandle(options.output),
|
|
6164
|
-
async comment(value) {
|
|
6165
|
-
collectComment(options.output, value, options.taskName);
|
|
5809
|
+
async listThreads(repositoryId, changeNumber) {
|
|
5810
|
+
return (await api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads`), collectionSchema(threadSchema))).value;
|
|
6166
5811
|
},
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
}
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
}
|
|
6174
|
-
function resolveTaskSecret(secret, options) {
|
|
6175
|
-
if (secret.kind !== "pipr.secret" || typeof secret.name !== "string") throw new Error("ctx.secret(...) requires a pipr.secret reference");
|
|
6176
|
-
const value = (options.env ?? process.env)[secret.name];
|
|
6177
|
-
if (!value) throw new Error(`Missing secret env var: ${secret.name}`);
|
|
6178
|
-
options.log?.addSecret(value);
|
|
6179
|
-
options.secretRedactor?.addSecret(value);
|
|
6180
|
-
return value;
|
|
6181
|
-
}
|
|
6182
|
-
function commandResponseRuntimeResult(options) {
|
|
6183
|
-
const redacted = redactCommandPublication({
|
|
6184
|
-
body: options.commandResponse.value,
|
|
6185
|
-
taskChecks: options.taskChecks,
|
|
6186
|
-
redactor: options.secretRedactor
|
|
6187
|
-
});
|
|
6188
|
-
return {
|
|
6189
|
-
kind: "command-response",
|
|
6190
|
-
provider: options.provider,
|
|
6191
|
-
diffManifest: options.diffManifest,
|
|
6192
|
-
taskChecks: redacted.taskChecks,
|
|
6193
|
-
repairAttempted: options.output.repairAttempted,
|
|
6194
|
-
commandResponse: {
|
|
6195
|
-
commandName: options.commandInvocation.name,
|
|
6196
|
-
line: options.commandInvocation.line,
|
|
6197
|
-
arguments: options.commandInvocation.arguments,
|
|
6198
|
-
body: redacted.body
|
|
6199
|
-
}
|
|
6200
|
-
};
|
|
6201
|
-
}
|
|
6202
|
-
function publishTaskChecks(sink, checks) {
|
|
6203
|
-
for (const check of checks) sink?.setTaskResult(check);
|
|
6204
|
-
}
|
|
6205
|
-
function skippedTaskRuntimeResult(options) {
|
|
6206
|
-
const reason = options.reason ?? (options.taskName ? `Task '${options.taskName}' was not registered` : "No tasks matched the change request event");
|
|
6207
|
-
const review = {
|
|
6208
|
-
summary: { body: reason },
|
|
6209
|
-
inlineFindings: []
|
|
6210
|
-
};
|
|
6211
|
-
const validated = {
|
|
6212
|
-
review,
|
|
6213
|
-
validFindings: [],
|
|
6214
|
-
droppedFindings: []
|
|
6215
|
-
};
|
|
6216
|
-
const publicationPlan = buildCommentPublishingPlan({
|
|
6217
|
-
event: options.event,
|
|
6218
|
-
main: reason,
|
|
6219
|
-
validated,
|
|
6220
|
-
manifest: options.diffManifest,
|
|
6221
|
-
maxInlineComments: options.config.publication.maxInlineComments,
|
|
6222
|
-
maxStoredFindings: options.config.publication.maxStoredFindings,
|
|
6223
|
-
showHeader: options.config.publication.showHeader,
|
|
6224
|
-
showFooter: options.config.publication.showFooter,
|
|
6225
|
-
showStats: options.config.publication.showStats,
|
|
6226
|
-
metadata: {
|
|
6227
|
-
runtimeVersion,
|
|
6228
|
-
configVersion: options.versionCompatibility?.configVersion,
|
|
6229
|
-
trustedConfigSha: options.trustedConfigSha,
|
|
6230
|
-
trustedConfigHash: options.trustedConfigHash,
|
|
6231
|
-
reviewedHeadSha: options.event.change.head.sha,
|
|
6232
|
-
providerModels: [options.provider.model],
|
|
6233
|
-
selectedTasks: [],
|
|
6234
|
-
failedTasks: [],
|
|
6235
|
-
validFindings: 0,
|
|
6236
|
-
droppedFindings: 0
|
|
6237
|
-
}
|
|
6238
|
-
}).publicationPlan;
|
|
6239
|
-
return {
|
|
6240
|
-
kind: "skipped",
|
|
6241
|
-
skipReason: reason,
|
|
6242
|
-
provider: options.provider,
|
|
6243
|
-
diffManifest: options.diffManifest,
|
|
6244
|
-
review,
|
|
6245
|
-
validated,
|
|
6246
|
-
publicationPlan,
|
|
6247
|
-
mainComment: publicationPlan.mainComment,
|
|
6248
|
-
inlineCommentDrafts: [],
|
|
6249
|
-
taskChecks: [],
|
|
6250
|
-
repairAttempted: false
|
|
5812
|
+
createThread: (repositoryId, changeNumber, body) => api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads`), threadSchema, jsonRequest$2("POST", body)),
|
|
5813
|
+
updateComment: (repositoryId, changeNumber, threadId, commentId, content) => api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads/${encodeURIComponent(threadId)}/comments/${encodeURIComponent(commentId)}`), threadCommentSchema, jsonRequest$2("PATCH", { content })),
|
|
5814
|
+
createThreadComment: (repositoryId, changeNumber, threadId, body) => api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads/${encodeURIComponent(threadId)}/comments`), threadCommentSchema, jsonRequest$2("POST", body)),
|
|
5815
|
+
updateThreadStatus: (repositoryId, changeNumber, threadId, status) => api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/threads/${encodeURIComponent(threadId)}`), threadSchema, jsonRequest$2("PATCH", { status })),
|
|
5816
|
+
async createStatus(repositoryId, changeNumber, body) {
|
|
5817
|
+
return (await api.json(withApiVersion(`${pullRequestPath(repositoryId, changeNumber)}/statuses`), statusSchema$1, jsonRequest$2("POST", body))).id;
|
|
5818
|
+
}
|
|
6251
5819
|
};
|
|
6252
5820
|
}
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
5821
|
+
function azureDevOpsStatusState(state) {
|
|
5822
|
+
switch (state) {
|
|
5823
|
+
case "pending": return "pending";
|
|
5824
|
+
case "success": return "succeeded";
|
|
5825
|
+
case "failure": return "failed";
|
|
5826
|
+
case "neutral": return "notApplicable";
|
|
5827
|
+
}
|
|
5828
|
+
}
|
|
5829
|
+
function azureRepositoryPermission(bits) {
|
|
5830
|
+
if ((bits & 2) === 0) return "none";
|
|
5831
|
+
if ((bits & 8192) !== 0) return "admin";
|
|
5832
|
+
if ((bits & 2048) !== 0) return "maintain";
|
|
5833
|
+
if ((bits & 4) !== 0) return "write";
|
|
5834
|
+
if ((bits & 16384) !== 0) return "triage";
|
|
5835
|
+
return "read";
|
|
5836
|
+
}
|
|
5837
|
+
function azurePermissionFromAcls(acls) {
|
|
5838
|
+
let allow = 0;
|
|
5839
|
+
let deny = 0;
|
|
5840
|
+
for (const acl of acls) for (const ace of Object.values(acl.acesDictionary)) {
|
|
5841
|
+
allow |= ace.extendedInfo?.effectiveAllow ?? ace.allow;
|
|
5842
|
+
deny |= ace.extendedInfo?.effectiveDeny ?? ace.deny;
|
|
5843
|
+
}
|
|
5844
|
+
return azureRepositoryPermission(allow & ~deny);
|
|
5845
|
+
}
|
|
5846
|
+
function collectionSchema(item) {
|
|
5847
|
+
return z.looseObject({
|
|
5848
|
+
count: z.number().int().nonnegative(),
|
|
5849
|
+
value: z.array(item)
|
|
5850
|
+
});
|
|
5851
|
+
}
|
|
5852
|
+
function withApiVersion(path) {
|
|
5853
|
+
return `${path}${path.includes("?") ? "&" : "?"}api-version=7.1`;
|
|
5854
|
+
}
|
|
5855
|
+
function jsonRequest$2(method, body) {
|
|
6259
5856
|
return {
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
redact(value) {
|
|
6264
|
-
const ordered = [...secrets].sort((left, right) => right.length - left.length);
|
|
6265
|
-
let next = value;
|
|
6266
|
-
for (const secret of ordered) next = next.replaceAll(secret, redactedSecret);
|
|
6267
|
-
return {
|
|
6268
|
-
value: next,
|
|
6269
|
-
detected: next !== value
|
|
6270
|
-
};
|
|
6271
|
-
}
|
|
5857
|
+
method,
|
|
5858
|
+
headers: { "Content-Type": "application/json" },
|
|
5859
|
+
body: JSON.stringify(body)
|
|
6272
5860
|
};
|
|
6273
5861
|
}
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
5862
|
+
function organizationFromCollectionUri$1(value) {
|
|
5863
|
+
return value ? azureOrganizationFromUrl(value) : void 0;
|
|
5864
|
+
}
|
|
5865
|
+
function branchName(ref) {
|
|
5866
|
+
return ref.replace(/^refs\/heads\//, "");
|
|
5867
|
+
}
|
|
5868
|
+
function repositoryWebUrl(organization, project, repository) {
|
|
5869
|
+
return `https://dev.azure.com/${encodeURIComponent(organization)}/${encodeURIComponent(project)}/_git/${encodeURIComponent(repository)}`;
|
|
5870
|
+
}
|
|
5871
|
+
function trimLeadingSlash(value) {
|
|
5872
|
+
return value.replace(/^\/+/, "");
|
|
6278
5873
|
}
|
|
6279
5874
|
//#endregion
|
|
6280
5875
|
//#region src/hosts/env.ts
|
|
@@ -7013,17 +6608,228 @@ function createAzureDevOpsHostAdapter(options = {}) {
|
|
|
7013
6608
|
}
|
|
7014
6609
|
};
|
|
7015
6610
|
}
|
|
7016
|
-
function azureCoordinates(change) {
|
|
7017
|
-
if (change.coordinates?.provider !== "azure-devops") throw new Error("Azure DevOps adapter requires Azure DevOps coordinates");
|
|
7018
|
-
return change.coordinates;
|
|
6611
|
+
function azureCoordinates(change) {
|
|
6612
|
+
if (change.coordinates?.provider !== "azure-devops") throw new Error("Azure DevOps adapter requires Azure DevOps coordinates");
|
|
6613
|
+
return change.coordinates;
|
|
6614
|
+
}
|
|
6615
|
+
function azureCoordinatesFromRepository(client, slug) {
|
|
6616
|
+
const repositoryId = slug.split("/").at(-1);
|
|
6617
|
+
if (!repositoryId) throw new Error("Azure DevOps repository slug must identify a repository");
|
|
6618
|
+
return {
|
|
6619
|
+
organization: client.organization,
|
|
6620
|
+
project: client.project,
|
|
6621
|
+
repositoryId
|
|
6622
|
+
};
|
|
6623
|
+
}
|
|
6624
|
+
//#endregion
|
|
6625
|
+
//#region src/hosts/bitbucket/schema.ts
|
|
6626
|
+
const bitbucketRepositorySchema = z.looseObject({
|
|
6627
|
+
uuid: z.string().min(1),
|
|
6628
|
+
name: z.string().min(1),
|
|
6629
|
+
full_name: z.string().min(1),
|
|
6630
|
+
slug: z.string().min(1).optional(),
|
|
6631
|
+
links: z.looseObject({ html: z.looseObject({ href: z.string().url() }) })
|
|
6632
|
+
}).transform((repository) => ({
|
|
6633
|
+
...repository,
|
|
6634
|
+
slug: repository.slug ?? repository.full_name.split("/").at(-1) ?? repository.full_name
|
|
6635
|
+
}));
|
|
6636
|
+
//#endregion
|
|
6637
|
+
//#region src/hosts/bitbucket/client.ts
|
|
6638
|
+
const userSchema$1 = z.looseObject({
|
|
6639
|
+
uuid: z.string().optional(),
|
|
6640
|
+
nickname: z.string().optional(),
|
|
6641
|
+
display_name: z.string().optional()
|
|
6642
|
+
});
|
|
6643
|
+
const endpointSchema = z.looseObject({
|
|
6644
|
+
branch: z.looseObject({ name: z.string().min(1) }),
|
|
6645
|
+
commit: z.looseObject({ hash: z.string().min(1) }),
|
|
6646
|
+
repository: bitbucketRepositorySchema
|
|
6647
|
+
});
|
|
6648
|
+
const pullRequestSchema = z.looseObject({
|
|
6649
|
+
id: z.number().int().positive(),
|
|
6650
|
+
draft: z.boolean().optional(),
|
|
6651
|
+
title: z.string(),
|
|
6652
|
+
description: z.string().default(""),
|
|
6653
|
+
author: userSchema$1.optional(),
|
|
6654
|
+
source: endpointSchema,
|
|
6655
|
+
destination: endpointSchema,
|
|
6656
|
+
links: z.looseObject({ html: z.looseObject({ href: z.string().url() }) })
|
|
6657
|
+
});
|
|
6658
|
+
const inlineSchema = z.looseObject({
|
|
6659
|
+
path: z.string().optional(),
|
|
6660
|
+
from: z.number().int().nullable().optional(),
|
|
6661
|
+
to: z.number().int().nullable().optional(),
|
|
6662
|
+
start_from: z.number().int().nullable().optional(),
|
|
6663
|
+
start_to: z.number().int().nullable().optional()
|
|
6664
|
+
});
|
|
6665
|
+
const commentSchema = z.looseObject({
|
|
6666
|
+
id: z.union([z.number(), z.string()]).transform(String),
|
|
6667
|
+
content: z.looseObject({ raw: z.string().default("") }),
|
|
6668
|
+
user: userSchema$1.optional(),
|
|
6669
|
+
parent: z.looseObject({ id: z.union([z.number(), z.string()]).transform(String) }).optional(),
|
|
6670
|
+
inline: inlineSchema.optional(),
|
|
6671
|
+
deleted: z.boolean().optional(),
|
|
6672
|
+
resolution: z.looseObject({}).optional()
|
|
6673
|
+
});
|
|
6674
|
+
function createBitbucketClient(env = process.env, fetch = globalThis.fetch) {
|
|
6675
|
+
const workspace = env.BITBUCKET_WORKSPACE;
|
|
6676
|
+
const repository = env.BITBUCKET_REPO_SLUG;
|
|
6677
|
+
const token = env.BITBUCKET_API_TOKEN;
|
|
6678
|
+
const email = env.BITBUCKET_EMAIL;
|
|
6679
|
+
if (!workspace) throw new Error("BITBUCKET_WORKSPACE is required for Bitbucket Cloud API calls");
|
|
6680
|
+
if (!repository) throw new Error("BITBUCKET_REPO_SLUG is required for Bitbucket Cloud API calls");
|
|
6681
|
+
if (!token || !email) throw new Error("BITBUCKET_EMAIL and BITBUCKET_API_TOKEN are required");
|
|
6682
|
+
const authorization = `Basic ${Buffer.from(`${email}:${token}`).toString("base64")}`;
|
|
6683
|
+
const repositoryApiPath = `/2.0/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repository)}/`;
|
|
6684
|
+
const api = createCodeHostHttpClient({
|
|
6685
|
+
baseUrl: `https://api.bitbucket.org${repositoryApiPath}`,
|
|
6686
|
+
headers: { Authorization: authorization },
|
|
6687
|
+
fetch
|
|
6688
|
+
});
|
|
6689
|
+
const rootApi = createCodeHostHttpClient({
|
|
6690
|
+
baseUrl: "https://api.bitbucket.org/2.0/",
|
|
6691
|
+
headers: { Authorization: authorization },
|
|
6692
|
+
fetch
|
|
6693
|
+
});
|
|
6694
|
+
const prPath = (id) => `pullrequests/${id}`;
|
|
6695
|
+
return {
|
|
6696
|
+
workspace,
|
|
6697
|
+
repository,
|
|
6698
|
+
async currentUser() {
|
|
6699
|
+
const value = await rootApi.json("user", userSchema$1);
|
|
6700
|
+
return {
|
|
6701
|
+
uuid: value.uuid,
|
|
6702
|
+
nickname: value.nickname,
|
|
6703
|
+
displayName: value.display_name
|
|
6704
|
+
};
|
|
6705
|
+
},
|
|
6706
|
+
async getRepository() {
|
|
6707
|
+
const value = await api.json("", bitbucketRepositorySchema);
|
|
6708
|
+
return {
|
|
6709
|
+
uuid: value.uuid,
|
|
6710
|
+
slug: value.slug,
|
|
6711
|
+
fullName: value.full_name,
|
|
6712
|
+
url: value.links.html.href
|
|
6713
|
+
};
|
|
6714
|
+
},
|
|
6715
|
+
async getRepositoryPermission(actor, repositoryUuid) {
|
|
6716
|
+
const permissionEmail = env.BITBUCKET_PERMISSION_EMAIL;
|
|
6717
|
+
const permissionToken = env.BITBUCKET_PERMISSION_API_TOKEN;
|
|
6718
|
+
if (!permissionEmail || !permissionToken) throw new Error("BITBUCKET_PERMISSION_EMAIL and BITBUCKET_PERMISSION_API_TOKEN are required for Bitbucket permission checks");
|
|
6719
|
+
const permissionApi = createCodeHostHttpClient({
|
|
6720
|
+
baseUrl: "https://api.bitbucket.org/2.0/",
|
|
6721
|
+
headers: { Authorization: `Basic ${Buffer.from(`${permissionEmail}:${permissionToken}`).toString("base64")}` },
|
|
6722
|
+
fetch
|
|
6723
|
+
});
|
|
6724
|
+
const query = encodeURIComponent(`repository.uuid="${escapeBitbucketQueryValue(repositoryUuid)}" AND user.nickname="${escapeBitbucketQueryValue(actor)}"`);
|
|
6725
|
+
return (await permissionApi.json(`workspaces/${encodeURIComponent(workspace)}/permissions/repositories?q=${query}&pagelen=100`, pagedSchema(z.looseObject({
|
|
6726
|
+
permission: z.enum([
|
|
6727
|
+
"read",
|
|
6728
|
+
"write",
|
|
6729
|
+
"admin"
|
|
6730
|
+
]),
|
|
6731
|
+
user: userSchema$1
|
|
6732
|
+
})))).values.find((entry) => entry.user.nickname === actor)?.permission ?? "none";
|
|
6733
|
+
},
|
|
6734
|
+
getPullRequest: (id) => api.json(prPath(id), pullRequestSchema),
|
|
6735
|
+
async loadChange(options) {
|
|
6736
|
+
if (options.workspace !== workspace || options.repository !== repository) throw new Error("Bitbucket client coordinates do not match the requested repository");
|
|
6737
|
+
const pullRequest = await this.getPullRequest(options.changeNumber);
|
|
6738
|
+
return {
|
|
6739
|
+
repository: {
|
|
6740
|
+
slug: pullRequest.destination.repository.full_name,
|
|
6741
|
+
url: pullRequest.destination.repository.links.html.href
|
|
6742
|
+
},
|
|
6743
|
+
coordinates: {
|
|
6744
|
+
provider: "bitbucket",
|
|
6745
|
+
workspace,
|
|
6746
|
+
repository,
|
|
6747
|
+
repositoryUuid: pullRequest.destination.repository.uuid
|
|
6748
|
+
},
|
|
6749
|
+
change: {
|
|
6750
|
+
number: pullRequest.id,
|
|
6751
|
+
isDraft: pullRequest.draft,
|
|
6752
|
+
title: pullRequest.title,
|
|
6753
|
+
description: pullRequest.description,
|
|
6754
|
+
url: pullRequest.links.html.href,
|
|
6755
|
+
author: pullRequest.author?.nickname ? { login: pullRequest.author.nickname } : void 0,
|
|
6756
|
+
base: {
|
|
6757
|
+
sha: pullRequest.destination.commit.hash,
|
|
6758
|
+
ref: pullRequest.destination.branch.name,
|
|
6759
|
+
url: pullRequest.destination.repository.links.html.href
|
|
6760
|
+
},
|
|
6761
|
+
head: {
|
|
6762
|
+
sha: pullRequest.source.commit.hash,
|
|
6763
|
+
ref: pullRequest.source.branch.name,
|
|
6764
|
+
url: pullRequest.source.repository.links.html.href
|
|
6765
|
+
},
|
|
6766
|
+
isFork: pullRequest.source.repository.uuid !== pullRequest.destination.repository.uuid
|
|
6767
|
+
}
|
|
6768
|
+
};
|
|
6769
|
+
},
|
|
6770
|
+
async listComments(id) {
|
|
6771
|
+
return (await listAll(api, `${prPath(id)}/comments`, commentSchema, repositoryApiPath)).filter((comment) => !comment.deleted);
|
|
6772
|
+
},
|
|
6773
|
+
createComment: (id, body) => api.json(`${prPath(id)}/comments`, commentSchema, jsonRequest$1("POST", body)),
|
|
6774
|
+
updateComment: (id, commentId, content) => api.json(`${prPath(id)}/comments/${encodeURIComponent(commentId)}`, commentSchema, jsonRequest$1("PUT", { content: { raw: content } })),
|
|
6775
|
+
async replyToComment(id, commentId, content) {
|
|
6776
|
+
const parentId = positiveCommentId(commentId);
|
|
6777
|
+
return await api.json(`${prPath(id)}/comments`, commentSchema, jsonRequest$1("POST", {
|
|
6778
|
+
content: { raw: content },
|
|
6779
|
+
parent: { id: parentId }
|
|
6780
|
+
}));
|
|
6781
|
+
},
|
|
6782
|
+
async resolveComment(id, commentId) {
|
|
6783
|
+
positiveCommentId(commentId);
|
|
6784
|
+
await api.json(`${prPath(id)}/comments/${encodeURIComponent(commentId)}/resolve`, z.unknown(), { method: "POST" });
|
|
6785
|
+
},
|
|
6786
|
+
async setStatus(sha, key, body) {
|
|
6787
|
+
return (await api.json(`commit/${encodeURIComponent(sha)}/statuses/build`, z.looseObject({ key: z.string().default(key) }), jsonRequest$1("POST", {
|
|
6788
|
+
...body,
|
|
6789
|
+
key
|
|
6790
|
+
}))).key;
|
|
6791
|
+
}
|
|
6792
|
+
};
|
|
6793
|
+
}
|
|
6794
|
+
function positiveCommentId(value) {
|
|
6795
|
+
const id = Number(value);
|
|
6796
|
+
if (!Number.isSafeInteger(id) || id <= 0) throw new Error("Bitbucket comment ID must be a positive integer");
|
|
6797
|
+
return id;
|
|
7019
6798
|
}
|
|
7020
|
-
function
|
|
7021
|
-
|
|
7022
|
-
if (
|
|
6799
|
+
function bitbucketStatusState(state) {
|
|
6800
|
+
if (state === "pending") return "INPROGRESS";
|
|
6801
|
+
if (state === "failure") return "FAILED";
|
|
6802
|
+
if (state === "neutral") return "STOPPED";
|
|
6803
|
+
return "SUCCESSFUL";
|
|
6804
|
+
}
|
|
6805
|
+
function escapeBitbucketQueryValue(value) {
|
|
6806
|
+
return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
|
|
6807
|
+
}
|
|
6808
|
+
function pagedSchema(item) {
|
|
6809
|
+
return z.looseObject({
|
|
6810
|
+
values: z.array(item),
|
|
6811
|
+
next: z.string().url().optional()
|
|
6812
|
+
});
|
|
6813
|
+
}
|
|
6814
|
+
async function listAll(api, path, schema, allowedPathPrefix) {
|
|
6815
|
+
const values = [];
|
|
6816
|
+
let next = path;
|
|
6817
|
+
while (next) {
|
|
6818
|
+
const page = await api.json(next, pagedSchema(schema));
|
|
6819
|
+
values.push(...page.values);
|
|
6820
|
+
if (page.next) {
|
|
6821
|
+
const url = new URL(page.next);
|
|
6822
|
+
if (url.origin !== "https://api.bitbucket.org" || !url.pathname.startsWith(allowedPathPrefix)) throw new Error("Bitbucket pagination URL must stay inside the configured repository API");
|
|
6823
|
+
}
|
|
6824
|
+
next = page.next;
|
|
6825
|
+
}
|
|
6826
|
+
return values;
|
|
6827
|
+
}
|
|
6828
|
+
function jsonRequest$1(method, body) {
|
|
7023
6829
|
return {
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
6830
|
+
method,
|
|
6831
|
+
headers: { "Content-Type": "application/json" },
|
|
6832
|
+
body: JSON.stringify(body)
|
|
7027
6833
|
};
|
|
7028
6834
|
}
|
|
7029
6835
|
//#endregion
|
|
@@ -8740,6 +8546,213 @@ function createGitHubHostAdapter(options = {}) {
|
|
|
8740
8546
|
};
|
|
8741
8547
|
}
|
|
8742
8548
|
//#endregion
|
|
8549
|
+
//#region src/hosts/gitlab/client.ts
|
|
8550
|
+
const userSchema = z.looseObject({
|
|
8551
|
+
id: z.number().int().positive(),
|
|
8552
|
+
username: z.string().min(1)
|
|
8553
|
+
});
|
|
8554
|
+
const noteSchema = z.looseObject({
|
|
8555
|
+
id: z.union([z.number(), z.string()]).transform(String),
|
|
8556
|
+
body: z.string(),
|
|
8557
|
+
author: userSchema.optional()
|
|
8558
|
+
});
|
|
8559
|
+
const positionSchema = z.looseObject({
|
|
8560
|
+
new_path: z.string().optional(),
|
|
8561
|
+
old_path: z.string().optional(),
|
|
8562
|
+
new_line: z.number().int().positive().nullish().transform((value) => value ?? void 0),
|
|
8563
|
+
old_line: z.number().int().positive().nullish().transform((value) => value ?? void 0),
|
|
8564
|
+
line_range: z.looseObject({
|
|
8565
|
+
start: z.looseObject({
|
|
8566
|
+
new_line: z.number().int().positive().nullish(),
|
|
8567
|
+
old_line: z.number().int().positive().nullish()
|
|
8568
|
+
}),
|
|
8569
|
+
end: z.looseObject({
|
|
8570
|
+
new_line: z.number().int().positive().nullish(),
|
|
8571
|
+
old_line: z.number().int().positive().nullish()
|
|
8572
|
+
})
|
|
8573
|
+
}).optional()
|
|
8574
|
+
});
|
|
8575
|
+
const discussionNoteSchema = noteSchema.extend({
|
|
8576
|
+
resolvable: z.boolean().optional(),
|
|
8577
|
+
resolved: z.boolean().optional(),
|
|
8578
|
+
position: positionSchema.nullable().optional()
|
|
8579
|
+
});
|
|
8580
|
+
const discussionSchema = z.looseObject({
|
|
8581
|
+
id: z.string().min(1),
|
|
8582
|
+
individual_note: z.boolean().optional(),
|
|
8583
|
+
notes: z.array(discussionNoteSchema)
|
|
8584
|
+
});
|
|
8585
|
+
const diffRefsSchema = z.looseObject({
|
|
8586
|
+
base_sha: z.string().min(1),
|
|
8587
|
+
start_sha: z.string().min(1),
|
|
8588
|
+
head_sha: z.string().min(1)
|
|
8589
|
+
});
|
|
8590
|
+
const mergeRequestResponseSchema = z.looseObject({
|
|
8591
|
+
iid: z.number().int().positive(),
|
|
8592
|
+
title: z.string().default(""),
|
|
8593
|
+
description: z.string().nullable().optional(),
|
|
8594
|
+
web_url: z.string().url().optional(),
|
|
8595
|
+
source_branch: z.string().min(1),
|
|
8596
|
+
target_branch: z.string().min(1),
|
|
8597
|
+
source_project_id: z.number().int().positive(),
|
|
8598
|
+
target_project_id: z.number().int().positive(),
|
|
8599
|
+
sha: z.string().min(1),
|
|
8600
|
+
draft: z.boolean().optional(),
|
|
8601
|
+
author: userSchema.optional(),
|
|
8602
|
+
references: z.looseObject({ full: z.string().optional() }).optional(),
|
|
8603
|
+
diff_refs: diffRefsSchema.nullable().optional()
|
|
8604
|
+
});
|
|
8605
|
+
const mergeRequestSchema = mergeRequestResponseSchema.extend({ diff_refs: diffRefsSchema });
|
|
8606
|
+
const memberSchema = z.looseObject({ access_level: z.number().int().min(0) });
|
|
8607
|
+
const statusSchema = z.looseObject({ id: z.union([z.number(), z.string()]).optional() });
|
|
8608
|
+
function createGitLabClient(env = process.env, fetch = globalThis.fetch, sleep = (milliseconds) => Bun.sleep(milliseconds)) {
|
|
8609
|
+
const token = env.GITLAB_TOKEN ?? env.CI_JOB_TOKEN;
|
|
8610
|
+
if (!token) throw new Error("GITLAB_TOKEN or CI_JOB_TOKEN is required for GitLab API calls");
|
|
8611
|
+
const headers = env.GITLAB_TOKEN ? { "PRIVATE-TOKEN": token } : { "JOB-TOKEN": token };
|
|
8612
|
+
const clientOptions = {
|
|
8613
|
+
baseUrl: `${(env.CI_API_V4_URL ?? "https://gitlab.com/api/v4").replace(/\/$/, "")}/`,
|
|
8614
|
+
headers,
|
|
8615
|
+
fetch,
|
|
8616
|
+
sleep: async (milliseconds) => void await sleep(milliseconds)
|
|
8617
|
+
};
|
|
8618
|
+
const api = createCodeHostHttpClient(clientOptions);
|
|
8619
|
+
const statusApi = createCodeHostHttpClient({
|
|
8620
|
+
...clientOptions,
|
|
8621
|
+
retryNonIdempotentStatuses: [409]
|
|
8622
|
+
});
|
|
8623
|
+
return {
|
|
8624
|
+
async getProject(project) {
|
|
8625
|
+
const value = await api.json(`projects/${encodeURIComponent(project)}`, z.looseObject({
|
|
8626
|
+
id: z.union([z.number(), z.string()]).transform(String),
|
|
8627
|
+
path_with_namespace: z.string().min(1)
|
|
8628
|
+
}));
|
|
8629
|
+
return {
|
|
8630
|
+
id: value.id,
|
|
8631
|
+
path: value.path_with_namespace
|
|
8632
|
+
};
|
|
8633
|
+
},
|
|
8634
|
+
currentUser: () => api.json("user", userSchema),
|
|
8635
|
+
async loadChange(options) {
|
|
8636
|
+
const mergeRequest = await this.getMergeRequest(options.projectId, options.changeNumber);
|
|
8637
|
+
return {
|
|
8638
|
+
repository: {
|
|
8639
|
+
slug: options.projectPath,
|
|
8640
|
+
url: projectWebUrl(mergeRequest.web_url)
|
|
8641
|
+
},
|
|
8642
|
+
coordinates: {
|
|
8643
|
+
provider: "gitlab",
|
|
8644
|
+
projectId: String(mergeRequest.target_project_id),
|
|
8645
|
+
projectPath: options.projectPath
|
|
8646
|
+
},
|
|
8647
|
+
change: {
|
|
8648
|
+
number: mergeRequest.iid,
|
|
8649
|
+
title: mergeRequest.title,
|
|
8650
|
+
description: mergeRequest.description ?? "",
|
|
8651
|
+
url: mergeRequest.web_url,
|
|
8652
|
+
author: mergeRequest.author ? { login: mergeRequest.author.username } : void 0,
|
|
8653
|
+
base: {
|
|
8654
|
+
sha: mergeRequest.diff_refs.start_sha,
|
|
8655
|
+
ref: mergeRequest.target_branch
|
|
8656
|
+
},
|
|
8657
|
+
head: {
|
|
8658
|
+
sha: mergeRequest.diff_refs.head_sha,
|
|
8659
|
+
ref: mergeRequest.source_branch
|
|
8660
|
+
},
|
|
8661
|
+
isFork: mergeRequest.source_project_id !== mergeRequest.target_project_id,
|
|
8662
|
+
isDraft: mergeRequest.draft
|
|
8663
|
+
}
|
|
8664
|
+
};
|
|
8665
|
+
},
|
|
8666
|
+
async getMergeRequest(projectId, changeNumber) {
|
|
8667
|
+
const requestPath = `projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}`;
|
|
8668
|
+
for (let attempt = 0;; attempt += 1) {
|
|
8669
|
+
const response = await api.json(requestPath, mergeRequestResponseSchema);
|
|
8670
|
+
const prepared = mergeRequestSchema.safeParse(response);
|
|
8671
|
+
if (prepared.success) return prepared.data;
|
|
8672
|
+
if (attempt >= 4) throw new Error("GitLab merge request diff refs were not prepared after 5 attempts");
|
|
8673
|
+
await sleep(250 * 2 ** attempt);
|
|
8674
|
+
}
|
|
8675
|
+
},
|
|
8676
|
+
async getRepositoryPermission(projectId, username) {
|
|
8677
|
+
const user = (await api.json(`users?username=${encodeURIComponent(username)}`, z.array(userSchema))).find((candidate) => candidate.username === username);
|
|
8678
|
+
if (!user) return "none";
|
|
8679
|
+
try {
|
|
8680
|
+
return accessLevelPermission((await api.json(`projects/${encodeURIComponent(projectId)}/members/all/${user.id}`, memberSchema)).access_level);
|
|
8681
|
+
} catch (error) {
|
|
8682
|
+
if (error instanceof CodeHostHttpError && error.status === 404) return "none";
|
|
8683
|
+
throw error;
|
|
8684
|
+
}
|
|
8685
|
+
},
|
|
8686
|
+
listNotes: (projectId, changeNumber) => paginated(api, `projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/notes`, noteSchema),
|
|
8687
|
+
createNote: (projectId, changeNumber, body) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/notes`, noteSchema, jsonRequest("POST", { body })),
|
|
8688
|
+
updateNote: (projectId, changeNumber, noteId, body) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/notes/${encodeURIComponent(noteId)}`, noteSchema, jsonRequest("PUT", { body })),
|
|
8689
|
+
listDiscussions: (projectId, changeNumber) => paginated(api, `projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions`, discussionSchema),
|
|
8690
|
+
getDiscussion: (projectId, changeNumber, discussionId) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions/${encodeURIComponent(discussionId)}`, discussionSchema),
|
|
8691
|
+
async findReplyParent(projectId, changeNumber, noteId, discussionId) {
|
|
8692
|
+
if (!discussionId) return void 0;
|
|
8693
|
+
const discussion = await this.getDiscussion(projectId, changeNumber, discussionId);
|
|
8694
|
+
if (!discussion.notes.some((note) => note.id === noteId) || discussion.notes[0]?.id === noteId) return;
|
|
8695
|
+
return discussion.notes[0]?.id;
|
|
8696
|
+
},
|
|
8697
|
+
createDiscussion: (projectId, changeNumber, body, position) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions`, discussionSchema, jsonRequest("POST", {
|
|
8698
|
+
body,
|
|
8699
|
+
position
|
|
8700
|
+
})),
|
|
8701
|
+
replyDiscussion: (projectId, changeNumber, discussionId, body) => api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions/${encodeURIComponent(discussionId)}/notes`, noteSchema, jsonRequest("POST", { body })),
|
|
8702
|
+
async resolveDiscussion(projectId, changeNumber, discussionId) {
|
|
8703
|
+
await api.json(`projects/${encodeURIComponent(projectId)}/merge_requests/${changeNumber}/discussions/${encodeURIComponent(discussionId)}`, discussionSchema, jsonRequest("PUT", { resolved: true }));
|
|
8704
|
+
},
|
|
8705
|
+
async setStatus(projectId, sha, name, state, description) {
|
|
8706
|
+
const status = await statusApi.json(`projects/${encodeURIComponent(projectId)}/statuses/${encodeURIComponent(sha)}`, statusSchema, jsonRequest("POST", {
|
|
8707
|
+
state: gitLabStatusState(state),
|
|
8708
|
+
name,
|
|
8709
|
+
description: boundedStatusDescription(description)
|
|
8710
|
+
}));
|
|
8711
|
+
return String(status.id ?? name);
|
|
8712
|
+
}
|
|
8713
|
+
};
|
|
8714
|
+
}
|
|
8715
|
+
const MAX_PAGINATION_PAGES = 100;
|
|
8716
|
+
async function paginated(client, path, schema) {
|
|
8717
|
+
const values = [];
|
|
8718
|
+
for (let page = 1; page <= MAX_PAGINATION_PAGES; page += 1) {
|
|
8719
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
8720
|
+
const batch = await client.json(`${path}${separator}per_page=100&page=${page}`, z.array(schema));
|
|
8721
|
+
values.push(...batch);
|
|
8722
|
+
if (batch.length < 100) return values;
|
|
8723
|
+
}
|
|
8724
|
+
throw new Error(`GitLab pagination exceeded ${MAX_PAGINATION_PAGES} pages for ${path}`);
|
|
8725
|
+
}
|
|
8726
|
+
function jsonRequest(method, body) {
|
|
8727
|
+
return {
|
|
8728
|
+
method,
|
|
8729
|
+
headers: { "Content-Type": "application/json" },
|
|
8730
|
+
body: JSON.stringify(body)
|
|
8731
|
+
};
|
|
8732
|
+
}
|
|
8733
|
+
function accessLevelPermission(level) {
|
|
8734
|
+
if (level >= 50) return "admin";
|
|
8735
|
+
if (level >= 40) return "maintain";
|
|
8736
|
+
if (level >= 30) return "write";
|
|
8737
|
+
if (level >= 15) return "triage";
|
|
8738
|
+
if (level >= 10) return "read";
|
|
8739
|
+
return "none";
|
|
8740
|
+
}
|
|
8741
|
+
function boundedStatusDescription(description) {
|
|
8742
|
+
return description && description.length > 255 ? `${description.slice(0, 252).trimEnd()}...` : description;
|
|
8743
|
+
}
|
|
8744
|
+
function gitLabStatusState(state) {
|
|
8745
|
+
switch (state) {
|
|
8746
|
+
case "pending": return "pending";
|
|
8747
|
+
case "success": return "success";
|
|
8748
|
+
case "failure": return "failed";
|
|
8749
|
+
case "neutral": return "skipped";
|
|
8750
|
+
}
|
|
8751
|
+
}
|
|
8752
|
+
function projectWebUrl(mergeRequestUrl) {
|
|
8753
|
+
return mergeRequestUrl?.replace(/\/-\/merge_requests\/\d+$/, "");
|
|
8754
|
+
}
|
|
8755
|
+
//#endregion
|
|
8743
8756
|
//#region src/hosts/gitlab/event.ts
|
|
8744
8757
|
const projectSchema = z.looseObject({
|
|
8745
8758
|
id: z.union([z.number(), z.string()]),
|
|
@@ -10252,6 +10265,6 @@ async function runHostRunCommandWithDependencies(options) {
|
|
|
10252
10265
|
});
|
|
10253
10266
|
}
|
|
10254
10267
|
//#endregion
|
|
10255
|
-
export { piReadOnlyToolNames as _, runInspectCommand as a,
|
|
10268
|
+
export { piReadOnlyToolNames as _, runInspectCommand as a, createGitLabClient as c, PublicationError as d, createAzureDevOpsClient as f, piBuiltinToolNames as g, supportedOfficialInitAdapters as h, runInitCommand as i, createGitHubHostAdapter as l, createKnownSecretRedactor as m, runHostRunCommand as n, runLocalReviewCommand as o, azureOrganizationFromUrl as p, runHostRunCommandWithDependencies as r, runValidateCommand as s, runDryRunCommand as t, createBitbucketClient as u, piRequiredCliFlags as v, piThinkingLevels as y };
|
|
10256
10269
|
|
|
10257
|
-
//# sourceMappingURL=commands-
|
|
10270
|
+
//# sourceMappingURL=commands-B9qNW4pk.mjs.map
|