@codacy/analysis-runner 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +438 -17
  2. package/dist/index.js +1393 -219
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -30,15 +30,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ DEFAULT_IMAGE_SCAN_PKG_TYPES: () => DEFAULT_IMAGE_SCAN_PKG_TYPES,
34
+ DEFAULT_IMAGE_SCAN_SEVERITIES: () => DEFAULT_IMAGE_SCAN_SEVERITIES,
33
35
  analyze: () => analyze,
34
36
  applyCodacyYamlExcludes: () => applyCodacyYamlExcludes,
35
37
  applyConfigOperation: () => applyConfigOperation,
38
+ buildCodacyrc: () => buildCodacyrc,
39
+ buildPatternTranslator: () => buildPatternTranslator,
36
40
  buildPayloads: () => buildPayloads,
37
41
  buildToolConfig: () => buildToolConfig,
38
42
  clearAdapters: () => clearAdapters,
39
43
  computePatternSummary: () => computePatternSummary,
40
44
  configureApiToken: () => configureApiToken,
41
- configureProxy: () => import_tooling8.configureProxy,
45
+ configureProxy: () => import_tooling9.configureProxy,
42
46
  createCodacyConfig: () => createCodacyConfig,
43
47
  createLogger: () => createLogger,
44
48
  createNullLogger: () => createNullLogger,
@@ -65,29 +69,39 @@ __export(index_exports, {
65
69
  loadCredentials: () => loadCredentials,
66
70
  mergeConfigs: () => mergeConfigs,
67
71
  parseAutoFilters: () => parseAutoFilters,
72
+ parseCodacyrc: () => parseCodacyrc,
68
73
  parseReport: () => parseReport,
74
+ partitionContainerErrors: () => partitionContainerErrors,
69
75
  pruneEmptyTools: () => pruneEmptyTools,
70
76
  readBaselineConfig: () => readBaselineConfig,
71
77
  readCodacyConfig: () => readCodacyConfig,
72
78
  readCodacyYaml: () => readCodacyYaml,
79
+ readCodacyrc: () => readCodacyrc,
73
80
  registerAdapter: () => registerAdapter,
74
81
  registerLazyAdapter: () => registerLazyAdapter,
75
82
  registerUnsupportedTool: () => registerUnsupportedTool,
83
+ registryLookup: () => registryLookup,
76
84
  relativizeLocalConfigPaths: () => relativizeLocalConfigPaths,
77
85
  resolveApiToken: () => resolveApiToken,
78
86
  resolveBaselinePath: () => resolveBaselinePath,
87
+ resolveCodacyrc: () => resolveCodacyrc,
79
88
  resolveConfigPath: () => resolveConfigPath,
80
89
  resolveDiscoveryExcludes: () => resolveDiscoveryExcludes,
81
90
  resolveEngineToAdapter: () => resolveEngineToAdapter,
82
91
  resolveHeadCommit: () => resolveHeadCommit,
83
92
  resolveLocalConfigPaths: () => resolveLocalConfigPaths,
84
- resolveProxyConfig: () => import_tooling8.resolveProxyConfig,
93
+ resolveProxyConfig: () => import_tooling9.resolveProxyConfig,
85
94
  resolveRemoteAuth: () => resolveRemoteAuth,
86
95
  resolveRepositoryRoot: () => resolveRepositoryRoot,
87
96
  resolveResultsApiBaseUrl: () => resolveResultsApiBaseUrl,
97
+ resolveToolName: () => resolveToolName,
88
98
  rotateLogFiles: () => rotateLogFiles,
89
99
  saveCredentials: () => saveCredentials,
100
+ scanContainerImage: () => scanContainerImage,
101
+ serializeCodacyrc: () => serializeCodacyrc,
102
+ toEnginePatternId: () => toEnginePatternId,
90
103
  updateConfigIncremental: () => updateConfigIncremental,
104
+ uploadImageSbom: () => uploadImageSbom2,
91
105
  uploadReport: () => uploadReport,
92
106
  validateApiToken: () => validateApiToken,
93
107
  writeBaselineConfig: () => writeBaselineConfig,
@@ -98,7 +112,7 @@ module.exports = __toCommonJS(index_exports);
98
112
 
99
113
  // src/analyze.ts
100
114
  var import_os2 = __toESM(require("os"));
101
- var import_path4 = __toESM(require("path"));
115
+ var import_path5 = __toESM(require("path"));
102
116
  var import_fs3 = __toESM(require("fs"));
103
117
  var import_child_process3 = require("child_process");
104
118
  var import_util = require("util");
@@ -348,7 +362,7 @@ function createCodacyConfig(params = {}) {
348
362
  tools: params.tools ?? [],
349
363
  exclude: []
350
364
  };
351
- if (params.includeFiles && params.includeFiles.length > 0) {
365
+ if (params.includeFiles !== void 0) {
352
366
  config.includeFiles = params.includeFiles;
353
367
  }
354
368
  return config;
@@ -602,6 +616,11 @@ function listRepoFiles(repositoryRoot, exclude) {
602
616
  encoding: "utf-8",
603
617
  maxBuffer: 50 * 1024 * 1024
604
618
  }).trim();
619
+ const deleted = (0, import_child_process2.execSync)("git ls-files --deleted", {
620
+ cwd: repositoryRoot,
621
+ encoding: "utf-8",
622
+ maxBuffer: 50 * 1024 * 1024
623
+ }).trim();
605
624
  const files = /* @__PURE__ */ new Set();
606
625
  for (const f of tracked.split("\n")) {
607
626
  if (f) files.add(f);
@@ -609,10 +628,15 @@ function listRepoFiles(repositoryRoot, exclude) {
609
628
  for (const f of untracked.split("\n")) {
610
629
  if (f) files.add(f);
611
630
  }
631
+ for (const f of deleted.split("\n")) {
632
+ if (f) files.delete(f);
633
+ }
612
634
  const all = Array.from(files).sort();
613
635
  return exclude && exclude.length > 0 ? applyExclusions(all, [...exclude], []) : all;
614
- } catch {
615
- return [];
636
+ } catch (err) {
637
+ throw new Error(
638
+ `Could not list repository files in ${repositoryRoot}: ${err instanceof Error ? err.message : String(err)}`
639
+ );
616
640
  }
617
641
  }
618
642
  function filterOversizedFiles(files, repositoryRoot, logger) {
@@ -652,6 +676,22 @@ function filterOversizedFiles(files, repositoryRoot, logger) {
652
676
  }
653
677
  return { files: result, skipped };
654
678
  }
679
+ function filterSkippedFilesToTools(skipped, tools, globalExclude = []) {
680
+ if (skipped.length === 0 || tools.length === 0) return [];
681
+ const afterGlobal = applyExclusions(
682
+ skipped.map((s) => s.filePath),
683
+ [...globalExclude],
684
+ []
685
+ );
686
+ if (afterGlobal.length === 0) return [];
687
+ const routable = /* @__PURE__ */ new Set();
688
+ for (const { adapter, toolConfig } of tools) {
689
+ const toolExclude = toolConfig.exclude ?? [];
690
+ const visible = toolExclude.length > 0 ? applyExclusions(afterGlobal, [], [...toolExclude]) : afterGlobal;
691
+ for (const file of routeFilesToTool(visible, adapter, toolConfig)) routable.add(file);
692
+ }
693
+ return skipped.filter((s) => routable.has(s.filePath));
694
+ }
655
695
  function applyExclusions(files, globalExclude, toolExclude) {
656
696
  if (globalExclude.length === 0 && toolExclude.length === 0) return files;
657
697
  const matcher = new CompiledMatcher([...globalExclude, ...toolExclude]);
@@ -683,6 +723,211 @@ function filterByTargetFiles(allFiles, targetPatterns) {
683
723
  );
684
724
  }
685
725
 
726
+ // src/codacyrc.ts
727
+ var import_promises2 = __toESM(require("fs/promises"));
728
+
729
+ // src/pattern-id.ts
730
+ function toEnginePatternId(patternId, toolId) {
731
+ const prefix = `${toolId}_`;
732
+ return patternId.startsWith(prefix) ? patternId.slice(prefix.length) : patternId;
733
+ }
734
+ function buildPatternTranslator(toolId, patternsMetadata) {
735
+ const prefix = `${toolId}_`;
736
+ const byEngineId = /* @__PURE__ */ new Map();
737
+ let prefixedCount = 0;
738
+ let total = 0;
739
+ for (const entry of patternsMetadata ?? []) {
740
+ if (typeof entry?.id !== "string" || entry.id === "") continue;
741
+ total++;
742
+ byEngineId.set(entry.id, entry.id);
743
+ const unprefixed = toEnginePatternId(entry.id, toolId);
744
+ if (unprefixed !== entry.id) {
745
+ byEngineId.set(unprefixed, entry.id);
746
+ prefixedCount++;
747
+ }
748
+ }
749
+ const hasCatalogue = total > 0;
750
+ const toolIsPrefixed = hasCatalogue ? prefixedCount === total : true;
751
+ return {
752
+ toolId,
753
+ hasCatalogue,
754
+ isKnown: (enginePatternId) => !hasCatalogue || byEngineId.has(enginePatternId),
755
+ toInternal(enginePatternId) {
756
+ const known = byEngineId.get(enginePatternId);
757
+ if (known) return known;
758
+ if (!toolIsPrefixed) return enginePatternId;
759
+ return enginePatternId.startsWith(prefix) ? enginePatternId : prefix + enginePatternId;
760
+ }
761
+ };
762
+ }
763
+
764
+ // src/codacyrc.ts
765
+ function registryLookup() {
766
+ return { descriptors: getRegisteredDescriptors(), load: getAdapter };
767
+ }
768
+ function parseCodacyrc(raw, label = ".codacyrc") {
769
+ let parsed;
770
+ try {
771
+ parsed = JSON.parse(raw);
772
+ } catch {
773
+ try {
774
+ parsed = JSON.parse(Buffer.from(raw.trim(), "base64").toString("utf-8"));
775
+ } catch {
776
+ throw new Error(`Failed to parse ${label}: not valid JSON or base64-encoded JSON`);
777
+ }
778
+ }
779
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
780
+ throw new Error(`Failed to parse ${label}: expected a JSON object`);
781
+ }
782
+ return parsed;
783
+ }
784
+ async function readCodacyrc(filePath) {
785
+ let raw;
786
+ try {
787
+ raw = await import_promises2.default.readFile(filePath, "utf-8");
788
+ } catch (err) {
789
+ if (err?.code === "ENOENT") return null;
790
+ throw err;
791
+ }
792
+ return parseCodacyrc(raw, filePath);
793
+ }
794
+ function resolveToolName(name, descriptors) {
795
+ const runnable = descriptors.filter((d) => !d.unsupported);
796
+ const lower = name.toLowerCase();
797
+ return runnable.find((d) => d.shortName === name) ?? runnable.find((d) => d.shortName?.toLowerCase() === lower) ?? runnable.find((d) => d.id === name) ?? runnable.find((d) => d.id.toLowerCase() === lower);
798
+ }
799
+ function toParameterRecord(parameters) {
800
+ const out = {};
801
+ if (!Array.isArray(parameters)) return out;
802
+ for (const parameter of parameters) {
803
+ if (!parameter || typeof parameter.name !== "string" || parameter.name === "") continue;
804
+ const { value } = parameter;
805
+ if (value === null || value === void 0) continue;
806
+ out[parameter.name] = typeof value === "object" ? JSON.stringify(value) : String(value);
807
+ }
808
+ return out;
809
+ }
810
+ function withSpecDefaults(entry, configured) {
811
+ const defaults = {};
812
+ for (const spec of entry?.parameters ?? []) {
813
+ if (typeof spec?.name !== "string" || spec.name === "") continue;
814
+ if (spec.default === void 0 || spec.default === null) continue;
815
+ defaults[spec.name] = String(spec.default);
816
+ }
817
+ return { ...defaults, ...configured };
818
+ }
819
+ async function resolveCodacyrc(codacyrc, opts = {}) {
820
+ const lookup = opts.lookup ?? registryLookup();
821
+ const logger = opts.logger;
822
+ const allowed = opts.toolIds && opts.toolIds.length > 0 ? new Set(opts.toolIds.map((id) => id.toLowerCase())) : void 0;
823
+ const descriptors = allowed ? lookup.descriptors.filter((d) => allowed.has(d.id.toLowerCase())) : lookup.descriptors;
824
+ const tools = [];
825
+ const matchedToolIds = [];
826
+ const unmatchedToolNames = [];
827
+ const unknownPatterns = [];
828
+ for (const entry of codacyrc?.tools ?? []) {
829
+ if (typeof entry !== "object" || entry === null || typeof entry.name !== "string") continue;
830
+ const descriptor = resolveToolName(entry.name, descriptors);
831
+ if (!descriptor) {
832
+ unmatchedToolNames.push(entry.name);
833
+ continue;
834
+ }
835
+ if (matchedToolIds.includes(descriptor.id)) continue;
836
+ const adapter = await lookup.load(descriptor.id);
837
+ const translator = buildPatternTranslator(descriptor.id, adapter?.patternsMetadata);
838
+ const byId = new Map((adapter?.patternsMetadata ?? []).map((p) => [p.id, p]));
839
+ const patterns = [];
840
+ for (const raw of entry.patterns ?? []) {
841
+ if (typeof raw !== "object" || raw === null || typeof raw.patternId !== "string") continue;
842
+ const translated = translator.toInternal(raw.patternId);
843
+ if (!translator.isKnown(raw.patternId)) {
844
+ unknownPatterns.push({ toolId: descriptor.id, patternId: raw.patternId });
845
+ }
846
+ const parameters = withSpecDefaults(byId.get(translated), toParameterRecord(raw.parameters));
847
+ patterns.push({
848
+ patternId: translated,
849
+ ...Object.keys(parameters).length > 0 ? { parameters } : {}
850
+ });
851
+ }
852
+ tools.push({ toolId: descriptor.id, patterns });
853
+ matchedToolIds.push(descriptor.id);
854
+ }
855
+ if (unmatchedToolNames.length > 0) {
856
+ logger?.warn(
857
+ `.codacyrc names ${unmatchedToolNames.length} tool(s) with no local analyzer here: ${unmatchedToolNames.join(", ")}`
858
+ );
859
+ }
860
+ for (const unknown of unknownPatterns) {
861
+ logger?.warn(
862
+ `.codacyrc enables "${unknown.patternId}", which ${unknown.toolId} does not ship; passing it through`
863
+ );
864
+ }
865
+ const files = Array.isArray(codacyrc?.files) ? codacyrc.files.filter((f) => typeof f === "string") : void 0;
866
+ const config = createCodacyConfig({ source: "container", tools, includeFiles: files });
867
+ return {
868
+ config,
869
+ matchedToolIds,
870
+ unmatchedToolNames,
871
+ unknownPatterns,
872
+ options: codacyrc?.options ?? {}
873
+ };
874
+ }
875
+ function toWireValue(raw, spec) {
876
+ const declared = spec?.default;
877
+ if (typeof declared === "number") {
878
+ const parsed = Number(raw);
879
+ return raw.trim() !== "" && Number.isFinite(parsed) ? parsed : raw;
880
+ }
881
+ if (typeof declared === "boolean") {
882
+ if (raw === "true") return true;
883
+ if (raw === "false") return false;
884
+ return raw;
885
+ }
886
+ if (declared !== null && typeof declared === "object") {
887
+ try {
888
+ return JSON.parse(raw);
889
+ } catch {
890
+ return raw;
891
+ }
892
+ }
893
+ return raw;
894
+ }
895
+ function toParameterList(entry, configured) {
896
+ const merged = withSpecDefaults(entry, configured ?? {});
897
+ const names = Object.keys(merged).sort();
898
+ if (names.length === 0) return void 0;
899
+ const specByName = /* @__PURE__ */ new Map();
900
+ for (const spec of entry?.parameters ?? []) {
901
+ if (typeof spec?.name === "string" && spec.name !== "") specByName.set(spec.name, spec);
902
+ }
903
+ return names.map((name) => ({ name, value: toWireValue(merged[name], specByName.get(name)) }));
904
+ }
905
+ function buildCodacyrc(tools, opts = {}) {
906
+ const files = /* @__PURE__ */ new Set();
907
+ const wireTools = [];
908
+ for (const { adapter, toolConfig, files: routed } of tools) {
909
+ for (const file of routed) files.add(file);
910
+ if (!adapter.shortName) {
911
+ opts.logger?.warn(
912
+ `${adapter.id} declares no Codacy short-name; emitting "${adapter.id}" as tools[].name, which a Codacy engine image will not recognize`
913
+ );
914
+ }
915
+ const byId = new Map((adapter.patternsMetadata ?? []).map((p) => [p.id, p]));
916
+ const patterns = toolConfig.useLocalConfigurationFile ? [] : (toolConfig.patterns ?? []).map((pattern) => {
917
+ const parameters = toParameterList(byId.get(pattern.patternId), pattern.parameters);
918
+ return {
919
+ patternId: toEnginePatternId(pattern.patternId, adapter.id),
920
+ ...parameters ? { parameters } : {}
921
+ };
922
+ });
923
+ wireTools.push({ name: adapter.shortName ?? adapter.id, patterns });
924
+ }
925
+ return { files: [...files].sort(), tools: wireTools };
926
+ }
927
+ function serializeCodacyrc(codacyrc) {
928
+ return JSON.stringify(codacyrc, null, 2) + "\n";
929
+ }
930
+
686
931
  // src/executor.ts
687
932
  var import_tooling2 = require("@codacy/tooling");
688
933
  var HARD_TIMEOUT_GRACE_MS = 2e3;
@@ -1081,6 +1326,81 @@ function computePatternSummary(issues, lookup) {
1081
1326
  };
1082
1327
  }
1083
1328
 
1329
+ // src/tool-install.ts
1330
+ var import_path4 = __toESM(require("path"));
1331
+ function makeToolContext(base, adapter, targetFiles, generatedRoot) {
1332
+ return {
1333
+ ...base,
1334
+ targetFiles,
1335
+ toolInstallDir: import_path4.default.join(base.globalDir, "tools", adapter.id),
1336
+ generatedConfigDir: import_path4.default.join(
1337
+ generatedRoot ?? import_path4.default.join(base.repositoryRoot, ".codacy", "generated"),
1338
+ adapter.id
1339
+ )
1340
+ };
1341
+ }
1342
+ async function installMissingTool(adapter, ctx, logger) {
1343
+ logger.info(`${adapter.displayName} is missing, attempting install...`);
1344
+ try {
1345
+ logger.info(`Downloading ${adapter.displayName}...`);
1346
+ const installResult = await adapter.install(ctx);
1347
+ if (installResult.success) {
1348
+ logger.info(`Checking ${adapter.displayName} installation...`);
1349
+ const recheck = await adapter.checkAvailability(ctx);
1350
+ if (recheck.available) {
1351
+ logger.info(`${adapter.displayName} is now available`);
1352
+ return {
1353
+ kind: "installed",
1354
+ version: recheck.version ?? installResult.installedVersion ?? adapter.preferredVersion,
1355
+ installation: recheck.installation ?? "codacy"
1356
+ };
1357
+ }
1358
+ logger.error(
1359
+ `${adapter.displayName} post-install check failed: ${recheck.reason ?? "unknown"}`
1360
+ );
1361
+ return {
1362
+ kind: "failed",
1363
+ error: {
1364
+ toolId: adapter.id,
1365
+ phase: "toolInstall",
1366
+ kind: "PostInstallCheckFailed",
1367
+ message: `Installed ${adapter.id} but availability check still fails: ${recheck.reason ?? "unknown"}`,
1368
+ level: "error"
1369
+ }
1370
+ };
1371
+ }
1372
+ if (installResult.error?.kind === "NotSupported") {
1373
+ logger.info(`${adapter.displayName} does not support automated install, skipping.`);
1374
+ return { kind: "notSupported" };
1375
+ }
1376
+ const errMsg = installResult.error?.message ?? `Failed to install ${adapter.id}`;
1377
+ logger.error(`${adapter.displayName} install failed: ${errMsg}`);
1378
+ return {
1379
+ kind: "failed",
1380
+ error: installResult.error ?? {
1381
+ toolId: adapter.id,
1382
+ phase: "toolInstall",
1383
+ kind: "InstallFailed",
1384
+ message: errMsg,
1385
+ level: "error"
1386
+ }
1387
+ };
1388
+ } catch (err) {
1389
+ const errMsg = err instanceof Error ? err.message : String(err);
1390
+ logger.error(`${adapter.displayName} install failed: ${errMsg}`);
1391
+ return {
1392
+ kind: "failed",
1393
+ error: {
1394
+ toolId: adapter.id,
1395
+ phase: "toolInstall",
1396
+ kind: "InstallError",
1397
+ message: `Install failed for ${adapter.id}: ${errMsg}`,
1398
+ level: "error"
1399
+ }
1400
+ };
1401
+ }
1402
+ }
1403
+
1084
1404
  // src/analyze.ts
1085
1405
  var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
1086
1406
  var CHECK_CONCURRENCY = 12;
@@ -1111,7 +1431,7 @@ async function analyze(options) {
1111
1431
  toolExecutionMs: 0,
1112
1432
  installMs: 0
1113
1433
  };
1114
- const emptyCapability = {
1434
+ const emptyCapability2 = {
1115
1435
  ready: [],
1116
1436
  unavailable: [],
1117
1437
  checkedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -1122,7 +1442,7 @@ async function analyze(options) {
1122
1442
  startedAt,
1123
1443
  options.repositoryRoot,
1124
1444
  mode,
1125
- emptyCapability,
1445
+ emptyCapability2,
1126
1446
  [],
1127
1447
  [
1128
1448
  {
@@ -1142,7 +1462,7 @@ async function analyze(options) {
1142
1462
  startedAt,
1143
1463
  options.repositoryRoot,
1144
1464
  mode,
1145
- emptyCapability,
1465
+ emptyCapability2,
1146
1466
  [],
1147
1467
  [
1148
1468
  {
@@ -1156,34 +1476,42 @@ async function analyze(options) {
1156
1476
  []
1157
1477
  );
1158
1478
  }
1479
+ let codacyConfig;
1480
+ let configError;
1159
1481
  try {
1160
- await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
1161
- cwd: options.repositoryRoot
1162
- });
1482
+ codacyConfig = options.config ?? await readCodacyConfig(options.repositoryRoot, options.configFile);
1163
1483
  } catch (err) {
1164
- const isGitMissing = err instanceof Error && "code" in err && err.code === "ENOENT";
1165
- const message = isGitMissing ? "git is not installed. Codacy analysis requires git for file discovery." : `${options.repositoryRoot} is not a git repository. Codacy analysis requires a git repository for file discovery. Run \`git init\` to initialize one.`;
1166
- const kind = isGitMissing ? "GitNotInstalled" : "NotAGitRepository";
1167
- return buildResult(
1168
- startedAt,
1169
- options.repositoryRoot,
1170
- mode,
1171
- emptyCapability,
1172
- [],
1173
- [{ toolId: "runner", phase: "requirementCheck", kind, message, level: "error" }],
1174
- []
1175
- );
1484
+ codacyConfig = null;
1485
+ configError = err;
1486
+ }
1487
+ if (configError === void 0 && codacyConfig?.includeFiles === void 0) {
1488
+ try {
1489
+ await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
1490
+ cwd: options.repositoryRoot
1491
+ });
1492
+ } catch (err) {
1493
+ const isGitMissing = err instanceof Error && "code" in err && err.code === "ENOENT";
1494
+ const message = isGitMissing ? "git is not installed. Codacy analysis requires git for file discovery." : `${options.repositoryRoot} is not a git repository. Codacy analysis requires a git repository for file discovery. Run \`git init\` to initialize one.`;
1495
+ const kind = isGitMissing ? "GitNotInstalled" : "NotAGitRepository";
1496
+ return buildResult(
1497
+ startedAt,
1498
+ options.repositoryRoot,
1499
+ mode,
1500
+ emptyCapability2,
1501
+ [],
1502
+ [{ toolId: "runner", phase: "requirementCheck", kind, message, level: "error" }],
1503
+ []
1504
+ );
1505
+ }
1176
1506
  }
1177
1507
  const configLabel = options.configFile ?? ".codacy/codacy.config.json";
1178
- let codacyConfig;
1179
- try {
1180
- codacyConfig = await readCodacyConfig(options.repositoryRoot, options.configFile);
1181
- } catch (err) {
1508
+ if (configError !== void 0) {
1509
+ const err = configError;
1182
1510
  return buildResult(
1183
1511
  startedAt,
1184
1512
  options.repositoryRoot,
1185
1513
  mode,
1186
- emptyCapability,
1514
+ emptyCapability2,
1187
1515
  [],
1188
1516
  [
1189
1517
  {
@@ -1202,7 +1530,7 @@ async function analyze(options) {
1202
1530
  startedAt,
1203
1531
  options.repositoryRoot,
1204
1532
  mode,
1205
- emptyCapability,
1533
+ emptyCapability2,
1206
1534
  [],
1207
1535
  [
1208
1536
  {
@@ -1219,7 +1547,7 @@ async function analyze(options) {
1219
1547
  const requestedTools = options.tools ?? codacyConfig.tools.map((t) => t.toolId);
1220
1548
  const adapters = await getRegisteredAdapters(requestedTools);
1221
1549
  logger.info("Pipeline started", { mode, toolCount: adapters.length, parallelTools, toolTimeout });
1222
- const globalDir = import_path4.default.join(import_os2.default.homedir(), ".codacy");
1550
+ const globalDir = import_path5.default.join(import_os2.default.homedir(), ".codacy");
1223
1551
  const runnerEnv = options.runner ?? "local";
1224
1552
  const baseCtx = {
1225
1553
  repositoryRoot: options.repositoryRoot,
@@ -1266,7 +1594,7 @@ async function analyze(options) {
1266
1594
  displayName: adapter.displayName,
1267
1595
  version: availability.version ?? adapter.preferredVersion,
1268
1596
  installation: availability.installation ?? "global",
1269
- runner: "local"
1597
+ runner: runnerEnv
1270
1598
  }
1271
1599
  };
1272
1600
  }
@@ -1366,6 +1694,25 @@ async function analyze(options) {
1366
1694
  logger.info(`Updated ${updatedCount} managed tool(s) to their pinned versions`);
1367
1695
  }
1368
1696
  }
1697
+ if (mode === "auto-install" && runnerEnv === "container") {
1698
+ return buildResult(
1699
+ startedAt,
1700
+ options.repositoryRoot,
1701
+ mode,
1702
+ capability,
1703
+ [],
1704
+ [
1705
+ {
1706
+ toolId: "runner",
1707
+ phase: "requirementCheck",
1708
+ kind: "InstallNotAllowed",
1709
+ message: "Installs are disabled under the container runner; the image must ship its tools.",
1710
+ level: "error"
1711
+ }
1712
+ ],
1713
+ []
1714
+ );
1715
+ }
1369
1716
  if (mode === "auto-install" && capability.unavailable.length > 0) {
1370
1717
  const installStartMs = Date.now();
1371
1718
  const installErrors = [];
@@ -1373,62 +1720,24 @@ async function analyze(options) {
1373
1720
  const status = capability.unavailable[i];
1374
1721
  const adapter = adapters.find((a) => a.id === status.toolId);
1375
1722
  if (!adapter) continue;
1376
- const ctx = makeToolContext(baseCtx, adapter, []);
1377
- logger.info(`${adapter.displayName} is missing, attempting install...`);
1378
- try {
1379
- logger.info(`Downloading ${adapter.displayName}...`);
1380
- const installResult = await adapter.install(ctx);
1381
- if (installResult.success) {
1382
- logger.info(`Checking ${adapter.displayName} installation...`);
1383
- const recheck = await adapter.checkAvailability(ctx);
1384
- if (recheck.available) {
1385
- logger.info(`${adapter.displayName} is now available`);
1386
- capability.ready.push({
1387
- toolId: adapter.id,
1388
- displayName: adapter.displayName,
1389
- version: recheck.version ?? installResult.installedVersion ?? adapter.preferredVersion,
1390
- installation: recheck.installation ?? "codacy",
1391
- runner: "local"
1392
- });
1393
- capability.unavailable.splice(i, 1);
1394
- } else {
1395
- logger.error(
1396
- `${adapter.displayName} post-install check failed: ${recheck.reason ?? "unknown"}`
1397
- );
1398
- installErrors.push({
1399
- toolId: adapter.id,
1400
- phase: "toolInstall",
1401
- kind: "PostInstallCheckFailed",
1402
- message: `Installed ${adapter.id} but availability check still fails: ${recheck.reason ?? "unknown"}`,
1403
- level: "error"
1404
- });
1405
- }
1406
- } else if (installResult.error?.kind === "NotSupported") {
1407
- logger.info(`${adapter.displayName} does not support automated install, skipping.`);
1408
- capability.unavailable.splice(i, 1);
1409
- } else {
1410
- const errMsg = installResult.error?.message ?? `Failed to install ${adapter.id}`;
1411
- logger.error(`${adapter.displayName} install failed: ${errMsg}`);
1412
- installErrors.push(
1413
- installResult.error ?? {
1414
- toolId: adapter.id,
1415
- phase: "toolInstall",
1416
- kind: "InstallFailed",
1417
- message: errMsg,
1418
- level: "error"
1419
- }
1420
- );
1421
- }
1422
- } catch (err) {
1423
- const errMsg = err instanceof Error ? err.message : String(err);
1424
- logger.error(`${adapter.displayName} install failed: ${errMsg}`);
1425
- installErrors.push({
1723
+ const outcome = await installMissingTool(
1724
+ adapter,
1725
+ makeToolContext(baseCtx, adapter, []),
1726
+ logger
1727
+ );
1728
+ if (outcome.kind === "installed") {
1729
+ capability.ready.push({
1426
1730
  toolId: adapter.id,
1427
- phase: "toolInstall",
1428
- kind: "InstallError",
1429
- message: `Install failed for ${adapter.id}: ${errMsg}`,
1430
- level: "error"
1731
+ displayName: adapter.displayName,
1732
+ version: outcome.version,
1733
+ installation: outcome.installation,
1734
+ runner: runnerEnv
1431
1735
  });
1736
+ capability.unavailable.splice(i, 1);
1737
+ } else if (outcome.kind === "notSupported") {
1738
+ capability.unavailable.splice(i, 1);
1739
+ } else {
1740
+ installErrors.push(outcome.error);
1432
1741
  }
1433
1742
  }
1434
1743
  phaseTimings.installMs = Date.now() - installStartMs;
@@ -1497,8 +1806,59 @@ async function analyze(options) {
1497
1806
  }
1498
1807
  const fileDiscoveryStartMs = Date.now();
1499
1808
  const globalExclude = codacyConfig.exclude || [];
1500
- let allFiles = listRepoFiles(options.repositoryRoot);
1501
- logger.debug("File discovery", { totalFiles: allFiles.length });
1809
+ let allFiles;
1810
+ const runnerErrors = [];
1811
+ if (codacyConfig.includeFiles === void 0) {
1812
+ try {
1813
+ allFiles = listRepoFiles(options.repositoryRoot);
1814
+ } catch (err) {
1815
+ return buildResult(
1816
+ startedAt,
1817
+ options.repositoryRoot,
1818
+ mode,
1819
+ capability,
1820
+ [],
1821
+ [
1822
+ {
1823
+ toolId: "runner",
1824
+ phase: "requirementCheck",
1825
+ kind: "FileDiscoveryFailed",
1826
+ message: err instanceof Error ? err.message : String(err),
1827
+ level: "error"
1828
+ }
1829
+ ],
1830
+ []
1831
+ );
1832
+ }
1833
+ logger.debug("File discovery", { source: "git", totalFiles: allFiles.length });
1834
+ } else {
1835
+ const seen = /* @__PURE__ */ new Set();
1836
+ allFiles = [];
1837
+ for (const raw of codacyConfig.includeFiles) {
1838
+ const rel = raw.replace(/\\/g, "/").replace(/^\.\//, "");
1839
+ if (rel === "" || seen.has(rel)) continue;
1840
+ seen.add(rel);
1841
+ const absolute = import_path5.default.resolve(options.repositoryRoot, rel);
1842
+ const relativeToRoot = import_path5.default.relative(options.repositoryRoot, absolute);
1843
+ const escapesRoot = relativeToRoot === ".." || relativeToRoot.startsWith(`..${import_path5.default.sep}`) || import_path5.default.isAbsolute(relativeToRoot);
1844
+ const stat = escapesRoot ? void 0 : import_fs3.default.statSync(absolute, { throwIfNoEntry: false });
1845
+ if (!stat?.isFile()) {
1846
+ runnerErrors.push({
1847
+ toolId: "runner",
1848
+ phase: "requirementCheck",
1849
+ kind: "FileNotFound",
1850
+ message: escapesRoot ? `${rel} was requested for analysis but is outside the repository` : `${rel} was requested for analysis but does not exist`,
1851
+ level: "warning",
1852
+ filePath: rel,
1853
+ fileNotAnalyzed: true
1854
+ });
1855
+ continue;
1856
+ }
1857
+ allFiles.push(rel);
1858
+ }
1859
+ allFiles.sort();
1860
+ logger.debug("File discovery", { source: "config", totalFiles: allFiles.length });
1861
+ }
1502
1862
  if (options.gitScope) {
1503
1863
  const scopeFiles = resolveGitScope(options.repositoryRoot, options.gitScope);
1504
1864
  logger.debug("Git scope resolved", {
@@ -1507,7 +1867,7 @@ async function analyze(options) {
1507
1867
  });
1508
1868
  if (scopeFiles.length === 0) {
1509
1869
  logger.info("Git scope resolved to zero files, skipping analysis");
1510
- return buildResult(startedAt, options.repositoryRoot, mode, capability, [], [], []);
1870
+ return buildResult(startedAt, options.repositoryRoot, mode, capability, [], runnerErrors, []);
1511
1871
  }
1512
1872
  const scopeSet = new Set(scopeFiles);
1513
1873
  allFiles = allFiles.filter((f) => scopeSet.has(f));
@@ -1524,6 +1884,8 @@ async function analyze(options) {
1524
1884
  const fileRoutingStartMs = Date.now();
1525
1885
  const readyById = new Map(capability.ready.map((r) => [r.toolId, r]));
1526
1886
  const readyAdapters = adapters.filter((a) => readyById.has(a.id));
1887
+ const generatedRoot = resolveGeneratedRoot(options.repositoryRoot, runnerEnv, logger);
1888
+ const generatedRootIsTemporary = runnerEnv === "container";
1527
1889
  const filesAfterGlobalExclude = applyExclusions(allFiles, globalExclude, []);
1528
1890
  const toolRuns = [];
1529
1891
  const candidates = [];
@@ -1540,7 +1902,7 @@ async function analyze(options) {
1540
1902
  candidates.push({
1541
1903
  adapter,
1542
1904
  toolFiles,
1543
- configCtx: makeToolContext(baseCtx, adapter, toolFiles),
1905
+ configCtx: makeToolContext(baseCtx, adapter, toolFiles, generatedRoot),
1544
1906
  readyEntry
1545
1907
  });
1546
1908
  }
@@ -1588,6 +1950,32 @@ async function analyze(options) {
1588
1950
  toolRuns.push({ adapter, ctx: configCtx, codacyToolConfig });
1589
1951
  }
1590
1952
  phaseTimings.fileRoutingMs = Date.now() - fileRoutingStartMs;
1953
+ const analyzedSkippedFiles = filterSkippedFilesToTools(
1954
+ skippedFiles,
1955
+ toolRuns.map(({ adapter, codacyToolConfig }) => ({
1956
+ adapter,
1957
+ toolConfig: codacyToolConfig
1958
+ })),
1959
+ globalExclude
1960
+ );
1961
+ if (options.onCodacyrc && toolRuns.length > 0) {
1962
+ if (toolRuns.length > 1) {
1963
+ logger.info("Emitting .codacyrc for multiple tools", {
1964
+ toolCount: toolRuns.length,
1965
+ hint: "`files` is the union of their routed lists; use --tool to pin one"
1966
+ });
1967
+ }
1968
+ await options.onCodacyrc(
1969
+ buildCodacyrc(
1970
+ toolRuns.map(({ adapter, ctx, codacyToolConfig }) => ({
1971
+ adapter,
1972
+ toolConfig: codacyToolConfig,
1973
+ files: ctx.targetFiles
1974
+ })),
1975
+ { logger }
1976
+ )
1977
+ );
1978
+ }
1591
1979
  const toolExecStartMs = Date.now();
1592
1980
  logger.info("Starting tool execution", {
1593
1981
  toolCount: toolRuns.length,
@@ -1613,7 +2001,17 @@ async function analyze(options) {
1613
2001
  }
1614
2002
  phaseTimings.toolExecutionMs = Date.now() - toolExecStartMs;
1615
2003
  const allIssues = normalizeIssues(results.flatMap((r) => r.issues));
1616
- const allErrors = results.flatMap((r) => r.errors);
2004
+ if (generatedRootIsTemporary) {
2005
+ try {
2006
+ import_fs3.default.rmSync(generatedRoot, { recursive: true, force: true });
2007
+ } catch (err) {
2008
+ logger.debug("Could not remove the temporary generated-config directory", {
2009
+ path: generatedRoot,
2010
+ error: err instanceof Error ? err.message : String(err)
2011
+ });
2012
+ }
2013
+ }
2014
+ const allErrors = [...runnerErrors, ...results.flatMap((r) => r.errors)];
1617
2015
  const allSummaries = results.map((r) => r.summary);
1618
2016
  const patternLookup = /* @__PURE__ */ new Map();
1619
2017
  for (const { adapter } of toolRuns) {
@@ -1643,16 +2041,14 @@ async function analyze(options) {
1643
2041
  allSummaries,
1644
2042
  patternSummary,
1645
2043
  phaseTimings,
1646
- skippedFiles
2044
+ analyzedSkippedFiles
1647
2045
  );
1648
2046
  }
1649
- function makeToolContext(base, adapter, targetFiles) {
1650
- return {
1651
- ...base,
1652
- targetFiles,
1653
- toolInstallDir: import_path4.default.join(base.globalDir, "tools", adapter.id),
1654
- generatedConfigDir: import_path4.default.join(base.repositoryRoot, ".codacy", "generated", adapter.id)
1655
- };
2047
+ function resolveGeneratedRoot(repositoryRoot, runnerEnv, logger) {
2048
+ if (runnerEnv !== "container") return import_path5.default.join(repositoryRoot, ".codacy", "generated");
2049
+ const dir = import_fs3.default.mkdtempSync(import_path5.default.join(import_os2.default.tmpdir(), "codacy-generated-"));
2050
+ logger.info("Generated tool configs will be written outside the source directory", { path: dir });
2051
+ return dir;
1656
2052
  }
1657
2053
  async function updateStaleManagedTools(capability, adapters, baseCtx, logger) {
1658
2054
  const adaptersById = new Map(adapters.map((a) => [a.id, a]));
@@ -1758,6 +2154,8 @@ async function repairMissingManagedTools(capability, adapters, baseCtx, logger)
1758
2154
  displayName: adapter.displayName,
1759
2155
  version: recheck.version ?? installResult.installedVersion ?? adapter.preferredVersion,
1760
2156
  installation: recheck.installation ?? "codacy",
2157
+ // Always local: this repair pass is skipped entirely under the container
2158
+ // runner, which never installs anything.
1761
2159
  runner: "local"
1762
2160
  });
1763
2161
  capability.unavailable.splice(i, 1);
@@ -1808,7 +2206,7 @@ function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors
1808
2206
  }
1809
2207
 
1810
2208
  // src/format.ts
1811
- var import_path5 = __toESM(require("path"));
2209
+ var import_path6 = __toESM(require("path"));
1812
2210
  var import_tooling4 = require("@codacy/tooling");
1813
2211
  var import_ansis = __toESM(require("ansis"));
1814
2212
  var import_cli_table3 = __toESM(require("cli-table3"));
@@ -1924,17 +2322,48 @@ function mapSeverityToSarif(severity) {
1924
2322
  return "warning";
1925
2323
  }
1926
2324
  }
2325
+ function partitionContainerErrors(errors) {
2326
+ const fileErrors = [];
2327
+ const otherErrors = [];
2328
+ for (const error of errors) {
2329
+ if (error.fileNotAnalyzed && error.filePath) fileErrors.push(error);
2330
+ else otherErrors.push(error);
2331
+ }
2332
+ return { fileErrors, otherErrors };
2333
+ }
1927
2334
  function formatContainer(result) {
1928
- return result.issues.map(
1929
- (issue) => JSON.stringify({
2335
+ const lines = [];
2336
+ const fileErrors = /* @__PURE__ */ new Map();
2337
+ for (const error of partitionContainerErrors(result.errors).fileErrors) {
2338
+ const filename = error.filePath;
2339
+ if (fileErrors.has(filename)) continue;
2340
+ fileErrors.set(filename, flattenErrorMessage(error.message) || void 0);
2341
+ }
2342
+ for (const skipped of result.skippedFiles ?? []) {
2343
+ if (fileErrors.has(skipped.filePath)) continue;
2344
+ fileErrors.set(
2345
+ skipped.filePath,
2346
+ `File skipped: ${skipped.sizeBytes} bytes exceeds the ${MAX_FILE_SIZE_BYTES}-byte limit`
2347
+ );
2348
+ }
2349
+ for (const issue of result.issues) {
2350
+ if (issue.line <= 0) {
2351
+ if (!fileErrors.has(issue.filePath)) {
2352
+ fileErrors.set(issue.filePath, import_tooling4.LINE_NUMBERS_NOT_SUPPORTED);
2353
+ }
2354
+ continue;
2355
+ }
2356
+ lines.push({
1930
2357
  filename: issue.filePath,
1931
- patternId: issue.patternId,
2358
+ patternId: toEnginePatternId(issue.patternId, issue.toolId),
1932
2359
  line: issue.line,
1933
2360
  message: issue.message,
1934
2361
  suggestion: issue.suggestion,
1935
2362
  sourceId: issue.sourceId
1936
- })
1937
- ).join("\n");
2363
+ });
2364
+ }
2365
+ for (const [filename, message] of fileErrors) lines.push({ filename, message });
2366
+ return lines.map((line) => JSON.stringify(line)).join("\n");
1938
2367
  }
1939
2368
  function formatText(result) {
1940
2369
  const lines = [];
@@ -2246,7 +2675,7 @@ function formatFilesCell(filesRouted) {
2246
2675
  }
2247
2676
  function formatConfigCell(tool, repositoryRoot) {
2248
2677
  if (tool.configSource === "local" && tool.configPath) {
2249
- const rawPath = import_path5.default.isAbsolute(tool.configPath) ? import_path5.default.relative(repositoryRoot, tool.configPath) : tool.configPath;
2678
+ const rawPath = import_path6.default.isAbsolute(tool.configPath) ? import_path6.default.relative(repositoryRoot, tool.configPath) : tool.configPath;
2250
2679
  return rawPath.startsWith("./") ? rawPath : `./${rawPath}`;
2251
2680
  }
2252
2681
  if (tool.configSource === "codacy") return "Codacy";
@@ -2541,15 +2970,14 @@ function updateConfigIncremental(base, current, next) {
2541
2970
  }
2542
2971
 
2543
2972
  // src/init.ts
2544
- var import_promises4 = __toESM(require("fs/promises"));
2545
2973
  var import_os3 = __toESM(require("os"));
2546
- var import_path8 = __toESM(require("path"));
2974
+ var import_path9 = __toESM(require("path"));
2547
2975
  var import_child_process4 = require("child_process");
2548
2976
  var import_tooling7 = require("@codacy/tooling");
2549
2977
 
2550
2978
  // src/discover/index.ts
2551
- var import_promises2 = __toESM(require("fs/promises"));
2552
- var import_path6 = __toESM(require("path"));
2979
+ var import_promises3 = __toESM(require("fs/promises"));
2980
+ var import_path7 = __toESM(require("path"));
2553
2981
 
2554
2982
  // src/discover/classify.ts
2555
2983
  var import_tooling6 = require("@codacy/tooling");
@@ -4049,7 +4477,12 @@ function isNotableFile(filePath) {
4049
4477
 
4050
4478
  // src/discover/index.ts
4051
4479
  async function discover(repositoryRoot, options) {
4052
- const unfiltered = listRepoFiles(repositoryRoot);
4480
+ let unfiltered;
4481
+ try {
4482
+ unfiltered = listRepoFiles(repositoryRoot);
4483
+ } catch {
4484
+ unfiltered = [];
4485
+ }
4053
4486
  const files = applyExclusions(unfiltered, [...options?.exclude ?? []], []);
4054
4487
  const excludedFiles = unfiltered.length - files.length;
4055
4488
  const filePatterns = extractFilePatterns(files);
@@ -4072,7 +4505,7 @@ async function discover(repositoryRoot, options) {
4072
4505
  frameworks,
4073
4506
  libraries,
4074
4507
  metadata: {
4075
- repositoryRoot: import_path6.default.resolve(repositoryRoot),
4508
+ repositoryRoot: import_path7.default.resolve(repositoryRoot),
4076
4509
  totalFiles: files.length,
4077
4510
  scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
4078
4511
  // Surfaced so a scan narrowed by excludes is never mistaken for a repo
@@ -4084,7 +4517,7 @@ async function discover(repositoryRoot, options) {
4084
4517
  function extractFilePatterns(files) {
4085
4518
  const extensions = /* @__PURE__ */ new Set();
4086
4519
  for (const file of files) {
4087
- const ext = import_path6.default.extname(file).toLowerCase();
4520
+ const ext = import_path7.default.extname(file).toLowerCase();
4088
4521
  if (ext) {
4089
4522
  extensions.add(`*${ext}`);
4090
4523
  }
@@ -4107,7 +4540,7 @@ async function extractDependencies(repositoryRoot, files) {
4107
4540
  if (!manifest) continue;
4108
4541
  let content;
4109
4542
  try {
4110
- content = await import_promises2.default.readFile(import_path6.default.join(repositoryRoot, filePath), "utf-8");
4543
+ content = await import_promises3.default.readFile(import_path7.default.join(repositoryRoot, filePath), "utf-8");
4111
4544
  } catch {
4112
4545
  continue;
4113
4546
  }
@@ -4147,15 +4580,15 @@ function deduplicateDeps(deps) {
4147
4580
  }
4148
4581
 
4149
4582
  // src/codacy-yaml.ts
4150
- var import_promises3 = __toESM(require("fs/promises"));
4151
- var import_path7 = __toESM(require("path"));
4583
+ var import_promises4 = __toESM(require("fs/promises"));
4584
+ var import_path8 = __toESM(require("path"));
4152
4585
  var import_yaml = require("yaml");
4153
4586
  async function readCodacyYaml(repositoryRoot) {
4154
4587
  const candidates = [".codacy.yaml", ".codacy.yml"];
4155
4588
  let raw = null;
4156
4589
  for (const filename of candidates) {
4157
4590
  try {
4158
- raw = await import_promises3.default.readFile(import_path7.default.join(repositoryRoot, filename), "utf-8");
4591
+ raw = await import_promises4.default.readFile(import_path8.default.join(repositoryRoot, filename), "utf-8");
4159
4592
  break;
4160
4593
  } catch {
4161
4594
  }
@@ -4256,7 +4689,7 @@ async function resolveDiscoveryExcludes(repositoryRoot, adapters, existingConfig
4256
4689
 
4257
4690
  // src/api/client/core/OpenAPI.ts
4258
4691
  var OpenAPI = {
4259
- BASE: "https://app.codacy.com/api/v3",
4692
+ BASE: "https://api.codacy.com/api/v3",
4260
4693
  VERSION: "3.1.0",
4261
4694
  WITH_CREDENTIALS: false,
4262
4695
  CREDENTIALS: "include",
@@ -4435,13 +4868,13 @@ var getQueryString = (params) => {
4435
4868
  };
4436
4869
  var getUrl = (config, options) => {
4437
4870
  const encoder = config.ENCODE_PATH || encodeURI;
4438
- const path10 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
4871
+ const path12 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
4439
4872
  if (options.path?.hasOwnProperty(group)) {
4440
4873
  return encoder(String(options.path[group]));
4441
4874
  }
4442
4875
  return substring;
4443
4876
  });
4444
- const url = `${config.BASE}${path10}`;
4877
+ const url = `${config.BASE}${path12}`;
4445
4878
  if (options.query) {
4446
4879
  return `${url}${getQueryString(options.query)}`;
4447
4880
  }
@@ -5176,10 +5609,10 @@ var AnalysisService = class {
5176
5609
  * Patterns configuration for (repository, tool). Uses standard if applied, repository settings otherwise.
5177
5610
  *
5178
5611
  * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param toolUuid Unique identifier (UUID) for the tool* @param languages Comma-separated list of programming languages to filter results by* @param categories Filter by a comma-separated list of code pattern categories. Valid values are `Security`, `ErrorProne`, `CodeStyle`, `Compatibility`, `UnusedCode`, `Complexity`, `Comprehensibility`, `Documentation`, `BestPractice`, and `Performance`.
5179
- * * @param severityLevels Filter by a comma-separated list of code pattern severity levels. Valid values are `Error`, `High`, `Warning`, and `Info`.* @param tags Filter by a comma-separated list of pattern tags* @param search Filter results by searching for this string* @param enabled Filter by pattern status. Set to `true` to return only enabled patterns, or `false` to return only disabled patterns* @param recommended Filter by recommended status. Set to `true` to return only recommended patterns, or `false` to return only non-recommended patterns* @param sort Field used to sort the tool's code patterns. Valid values are `category`, `recommended`, and `severity`.* @param direction Sort direction. Possible values are 'asc' (ascending) or 'desc' (descending).* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return * @returns ConfiguredPatternsListResponse Successful operation
5612
+ * * @param severityLevels Filter by a comma-separated list of code pattern severity levels. Valid values are `Error`, `High`, `Warning`, and `Info`.* @param tags Filter by a comma-separated list of pattern tags* @param search Filter results by searching for this string* @param enabled Filter by pattern status. Set to `true` to return only enabled patterns, or `false` to return only disabled patterns* @param recommended Filter by recommended status. Set to `true` to return only recommended patterns, or `false` to return only non-recommended patterns* @param matchesStack Filter repository code patterns by whether they match the repository stack.* @param sort Field used to sort the tool's code patterns. Valid values are `category`, `recommended`, and `severity`.* @param direction Sort direction. Possible values are 'asc' (ascending) or 'desc' (descending).* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return * @returns ConfiguredPatternsListResponse Successful operation
5180
5613
  * @throws ApiError
5181
5614
  */
5182
- static listRepositoryToolPatterns(provider, remoteOrganizationName, repositoryName, toolUuid, languages, categories, severityLevels, tags, search, enabled, recommended, sort, direction, cursor, limit = 100, abortSignal) {
5615
+ static listRepositoryToolPatterns(provider, remoteOrganizationName, repositoryName, toolUuid, languages, categories, severityLevels, tags, search, enabled, recommended, matchesStack, sort, direction, cursor, limit = 100, abortSignal) {
5183
5616
  return request(OpenAPI, {
5184
5617
  method: "GET",
5185
5618
  url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tools/{toolUuid}/patterns",
@@ -5197,6 +5630,7 @@ var AnalysisService = class {
5197
5630
  "search": search,
5198
5631
  "enabled": enabled,
5199
5632
  "recommended": recommended,
5633
+ "matchesStack": matchesStack,
5200
5634
  "sort": sort,
5201
5635
  "direction": direction,
5202
5636
  "cursor": cursor,
@@ -5218,10 +5652,10 @@ var AnalysisService = class {
5218
5652
  * Use filters to specify the code patterns to update, or omit the filters to update all code patterns.
5219
5653
  *
5220
5654
  * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param toolUuid Unique identifier (UUID) for the tool* @param requestBody * @param languages Comma-separated list of programming languages to filter results by* @param categories Filter by a comma-separated list of code pattern categories. Valid values are `Security`, `ErrorProne`, `CodeStyle`, `Compatibility`, `UnusedCode`, `Complexity`, `Comprehensibility`, `Documentation`, `BestPractice`, and `Performance`.
5221
- * * @param severityLevels Filter by a comma-separated list of code pattern severity levels. Valid values are `Error`, `High`, `Warning`, and `Info`.* @param tags Filter by a comma-separated list of pattern tags* @param search Filter results by searching for this string* @param recommended Filter by recommended status. Set to `true` to return only recommended patterns, or `false` to return only non-recommended patterns * @returns void
5655
+ * * @param severityLevels Filter by a comma-separated list of code pattern severity levels. Valid values are `Error`, `High`, `Warning`, and `Info`.* @param tags Filter by a comma-separated list of pattern tags* @param search Filter results by searching for this string* @param recommended Filter by recommended status. Set to `true` to return only recommended patterns, or `false` to return only non-recommended patterns* @param matchesStack Filter repository code patterns by whether they match the repository stack. * @returns void
5222
5656
  * @throws ApiError
5223
5657
  */
5224
- static updateRepositoryToolPatterns(provider, remoteOrganizationName, repositoryName, toolUuid, requestBody, languages, categories, severityLevels, tags, search, recommended, abortSignal) {
5658
+ static updateRepositoryToolPatterns(provider, remoteOrganizationName, repositoryName, toolUuid, requestBody, languages, categories, severityLevels, tags, search, recommended, matchesStack, abortSignal) {
5225
5659
  return request(OpenAPI, {
5226
5660
  method: "PATCH",
5227
5661
  url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tools/{toolUuid}/patterns",
@@ -5237,7 +5671,8 @@ var AnalysisService = class {
5237
5671
  "severityLevels": severityLevels,
5238
5672
  "tags": tags,
5239
5673
  "search": search,
5240
- "recommended": recommended
5674
+ "recommended": recommended,
5675
+ "matchesStack": matchesStack
5241
5676
  },
5242
5677
  abortSignal,
5243
5678
  body: requestBody,
@@ -5285,10 +5720,10 @@ var AnalysisService = class {
5285
5720
  * Patterns overview for tool. Uses standard if applied, repository settings otherwise.
5286
5721
  *
5287
5722
  * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param toolUuid Unique identifier (UUID) for the tool* @param languages Comma-separated list of programming languages to filter results by* @param categories Filter by a comma-separated list of code pattern categories. Valid values are `Security`, `ErrorProne`, `CodeStyle`, `Compatibility`, `UnusedCode`, `Complexity`, `Comprehensibility`, `Documentation`, `BestPractice`, and `Performance`.
5288
- * * @param severityLevels Filter by a comma-separated list of code pattern severity levels. Valid values are `Error`, `High`, `Warning`, and `Info`.* @param tags Filter by a comma-separated list of pattern tags* @param search Filter results by searching for this string* @param enabled Filter by pattern status. Set to `true` to return only enabled patterns, or `false` to return only disabled patterns* @param recommended Filter by recommended status. Set to `true` to return only recommended patterns, or `false` to return only non-recommended patterns * @returns ToolPatternsOverviewResponse Successful operation
5723
+ * * @param severityLevels Filter by a comma-separated list of code pattern severity levels. Valid values are `Error`, `High`, `Warning`, and `Info`.* @param tags Filter by a comma-separated list of pattern tags* @param search Filter results by searching for this string* @param enabled Filter by pattern status. Set to `true` to return only enabled patterns, or `false` to return only disabled patterns* @param recommended Filter by recommended status. Set to `true` to return only recommended patterns, or `false` to return only non-recommended patterns* @param matchesStack Filter repository code patterns by whether they match the repository stack. * @returns ToolPatternsOverviewResponse Successful operation
5289
5724
  * @throws ApiError
5290
5725
  */
5291
- static toolPatternsOverview(provider, remoteOrganizationName, repositoryName, toolUuid, languages, categories, severityLevels, tags, search, enabled, recommended, abortSignal) {
5726
+ static toolPatternsOverview(provider, remoteOrganizationName, repositoryName, toolUuid, languages, categories, severityLevels, tags, search, enabled, recommended, matchesStack, abortSignal) {
5292
5727
  return request(OpenAPI, {
5293
5728
  method: "GET",
5294
5729
  url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tools/{toolUuid}/patterns/overview",
@@ -5305,7 +5740,8 @@ var AnalysisService = class {
5305
5740
  "tags": tags,
5306
5741
  "search": search,
5307
5742
  "enabled": enabled,
5308
- "recommended": recommended
5743
+ "recommended": recommended,
5744
+ "matchesStack": matchesStack
5309
5745
  },
5310
5746
  abortSignal,
5311
5747
  errors: {
@@ -5378,33 +5814,34 @@ var AnalysisService = class {
5378
5814
  });
5379
5815
  }
5380
5816
  /**
5381
- * List pull requests from a repository that the user has access to
5382
- * You can search this endpoint for either `last-updated` (default), `impact` or `merged`
5383
- *
5384
- * **Note:** When applied to public repositories, this operation does not require authentication.
5817
+ * Recover a stuck repository
5818
+ * Triggers a recovery action when the repository is stuck on its first analysis.
5819
+ * The exact action taken depends on the stuck state.
5820
+ * Returns `422 Unprocessable Entity` when the repository is not stuck.
5385
5821
  *
5386
- * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param limit Maximum number of items to return* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param search Filter results by searching for this string* @param includeNotAnalyzed If true, also return pull requests that weren't analyzed * @returns PullRequestWithAnalysisListResponse Successful operation
5822
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param branch Name of a [repository branch enabled on Codacy](https://docs.codacy.com/repositories-configure/managing-branches/),
5823
+ * as returned by the endpoint [listRepositoryBranches](#listrepositorybranches).
5824
+ * By default, uses the main branch defined on the Codacy repository settings.
5825
+ * * @returns void
5387
5826
  * @throws ApiError
5388
5827
  */
5389
- static listRepositoryPullRequests(provider, remoteOrganizationName, repositoryName, limit = 100, cursor, search, includeNotAnalyzed = false, abortSignal) {
5828
+ static recoverRepository(provider, remoteOrganizationName, repositoryName, branch, abortSignal) {
5390
5829
  return request(OpenAPI, {
5391
- method: "GET",
5392
- url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/pull-requests",
5830
+ method: "POST",
5831
+ url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/recover",
5393
5832
  path: {
5394
5833
  "provider": provider,
5395
5834
  "remoteOrganizationName": remoteOrganizationName,
5396
5835
  "repositoryName": repositoryName
5397
5836
  },
5398
5837
  query: {
5399
- "limit": limit,
5400
- "cursor": cursor,
5401
- "search": search,
5402
- "includeNotAnalyzed": includeNotAnalyzed
5838
+ "branch": branch
5403
5839
  },
5404
5840
  abortSignal,
5405
5841
  errors: {
5406
5842
  400: `Bad Request`,
5407
5843
  401: `Unauthorized`,
5844
+ 403: `Forbidden`,
5408
5845
  404: `Not Found`,
5409
5846
  422: `Unprocessable Entity`,
5410
5847
  500: `Internal Server Error`
@@ -5412,15 +5849,134 @@ var AnalysisService = class {
5412
5849
  });
5413
5850
  }
5414
5851
  /**
5415
- * Get pull request from a repository
5416
- * **Note:** When applied to public repositories, this operation does not require authentication.
5852
+ * Add repository autoconfiguration run
5853
+ * **[Experimental]** Autoconfig enqueuing endpoint
5417
5854
  *
5418
- * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param pullRequestNumber Pull request number * @returns PullRequestWithAnalysis Successful operation
5855
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization * @returns AddAutoconfigResponse Autoconfig run accepted
5419
5856
  * @throws ApiError
5420
5857
  */
5421
- static getRepositoryPullRequest(provider, remoteOrganizationName, repositoryName, pullRequestNumber, abortSignal) {
5858
+ static addAutoconfig(provider, remoteOrganizationName, repositoryName, abortSignal) {
5422
5859
  return request(OpenAPI, {
5423
- method: "GET",
5860
+ method: "POST",
5861
+ url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/autoconfig",
5862
+ path: {
5863
+ "provider": provider,
5864
+ "remoteOrganizationName": remoteOrganizationName,
5865
+ "repositoryName": repositoryName
5866
+ },
5867
+ abortSignal,
5868
+ errors: {
5869
+ 400: `Bad Request`,
5870
+ 401: `Unauthorized`,
5871
+ 403: `Forbidden`,
5872
+ 404: `Not Found`,
5873
+ 422: `Unprocessable Entity`,
5874
+ 429: `Too Many Requests`,
5875
+ 500: `Internal Server Error`
5876
+ }
5877
+ });
5878
+ }
5879
+ /**
5880
+ * Get latest autoconfig run summary
5881
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization * @returns AutoconfigRunSummaryResponse Successful operation
5882
+ * @throws ApiError
5883
+ */
5884
+ static getLatestAutoconfigRunSummary(provider, remoteOrganizationName, repositoryName, abortSignal) {
5885
+ return request(OpenAPI, {
5886
+ method: "GET",
5887
+ url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/autoconfig/runs",
5888
+ path: {
5889
+ "provider": provider,
5890
+ "remoteOrganizationName": remoteOrganizationName,
5891
+ "repositoryName": repositoryName
5892
+ },
5893
+ abortSignal,
5894
+ errors: {
5895
+ 400: `Bad Request`,
5896
+ 401: `Unauthorized`,
5897
+ 403: `Forbidden`,
5898
+ 404: `Not Found`,
5899
+ 500: `Internal Server Error`
5900
+ }
5901
+ });
5902
+ }
5903
+ /**
5904
+ * Get autoconfig run status
5905
+ * **[Experimental]** Fetch latest autoconfig run state managed (can be `Requested`, `Running`, `Done` or `Failed`)
5906
+ *
5907
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization * @returns AutoconfigStatusResponse Successful operation
5908
+ * @throws ApiError
5909
+ */
5910
+ static getAutoconfigStatus(provider, remoteOrganizationName, repositoryName, abortSignal) {
5911
+ return request(OpenAPI, {
5912
+ method: "GET",
5913
+ url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/autoconfig/status",
5914
+ path: {
5915
+ "provider": provider,
5916
+ "remoteOrganizationName": remoteOrganizationName,
5917
+ "repositoryName": repositoryName
5918
+ },
5919
+ abortSignal,
5920
+ errors: {
5921
+ 400: `Bad Request`,
5922
+ 401: `Unauthorized`,
5923
+ 403: `Forbidden`,
5924
+ 404: `Not Found`,
5925
+ 500: `Internal Server Error`
5926
+ }
5927
+ });
5928
+ }
5929
+ /**
5930
+ * List pull requests from a repository that the user has access to
5931
+ * You can search this endpoint for either `last-updated` (default), `merged`
5932
+ *
5933
+ * **Note:** When applied to public repositories, this operation does not require authentication.
5934
+ *
5935
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param limit Maximum number of items to return* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param search Filter results by searching for this string* @param textQuery Filter pull requests by a free-text search matched, case-insensitively, against the
5936
+ * pull request title or the author's handle.
5937
+ * * @param targetBranch Filter pull requests by the name of their target branch.
5938
+ * Pull requests targeting any other branch are excluded from the results.
5939
+ * By default, pull requests targeting any branch are returned.
5940
+ * * @param includeNotAnalyzed If true, also return pull requests that weren't analyzed * @returns PullRequestWithAnalysisListResponse Successful operation
5941
+ * @throws ApiError
5942
+ */
5943
+ static listRepositoryPullRequests(provider, remoteOrganizationName, repositoryName, limit = 100, cursor, search, textQuery, targetBranch, includeNotAnalyzed = false, abortSignal) {
5944
+ return request(OpenAPI, {
5945
+ method: "GET",
5946
+ url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/pull-requests",
5947
+ path: {
5948
+ "provider": provider,
5949
+ "remoteOrganizationName": remoteOrganizationName,
5950
+ "repositoryName": repositoryName
5951
+ },
5952
+ query: {
5953
+ "limit": limit,
5954
+ "cursor": cursor,
5955
+ "search": search,
5956
+ "textQuery": textQuery,
5957
+ "targetBranch": targetBranch,
5958
+ "includeNotAnalyzed": includeNotAnalyzed
5959
+ },
5960
+ abortSignal,
5961
+ errors: {
5962
+ 400: `Bad Request`,
5963
+ 401: `Unauthorized`,
5964
+ 404: `Not Found`,
5965
+ 422: `Unprocessable Entity`,
5966
+ 500: `Internal Server Error`
5967
+ }
5968
+ });
5969
+ }
5970
+ /**
5971
+ * Get pull request from a repository
5972
+ * **Note:** When applied to public repositories, this operation does not require authentication.
5973
+ *
5974
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param pullRequestNumber Pull request number * @returns PullRequestWithAnalysis Successful operation
5975
+ * @throws ApiError
5976
+ */
5977
+ static getRepositoryPullRequest(provider, remoteOrganizationName, repositoryName, pullRequestNumber, abortSignal) {
5978
+ return request(OpenAPI, {
5979
+ method: "GET",
5424
5980
  url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/pull-requests/{pullRequestNumber}",
5425
5981
  path: {
5426
5982
  "provider": provider,
@@ -5494,6 +6050,33 @@ var AnalysisService = class {
5494
6050
  }
5495
6051
  });
5496
6052
  }
6053
+ /**
6054
+ * Triggers an AI review for a pull request
6055
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param pullRequestNumber Pull request number * @returns void
6056
+ * @throws ApiError
6057
+ */
6058
+ static triggerPullRequestAiReview(provider, remoteOrganizationName, repositoryName, pullRequestNumber, abortSignal) {
6059
+ return request(OpenAPI, {
6060
+ method: "POST",
6061
+ url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/pull-requests/{pullRequestNumber}/ai-reviewer/trigger",
6062
+ path: {
6063
+ "provider": provider,
6064
+ "remoteOrganizationName": remoteOrganizationName,
6065
+ "repositoryName": repositoryName,
6066
+ "pullRequestNumber": pullRequestNumber
6067
+ },
6068
+ abortSignal,
6069
+ errors: {
6070
+ 400: `Bad Request`,
6071
+ 401: `Unauthorized`,
6072
+ 403: `Forbidden`,
6073
+ 404: `Not Found`,
6074
+ 409: `Conflict`,
6075
+ 422: `Unprocessable Entity`,
6076
+ 500: `Internal Server Error`
6077
+ }
6078
+ });
6079
+ }
5497
6080
  /**
5498
6081
  * List all coverage reports uploaded for the common ancestor commit and head commit of a pull request branch
5499
6082
  * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param pullRequestNumber Pull request number * @returns CoveragePullRequestResponse Successful operation
@@ -5767,7 +6350,7 @@ var AnalysisService = class {
5767
6350
  }
5768
6351
  /**
5769
6352
  * List organization pull requests from repositories that the user has access to
5770
- * You can sort by `last-updated` (default), `impact`, or `merged`
6353
+ * You can sort by `last-updated` (default), `merged`
5771
6354
  * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param limit Maximum number of items to return* @param search Filter results by searching for this string* @param repositories **Deprecated:** Use [searchOrganizationRepositoriesWithAnalysis](#searchorganizationrepositorieswithanalysis) instead. * @returns PullRequestWithAnalysisListResponse Successful operation
5772
6355
  * @throws ApiError
5773
6356
  */
@@ -5896,7 +6479,7 @@ var AnalysisService = class {
5896
6479
  }
5897
6480
  /**
5898
6481
  * Bulk ignore issues in a repository
5899
- * Receives a list of issue ids and ignores them in the specified repository. Limited to 50 issues at once
6482
+ * Receives a list of issue ids and ignores them in the specified repository. Limited to 100 issues at once
5900
6483
  * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param requestBody List of issue ids to ignore * @returns void
5901
6484
  * @throws ApiError
5902
6485
  */
@@ -7114,10 +7697,16 @@ var RepositoryService = class {
7114
7697
  * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param branch Name of a [repository branch enabled on Codacy](https://docs.codacy.com/repositories-configure/managing-branches/),
7115
7698
  * as returned by the endpoint [listRepositoryBranches](#listrepositorybranches).
7116
7699
  * By default, uses the main branch defined on the Codacy repository settings.
7117
- * * @param search Filter files that include this string anywhere in their relative path* @param sort Field used to sort the list of files. Valid values are `filename`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.* @param direction Sort direction. Possible values are 'asc' (ascending) or 'desc' (descending).* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return * @returns FileListResponse List of files in the repository
7700
+ * * @param path Relative path of the folder whose direct child files are listed.
7701
+ * When absent, all files in the repository are returned (recursive).
7702
+ * When empty, only files at the repository root are returned.
7703
+ * * @param search Filter files that include this string anywhere in their relative path* @param sort Field used to sort the results.
7704
+ * For files the valid values are `filename`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.
7705
+ * For folders the valid values are `name`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.
7706
+ * * @param direction Sort direction. Possible values are 'asc' (ascending) or 'desc' (descending).* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return * @returns FileListResponse List of files in the repository
7118
7707
  * @throws ApiError
7119
7708
  */
7120
- static listFiles(provider, remoteOrganizationName, repositoryName, branch, search, sort, direction, cursor, limit = 100, abortSignal) {
7709
+ static listFiles(provider, remoteOrganizationName, repositoryName, branch, path12, search, sort, direction, cursor, limit = 100, abortSignal) {
7121
7710
  return request(OpenAPI, {
7122
7711
  method: "GET",
7123
7712
  url: "/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/files",
@@ -7128,6 +7717,7 @@ var RepositoryService = class {
7128
7717
  },
7129
7718
  query: {
7130
7719
  "branch": branch,
7720
+ "path": path12,
7131
7721
  "search": search,
7132
7722
  "sort": sort,
7133
7723
  "direction": direction,
@@ -7144,6 +7734,48 @@ var RepositoryService = class {
7144
7734
  }
7145
7735
  });
7146
7736
  }
7737
+ /**
7738
+ * List folders in a repository
7739
+ * Returns the most recent analysis information for the folders directly inside a path of a repository, with metrics aggregated over every file in each folder's subtree.
7740
+ * Use the `path` parameter to list the folders inside a folder; when absent or empty, the folders at the repository root are returned.
7741
+ *
7742
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param branch Name of a [repository branch enabled on Codacy](https://docs.codacy.com/repositories-configure/managing-branches/),
7743
+ * as returned by the endpoint [listRepositoryBranches](#listrepositorybranches).
7744
+ * By default, uses the main branch defined on the Codacy repository settings.
7745
+ * * @param path Path of the folder whose child folders are listed.
7746
+ * When absent or empty, the folders at the repository root are returned.
7747
+ * * @param sort Field used to sort the results.
7748
+ * For files the valid values are `filename`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.
7749
+ * For folders the valid values are `name`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.
7750
+ * * @param direction Sort direction. Possible values are 'asc' (ascending) or 'desc' (descending).* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return * @returns DirectoryListResponse List of folders in the repository
7751
+ * @throws ApiError
7752
+ */
7753
+ static listDirectories(provider, remoteOrganizationName, repositoryName, branch, path12, sort, direction, cursor, limit = 100, abortSignal) {
7754
+ return request(OpenAPI, {
7755
+ method: "GET",
7756
+ url: "/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/directories",
7757
+ path: {
7758
+ "provider": provider,
7759
+ "remoteOrganizationName": remoteOrganizationName,
7760
+ "repositoryName": repositoryName
7761
+ },
7762
+ query: {
7763
+ "branch": branch,
7764
+ "path": path12,
7765
+ "sort": sort,
7766
+ "direction": direction,
7767
+ "cursor": cursor,
7768
+ "limit": limit
7769
+ },
7770
+ abortSignal,
7771
+ errors: {
7772
+ 400: `Bad Request`,
7773
+ 401: `Unauthorized`,
7774
+ 404: `Not Found`,
7775
+ 500: `Internal Server Error`
7776
+ }
7777
+ });
7778
+ }
7147
7779
  /**
7148
7780
  * List ignored files in a repository
7149
7781
  * Returns the most recent ignored files information.
@@ -7267,6 +7899,9 @@ var RepositoryService = class {
7267
7899
  }
7268
7900
  /**
7269
7901
  * List the [repository API tokens](https://docs.codacy.com/codacy-api/api-tokens/)
7902
+ * Each token reports its name, expiration and creation date. Tokens created before creation dates were
7903
+ * recorded have no `createdAt`
7904
+ *
7270
7905
  * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization * @returns ApiTokenListResponse Successful operation
7271
7906
  * @throws ApiError
7272
7907
  */
@@ -7291,10 +7926,10 @@ var RepositoryService = class {
7291
7926
  }
7292
7927
  /**
7293
7928
  * Create a new [repository API token](https://docs.codacy.com/codacy-api/api-tokens/)
7294
- * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization * @returns ApiTokenResponse Successful operation
7929
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param requestBody * @returns ApiTokenResponse Successful operation
7295
7930
  * @throws ApiError
7296
7931
  */
7297
- static createRepositoryApiToken(provider, remoteOrganizationName, repositoryName, abortSignal) {
7932
+ static createRepositoryApiToken(provider, remoteOrganizationName, repositoryName, requestBody, abortSignal) {
7298
7933
  return request(OpenAPI, {
7299
7934
  method: "POST",
7300
7935
  url: "/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tokens",
@@ -7304,6 +7939,35 @@ var RepositoryService = class {
7304
7939
  "repositoryName": repositoryName
7305
7940
  },
7306
7941
  abortSignal,
7942
+ body: requestBody,
7943
+ mediaType: "application/json",
7944
+ errors: {
7945
+ 400: `Bad Request`,
7946
+ 401: `Unauthorized`,
7947
+ 404: `Not Found`,
7948
+ 422: `Unprocessable Entity`,
7949
+ 500: `Internal Server Error`
7950
+ }
7951
+ });
7952
+ }
7953
+ /**
7954
+ * Delete multiple [repository API tokens](https://docs.codacy.com/codacy-api/api-tokens/) by id
7955
+ * Deletes the repository API tokens with the given ids
7956
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param requestBody * @returns void
7957
+ * @throws ApiError
7958
+ */
7959
+ static deleteRepositoryApiTokens(provider, remoteOrganizationName, repositoryName, requestBody, abortSignal) {
7960
+ return request(OpenAPI, {
7961
+ method: "POST",
7962
+ url: "/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tokens/delete",
7963
+ path: {
7964
+ "provider": provider,
7965
+ "remoteOrganizationName": remoteOrganizationName,
7966
+ "repositoryName": repositoryName
7967
+ },
7968
+ abortSignal,
7969
+ body: requestBody,
7970
+ mediaType: "application/json",
7307
7971
  errors: {
7308
7972
  400: `Bad Request`,
7309
7973
  401: `Unauthorized`,
@@ -7555,6 +8219,256 @@ var RepositoryService = class {
7555
8219
  }
7556
8220
  };
7557
8221
 
8222
+ // src/api/client/services/SbomService.ts
8223
+ var SbomService = class {
8224
+ /**
8225
+ * Return a list of SBOM dependencies, used across the organization.
8226
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return* @param sortColumn Field used to sort the results. The possible values are `severity` (default, to sort by the item's severity), or `order` (to sort by the OSSF order).* @param columnOrder Sort direction. The possible values are `asc` (ascending) or `desc` (descending - default).* @param requestBody Request body with filters. * @returns SearchSbomDependenciesResponse Successful operation
8227
+ * @throws ApiError
8228
+ */
8229
+ static searchSbomDependencies(provider, remoteOrganizationName, cursor, limit = 100, sortColumn = "severity", columnOrder = "desc", requestBody, abortSignal) {
8230
+ return request(OpenAPI, {
8231
+ method: "POST",
8232
+ url: "/organizations/{provider}/{remoteOrganizationName}/sbom/dependencies/search",
8233
+ path: {
8234
+ "provider": provider,
8235
+ "remoteOrganizationName": remoteOrganizationName
8236
+ },
8237
+ query: {
8238
+ "cursor": cursor,
8239
+ "limit": limit,
8240
+ "sortColumn": sortColumn,
8241
+ "columnOrder": columnOrder
8242
+ },
8243
+ abortSignal,
8244
+ body: requestBody,
8245
+ mediaType: "application/json",
8246
+ errors: {
8247
+ 400: `Bad Request`,
8248
+ 401: `Unauthorized`,
8249
+ 403: `Forbidden`,
8250
+ 404: `Not Found`,
8251
+ 422: `Unprocessable Entity`,
8252
+ 500: `Internal Server Error`,
8253
+ 501: `Not Implemented`,
8254
+ 502: `Bad Gateway`
8255
+ }
8256
+ });
8257
+ }
8258
+ /**
8259
+ * Return a paginated list of repositories where a version of the specified SBOM dependency is used.
8260
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param requestBody Request body with filters.* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return * @returns SearchRepositoriesOfSbomDependencyResponse Successful operation
8261
+ * @throws ApiError
8262
+ */
8263
+ static searchRepositoriesOfSbomDependency(provider, remoteOrganizationName, requestBody, cursor, limit = 100, abortSignal) {
8264
+ return request(OpenAPI, {
8265
+ method: "POST",
8266
+ url: "/organizations/{provider}/{remoteOrganizationName}/sbom/dependencies/repositories/search",
8267
+ path: {
8268
+ "provider": provider,
8269
+ "remoteOrganizationName": remoteOrganizationName
8270
+ },
8271
+ query: {
8272
+ "cursor": cursor,
8273
+ "limit": limit
8274
+ },
8275
+ abortSignal,
8276
+ body: requestBody,
8277
+ mediaType: "application/json",
8278
+ errors: {
8279
+ 400: `Bad Request`,
8280
+ 401: `Unauthorized`,
8281
+ 403: `Forbidden`,
8282
+ 404: `Not Found`,
8283
+ 422: `Unprocessable Entity`,
8284
+ 500: `Internal Server Error`,
8285
+ 501: `Not Implemented`,
8286
+ 502: `Bad Gateway`
8287
+ }
8288
+ });
8289
+ }
8290
+ /**
8291
+ * List repositories with SBOM dependency information
8292
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return* @param requestBody Request body with filters. * @returns SearchSbomRepositoriesResponse Successful operation
8293
+ * @throws ApiError
8294
+ */
8295
+ static searchSbomRepositories(provider, remoteOrganizationName, cursor, limit = 100, requestBody, abortSignal) {
8296
+ return request(OpenAPI, {
8297
+ method: "POST",
8298
+ url: "/organizations/{provider}/{remoteOrganizationName}/sbom/repositories/search",
8299
+ path: {
8300
+ "provider": provider,
8301
+ "remoteOrganizationName": remoteOrganizationName
8302
+ },
8303
+ query: {
8304
+ "cursor": cursor,
8305
+ "limit": limit
8306
+ },
8307
+ abortSignal,
8308
+ body: requestBody,
8309
+ mediaType: "application/json",
8310
+ errors: {
8311
+ 400: `Bad Request`,
8312
+ 401: `Unauthorized`,
8313
+ 403: `Forbidden`,
8314
+ 404: `Not Found`,
8315
+ 422: `Unprocessable Entity`,
8316
+ 500: `Internal Server Error`,
8317
+ 501: `Not Implemented`,
8318
+ 502: `Bad Gateway`
8319
+ }
8320
+ });
8321
+ }
8322
+ /**
8323
+ * Get a presigned URL for the latest SBOM of the repository
8324
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization * @returns SbomPresignResponse Successful operation.
8325
+ * @throws ApiError
8326
+ */
8327
+ static getRepositorySbomPresignedUrl(provider, remoteOrganizationName, repositoryName, abortSignal) {
8328
+ return request(OpenAPI, {
8329
+ method: "GET",
8330
+ url: "/organizations/{provider}/{remoteOrganizationName}/projects/{repositoryName}/sbom",
8331
+ path: {
8332
+ "provider": provider,
8333
+ "remoteOrganizationName": remoteOrganizationName,
8334
+ "repositoryName": repositoryName
8335
+ },
8336
+ abortSignal,
8337
+ errors: {
8338
+ 400: `Bad Request`,
8339
+ 401: `Unauthorized`,
8340
+ 404: `Not Found`,
8341
+ 500: `Internal Server Error`
8342
+ }
8343
+ });
8344
+ }
8345
+ /**
8346
+ * Upload an SBOM for a Docker image
8347
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param formData * @returns void
8348
+ * @throws ApiError
8349
+ */
8350
+ static uploadImageSbom(provider, remoteOrganizationName, formData, abortSignal) {
8351
+ return request(OpenAPI, {
8352
+ method: "POST",
8353
+ url: "/organizations/{provider}/{remoteOrganizationName}/image-sboms",
8354
+ path: {
8355
+ "provider": provider,
8356
+ "remoteOrganizationName": remoteOrganizationName
8357
+ },
8358
+ abortSignal,
8359
+ formData,
8360
+ mediaType: "multipart/form-data",
8361
+ errors: {
8362
+ 400: `Bad Request`,
8363
+ 401: `Unauthorized`,
8364
+ 404: `Not Found`,
8365
+ 409: `Conflict`,
8366
+ 500: `Internal Server Error`
8367
+ }
8368
+ });
8369
+ }
8370
+ /**
8371
+ * Delete all SBOMs for a given image
8372
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param imageName Image name of the SBOM Image * @returns void
8373
+ * @throws ApiError
8374
+ */
8375
+ static deleteImageSboms(provider, remoteOrganizationName, imageName, abortSignal) {
8376
+ return request(OpenAPI, {
8377
+ method: "DELETE",
8378
+ url: "/organizations/{provider}/{remoteOrganizationName}/image-sboms/{imageName}",
8379
+ path: {
8380
+ "provider": provider,
8381
+ "remoteOrganizationName": remoteOrganizationName,
8382
+ "imageName": imageName
8383
+ },
8384
+ abortSignal,
8385
+ errors: {
8386
+ 400: `Bad Request`,
8387
+ 401: `Unauthorized`,
8388
+ 404: `Not Found`,
8389
+ 500: `Internal Server Error`
8390
+ }
8391
+ });
8392
+ }
8393
+ /**
8394
+ * Delete SBOM for a given image/tag combination
8395
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param imageName Image name of the SBOM Image* @param tag Image Tag of the SBOM Image * @returns void
8396
+ * @throws ApiError
8397
+ */
8398
+ static deleteImageTag(provider, remoteOrganizationName, imageName, tag, abortSignal) {
8399
+ return request(OpenAPI, {
8400
+ method: "DELETE",
8401
+ url: "/organizations/{provider}/{remoteOrganizationName}/image-sboms/{imageName}/tags/{tag}",
8402
+ path: {
8403
+ "provider": provider,
8404
+ "remoteOrganizationName": remoteOrganizationName,
8405
+ "imageName": imageName,
8406
+ "tag": tag
8407
+ },
8408
+ abortSignal,
8409
+ errors: {
8410
+ 400: `Bad Request`,
8411
+ 401: `Unauthorized`,
8412
+ 404: `Not Found`,
8413
+ 500: `Internal Server Error`
8414
+ }
8415
+ });
8416
+ }
8417
+ /**
8418
+ * List Docker images for an organization
8419
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return * @returns ListImagesResponse Successful operation
8420
+ * @throws ApiError
8421
+ */
8422
+ static listOrganizationImages(provider, remoteOrganizationName, cursor, limit = 100, abortSignal) {
8423
+ return request(OpenAPI, {
8424
+ method: "GET",
8425
+ url: "/organizations/{provider}/{remoteOrganizationName}/images",
8426
+ path: {
8427
+ "provider": provider,
8428
+ "remoteOrganizationName": remoteOrganizationName
8429
+ },
8430
+ query: {
8431
+ "cursor": cursor,
8432
+ "limit": limit
8433
+ },
8434
+ abortSignal,
8435
+ errors: {
8436
+ 400: `Bad Request`,
8437
+ 401: `Unauthorized`,
8438
+ 404: `Not Found`,
8439
+ 500: `Internal Server Error`
8440
+ }
8441
+ });
8442
+ }
8443
+ /**
8444
+ * List Docker image tags for an organization
8445
+ * @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param imageName Image name of the SBOM Image* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param limit Maximum number of items to return * @returns ListImageTagsResponse Successful operation
8446
+ * @throws ApiError
8447
+ */
8448
+ static listImageTags(provider, remoteOrganizationName, imageName, cursor, limit = 100, abortSignal) {
8449
+ return request(OpenAPI, {
8450
+ method: "GET",
8451
+ url: "/organizations/{provider}/{remoteOrganizationName}/images/{imageName}/tags",
8452
+ path: {
8453
+ "provider": provider,
8454
+ "remoteOrganizationName": remoteOrganizationName,
8455
+ "imageName": imageName
8456
+ },
8457
+ query: {
8458
+ "cursor": cursor,
8459
+ "limit": limit
8460
+ },
8461
+ abortSignal,
8462
+ errors: {
8463
+ 400: `Bad Request`,
8464
+ 401: `Unauthorized`,
8465
+ 404: `Not Found`,
8466
+ 500: `Internal Server Error`
8467
+ }
8468
+ });
8469
+ }
8470
+ };
8471
+
7558
8472
  // src/api/client/services/ToolsService.ts
7559
8473
  var ToolsService = class {
7560
8474
  /**
@@ -7753,6 +8667,8 @@ async function listRepositoryToolPatterns(provider, org, repo, toolUuid) {
7753
8667
  void 0,
7754
8668
  // recommended
7755
8669
  void 0,
8670
+ // matchesStack
8671
+ void 0,
7756
8672
  // sort
7757
8673
  void 0,
7758
8674
  // direction
@@ -7790,6 +8706,20 @@ async function listIgnoredFiles(provider, org, repo) {
7790
8706
  capped: pages >= MAX_PAGES && cursor !== void 0
7791
8707
  };
7792
8708
  }
8709
+ async function uploadImageSbom(provider, organization, upload, signal) {
8710
+ await SbomService.uploadImageSbom(
8711
+ provider,
8712
+ organization,
8713
+ {
8714
+ sbom: upload.sbom,
8715
+ imageName: upload.imageName,
8716
+ tag: upload.tag,
8717
+ ...upload.repositoryName ? { repositoryName: upload.repositoryName } : {},
8718
+ ...upload.environment ? { environment: upload.environment } : {}
8719
+ },
8720
+ signal
8721
+ );
8722
+ }
7793
8723
 
7794
8724
  // src/init.ts
7795
8725
  function isAuthFailure(err) {
@@ -7827,6 +8757,13 @@ function buildAdapterByUuid(adapters, apiTools) {
7827
8757
  }
7828
8758
  return result;
7829
8759
  }
8760
+ function listRepoFilesOrEmpty(repositoryRoot, exclude) {
8761
+ try {
8762
+ return listRepoFiles(repositoryRoot, exclude);
8763
+ } catch {
8764
+ return [];
8765
+ }
8766
+ }
7830
8767
  async function detectLanguages(repositoryRoot, precomputedFiles, exclude) {
7831
8768
  let files;
7832
8769
  if (precomputedFiles) {
@@ -7848,8 +8785,8 @@ async function detectLanguages(repositoryRoot, precomputedFiles, exclude) {
7848
8785
  }
7849
8786
  const detected = /* @__PURE__ */ new Set();
7850
8787
  for (const file of files) {
7851
- const basename = import_path8.default.basename(file);
7852
- const ext = import_path8.default.extname(file).toLowerCase();
8788
+ const basename = import_path9.default.basename(file);
8789
+ const ext = import_path9.default.extname(file).toLowerCase();
7853
8790
  for (const [lang, config] of Object.entries(import_tooling7.LANGUAGE_CONFIG)) {
7854
8791
  if (config.extensions.includes(ext)) {
7855
8792
  detected.add(lang);
@@ -7866,9 +8803,9 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
7866
8803
  adapters.map(async (adapter) => {
7867
8804
  const result = await adapter.checkLocalConfigurationFile({
7868
8805
  repositoryRoot,
7869
- generatedConfigDir: import_path8.default.join(repositoryRoot, ".codacy", "generated"),
7870
- globalDir: import_path8.default.join(process.env.HOME ?? "~", ".codacy"),
7871
- toolInstallDir: import_path8.default.join(process.env.HOME ?? "~", ".codacy", "tools", adapter.id),
8806
+ generatedConfigDir: import_path9.default.join(repositoryRoot, ".codacy", "generated"),
8807
+ globalDir: import_path9.default.join(process.env.HOME ?? "~", ".codacy"),
8808
+ toolInstallDir: import_path9.default.join(process.env.HOME ?? "~", ".codacy", "tools", adapter.id),
7872
8809
  targetFiles: [],
7873
8810
  platform: { os: process.platform, arch: process.arch },
7874
8811
  logger: createNullLogger(),
@@ -7894,7 +8831,7 @@ function enrichWithCodacyYaml(config, adapters, excludes) {
7894
8831
  async function initLocalConfigBase(repositoryRoot, adapters, excludes) {
7895
8832
  const languages = await detectLanguages(
7896
8833
  repositoryRoot,
7897
- listRepoFiles(repositoryRoot, excludes.global)
8834
+ listRepoFilesOrEmpty(repositoryRoot, excludes.global)
7898
8835
  );
7899
8836
  const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
7900
8837
  const tools = [];
@@ -8078,60 +9015,11 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
8078
9015
  }
8079
9016
  };
8080
9017
  }
8081
- async function initContainerConfig(repositoryRoot, adapters) {
8082
- const codacyrcPath = import_path8.default.join(repositoryRoot, ".codacyrc");
8083
- let raw;
8084
- try {
8085
- raw = await import_promises4.default.readFile(codacyrcPath, "utf-8");
8086
- } catch {
8087
- throw new Error(`.codacyrc not found at ${codacyrcPath}`);
8088
- }
8089
- let parsed;
8090
- try {
8091
- parsed = JSON.parse(raw);
8092
- } catch {
8093
- try {
8094
- const decoded = Buffer.from(raw.trim(), "base64").toString("utf-8");
8095
- parsed = JSON.parse(decoded);
8096
- } catch {
8097
- throw new Error(`Failed to parse .codacyrc: not valid JSON or base64-encoded JSON`);
8098
- }
8099
- }
8100
- const includeFiles = Array.isArray(parsed.files) ? parsed.files.filter((f) => typeof f === "string") : [];
8101
- const adapterById = new Map(adapters.map((a) => [a.id, a]));
8102
- const tools = [];
8103
- if (Array.isArray(parsed.tools)) {
8104
- for (const toolEntry of parsed.tools) {
8105
- if (typeof toolEntry !== "object" || toolEntry === null) continue;
8106
- const entry = toolEntry;
8107
- const toolId = typeof entry["toolId"] === "string" ? entry["toolId"] : void 0;
8108
- if (!toolId || !adapterById.has(toolId)) continue;
8109
- const patterns = [];
8110
- if (Array.isArray(entry["patterns"])) {
8111
- for (const p of entry["patterns"]) {
8112
- if (typeof p === "object" && p !== null && typeof p["patternId"] === "string") {
8113
- const pe = p;
8114
- const rawParams = pe["parameters"];
8115
- let params;
8116
- if (rawParams && typeof rawParams === "object" && !Array.isArray(rawParams)) {
8117
- params = Object.fromEntries(
8118
- Object.entries(rawParams).map(([k, v]) => [
8119
- k,
8120
- String(v)
8121
- ])
8122
- );
8123
- }
8124
- patterns.push({
8125
- patternId: pe["patternId"],
8126
- ...params ? { parameters: params } : {}
8127
- });
8128
- }
8129
- }
8130
- }
8131
- tools.push({ toolId, patterns });
8132
- }
8133
- }
8134
- return createCodacyConfig({ source: "container", tools, includeFiles });
9018
+ async function initContainerConfig(repositoryRoot, adapters, codacyrcPath) {
9019
+ const resolvedPath = codacyrcPath ?? import_path9.default.join(repositoryRoot, ".codacyrc");
9020
+ const codacyrc = await readCodacyrc(resolvedPath);
9021
+ const { config } = await resolveCodacyrc(codacyrc, { toolIds: adapters.map((a) => a.id) });
9022
+ return config;
8135
9023
  }
8136
9024
  function hasMatchingFiles(files, filePatterns) {
8137
9025
  const matcher = new CompiledMatcher(filePatterns);
@@ -8152,7 +9040,7 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
8152
9040
  for (const lib of discoveryResult.libraries) detectedKeys.add(lib.key);
8153
9041
  const repoLanguages = new Set(discoveryResult.languages);
8154
9042
  const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
8155
- const repoFiles = listRepoFiles(repositoryRoot, excludes.global);
9043
+ const repoFiles = listRepoFilesOrEmpty(repositoryRoot, excludes.global);
8156
9044
  const descriptorMap = new Map(descriptors.map((d) => [d.id, d]));
8157
9045
  const perToolFiles = /* @__PURE__ */ new Map();
8158
9046
  const filesVisibleTo = (toolId) => {
@@ -8279,12 +9167,12 @@ async function applyAdapterConfigSuggestions(adapter, toolEntry, repositoryRoot,
8279
9167
  }
8280
9168
  };
8281
9169
  try {
8282
- const globalDir = import_path8.default.join(import_os3.default.homedir(), ".codacy");
9170
+ const globalDir = import_path9.default.join(import_os3.default.homedir(), ".codacy");
8283
9171
  const ctx = {
8284
9172
  repositoryRoot,
8285
- generatedConfigDir: import_path8.default.join(repositoryRoot, ".codacy", "generated", adapter.id),
9173
+ generatedConfigDir: import_path9.default.join(repositoryRoot, ".codacy", "generated", adapter.id),
8286
9174
  globalDir,
8287
- toolInstallDir: import_path8.default.join(globalDir, "tools", adapter.id),
9175
+ toolInstallDir: import_path9.default.join(globalDir, "tools", adapter.id),
8288
9176
  targetFiles: routeFilesToTool(repoFiles, adapter, toolEntry),
8289
9177
  platform: { os: process.platform, arch: process.arch },
8290
9178
  logger,
@@ -8545,15 +9433,15 @@ function parseAutoFilters(raw) {
8545
9433
  }
8546
9434
 
8547
9435
  // src/index.ts
8548
- var import_tooling8 = require("@codacy/tooling");
9436
+ var import_tooling9 = require("@codacy/tooling");
8549
9437
 
8550
9438
  // src/credentials.ts
8551
9439
  var import_crypto = __toESM(require("crypto"));
8552
9440
  var import_promises5 = __toESM(require("fs/promises"));
8553
9441
  var import_os4 = __toESM(require("os"));
8554
- var import_path9 = __toESM(require("path"));
9442
+ var import_path10 = __toESM(require("path"));
8555
9443
  function credentialsPath() {
8556
- return import_path9.default.join(import_os4.default.homedir(), ".codacy", "credentials");
9444
+ return import_path10.default.join(import_os4.default.homedir(), ".codacy", "credentials");
8557
9445
  }
8558
9446
  function deriveKey(salt) {
8559
9447
  const info = import_os4.default.userInfo();
@@ -8574,7 +9462,7 @@ async function saveCredentials(token) {
8574
9462
  authTag: authTag.toString("hex"),
8575
9463
  encrypted: encrypted.toString("hex")
8576
9464
  });
8577
- await import_promises5.default.mkdir(import_path9.default.dirname(credentialsFile), { recursive: true });
9465
+ await import_promises5.default.mkdir(import_path10.default.dirname(credentialsFile), { recursive: true });
8578
9466
  await import_promises5.default.writeFile(credentialsFile, payload, { encoding: "utf-8", mode: 384 });
8579
9467
  }
8580
9468
  async function loadCredentials() {
@@ -8875,11 +9763,287 @@ async function safeReadBody(res) {
8875
9763
  return "";
8876
9764
  }
8877
9765
  }
9766
+
9767
+ // src/container-image.ts
9768
+ var import_promises7 = __toESM(require("fs/promises"));
9769
+ var import_os5 = __toESM(require("os"));
9770
+ var import_path11 = __toESM(require("path"));
9771
+ var import_tooling8 = require("@codacy/tooling");
9772
+ var DEFAULT_IMAGE_SCAN_SEVERITIES = ["HIGH", "CRITICAL"];
9773
+ var DEFAULT_IMAGE_SCAN_PKG_TYPES = ["os"];
9774
+ var SBOM_UPLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
9775
+ var PROVIDERS = ["gh", "gl", "bb"];
9776
+ async function scanContainerImage(options) {
9777
+ const startedAt = /* @__PURE__ */ new Date();
9778
+ (0, import_tooling8.validateImageReference)(options.image);
9779
+ const severities = parseVocabulary(
9780
+ options.severities ?? DEFAULT_IMAGE_SCAN_SEVERITIES,
9781
+ import_tooling8.IMAGE_SCAN_SEVERITIES,
9782
+ "severity",
9783
+ (v) => v.toUpperCase()
9784
+ );
9785
+ const pkgTypes = parseVocabulary(
9786
+ options.pkgTypes ?? DEFAULT_IMAGE_SCAN_PKG_TYPES,
9787
+ import_tooling8.IMAGE_SCAN_PKG_TYPES,
9788
+ "package type",
9789
+ (v) => v.toLowerCase()
9790
+ );
9791
+ if (options.signal?.aborted) {
9792
+ return imageResult(startedAt, emptyCapability(), [], [cancelledError("before it started")], []);
9793
+ }
9794
+ const logger = options.logger ?? createNullLogger();
9795
+ const adapter = await resolveImageAdapter();
9796
+ const baseCtx = makeBaseContext(logger, options.signal);
9797
+ const prepared = await prepareTool(adapter, baseCtx, logger);
9798
+ if (!prepared.ok) {
9799
+ return imageResult(startedAt, prepared.capability, [], [prepared.error], []);
9800
+ }
9801
+ if (options.signal?.aborted) {
9802
+ return imageResult(startedAt, prepared.capability, [], [cancelledError("before the scan")], []);
9803
+ }
9804
+ const run = await adapter.containerImage.scan(makeToolContext(baseCtx, adapter, []), {
9805
+ image: options.image,
9806
+ severities,
9807
+ pkgTypes,
9808
+ ignoreUnfixed: options.ignoreUnfixed ?? true
9809
+ });
9810
+ const lookup = /* @__PURE__ */ new Map();
9811
+ for (const entry of adapter.patternsMetadata ?? []) {
9812
+ lookup.set(`${adapter.id}|${entry.id}`, {
9813
+ title: entry.title,
9814
+ toolDisplayName: adapter.displayName
9815
+ });
9816
+ }
9817
+ const patternSummary = run.issues.length > 0 ? computePatternSummary(run.issues, lookup) : void 0;
9818
+ return imageResult(
9819
+ startedAt,
9820
+ prepared.capability,
9821
+ run.issues,
9822
+ run.errors,
9823
+ [run.summary],
9824
+ patternSummary
9825
+ );
9826
+ }
9827
+ async function uploadImageSbom2(options) {
9828
+ const target = (0, import_tooling8.resolveImageTarget)(options.image, options.tag);
9829
+ const format = parseSbomFormat(options.format);
9830
+ if (!PROVIDERS.includes(options.provider)) {
9831
+ throw new Error(
9832
+ `Invalid provider "${options.provider}": expected one of ${PROVIDERS.join(", ")}`
9833
+ );
9834
+ }
9835
+ assertValidCoordinate("organization", options.organization);
9836
+ if (options.repositoryName) assertValidCoordinate("repository", options.repositoryName);
9837
+ if (!options.apiToken) {
9838
+ throw new Error("An account API token is required to upload an SBOM.");
9839
+ }
9840
+ throwIfCancelled(options.signal, "before it started");
9841
+ const logger = options.logger ?? createNullLogger();
9842
+ const progress = options.onProgress ?? (() => {
9843
+ });
9844
+ const adapter = await resolveImageAdapter();
9845
+ const baseCtx = makeBaseContext(logger, options.signal);
9846
+ const prepared = await prepareTool(adapter, baseCtx, logger);
9847
+ if (!prepared.ok) {
9848
+ throw new Error(`Could not install ${adapter.displayName}: ${prepared.error.message}`);
9849
+ }
9850
+ throwIfCancelled(options.signal, "before SBOM generation");
9851
+ const tempDir = await import_promises7.default.mkdtemp(import_path11.default.join(import_os5.default.tmpdir(), "codacy-sbom-"));
9852
+ try {
9853
+ const outputPath = import_path11.default.join(
9854
+ tempDir,
9855
+ format === "cyclonedx" ? "sbom.cdx.json" : "sbom.spdx.json"
9856
+ );
9857
+ progress(`Generating SBOM for image: ${target.scanRef}`);
9858
+ const generated = await adapter.containerImage.generateSbom(
9859
+ makeToolContext(baseCtx, adapter, []),
9860
+ { image: target.scanRef, format, outputPath }
9861
+ );
9862
+ if (!generated.success) {
9863
+ throw new Error(`Failed to generate SBOM: ${generated.error?.message ?? "unknown error"}`);
9864
+ }
9865
+ const contents = await import_promises7.default.readFile(outputPath);
9866
+ const sbom = new File([contents], import_path11.default.basename(outputPath), { type: "application/json" });
9867
+ throwIfCancelled(options.signal, "before the upload");
9868
+ progress(`Uploading SBOM to Codacy (org: ${options.provider}/${options.organization})...`);
9869
+ configureApiToken({ kind: "account-token", token: options.apiToken });
9870
+ const timeout = AbortSignal.timeout(SBOM_UPLOAD_TIMEOUT_MS);
9871
+ try {
9872
+ await uploadImageSbom(
9873
+ options.provider,
9874
+ options.organization,
9875
+ {
9876
+ sbom,
9877
+ imageName: target.name,
9878
+ tag: target.tag,
9879
+ repositoryName: options.repositoryName,
9880
+ environment: options.environment
9881
+ },
9882
+ options.signal ? AbortSignal.any([options.signal, timeout]) : timeout
9883
+ );
9884
+ } catch (err) {
9885
+ throw new Error(`Failed to upload SBOM: ${describeUploadError(err)}`);
9886
+ }
9887
+ return { imageName: target.name, tag: target.tag, format };
9888
+ } finally {
9889
+ await import_promises7.default.rm(tempDir, { recursive: true, force: true });
9890
+ }
9891
+ }
9892
+ function parseVocabulary(values, allowed, label, normalize) {
9893
+ const parsed = values.flatMap((v) => v.split(",")).map((v) => normalize(v.trim())).filter((v) => v.length > 0);
9894
+ if (parsed.length === 0) {
9895
+ throw new Error(`At least one ${label} is required (accepted: ${allowed.join(", ")})`);
9896
+ }
9897
+ for (const value of parsed) {
9898
+ if (!allowed.includes(value)) {
9899
+ throw new Error(`Invalid ${label} "${value}": expected one of ${allowed.join(", ")}`);
9900
+ }
9901
+ }
9902
+ return [...new Set(parsed)];
9903
+ }
9904
+ function parseSbomFormat(format) {
9905
+ const value = format ?? import_tooling8.SBOM_FORMATS[0];
9906
+ if (!import_tooling8.SBOM_FORMATS.includes(value)) {
9907
+ throw new Error(`Invalid format "${value}": expected one of ${import_tooling8.SBOM_FORMATS.join(", ")}`);
9908
+ }
9909
+ return value;
9910
+ }
9911
+ async function resolveImageAdapter() {
9912
+ const descriptor = getRegisteredDescriptors().find(
9913
+ (d) => d.scansContainerImages && !d.unsupported
9914
+ );
9915
+ const adapter = descriptor ? await getAdapter(descriptor.id) : void 0;
9916
+ if (!adapter?.containerImage) {
9917
+ throw new Error(
9918
+ "No registered tool can scan container images. Call registerBuiltinAdapters() first."
9919
+ );
9920
+ }
9921
+ return adapter;
9922
+ }
9923
+ function makeBaseContext(logger, signal) {
9924
+ return {
9925
+ repositoryRoot: process.cwd(),
9926
+ globalDir: import_path11.default.join(import_os5.default.homedir(), ".codacy"),
9927
+ platform: { os: import_os5.default.platform(), arch: import_os5.default.arch() },
9928
+ logger,
9929
+ runner: "local",
9930
+ ...signal ? { signal } : {}
9931
+ };
9932
+ }
9933
+ async function prepareTool(adapter, baseCtx, logger) {
9934
+ const ctx = makeToolContext(baseCtx, adapter, []);
9935
+ const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
9936
+ const availability = await adapter.checkAvailability(ctx);
9937
+ if (availability.available) {
9938
+ const status = {
9939
+ toolId: adapter.id,
9940
+ displayName: adapter.displayName,
9941
+ version: availability.version ?? adapter.preferredVersion,
9942
+ installation: availability.installation ?? "global",
9943
+ runner: "local"
9944
+ };
9945
+ const capability = { ready: [status], unavailable: [], checkedAt };
9946
+ await updateStaleManagedTools(capability, [adapter], baseCtx, logger);
9947
+ return { ok: true, capability };
9948
+ }
9949
+ const outcome = await installMissingTool(adapter, ctx, logger);
9950
+ if (outcome.kind === "installed") {
9951
+ return {
9952
+ ok: true,
9953
+ capability: {
9954
+ ready: [
9955
+ {
9956
+ toolId: adapter.id,
9957
+ displayName: adapter.displayName,
9958
+ version: outcome.version,
9959
+ installation: outcome.installation,
9960
+ runner: "local"
9961
+ }
9962
+ ],
9963
+ unavailable: [],
9964
+ checkedAt
9965
+ }
9966
+ };
9967
+ }
9968
+ const error = outcome.kind === "failed" ? outcome.error : {
9969
+ toolId: adapter.id,
9970
+ phase: "toolInstall",
9971
+ kind: "NotInstalled",
9972
+ message: `${adapter.displayName} is not installed and cannot be installed automatically: ${availability.reason ?? "not found"}`,
9973
+ level: "error"
9974
+ };
9975
+ return {
9976
+ ok: false,
9977
+ error,
9978
+ capability: {
9979
+ ready: [],
9980
+ unavailable: [
9981
+ {
9982
+ toolId: adapter.id,
9983
+ displayName: adapter.displayName,
9984
+ reason: error.message,
9985
+ ...availability.installHint ? { remediation: availability.installHint } : {}
9986
+ }
9987
+ ],
9988
+ checkedAt
9989
+ }
9990
+ };
9991
+ }
9992
+ function emptyCapability() {
9993
+ return { ready: [], unavailable: [], checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
9994
+ }
9995
+ function cancelledError(when) {
9996
+ return {
9997
+ toolId: "",
9998
+ phase: "toolInvoke",
9999
+ kind: "Cancelled",
10000
+ message: `Container image scan cancelled by caller ${when}`,
10001
+ level: "error"
10002
+ };
10003
+ }
10004
+ function throwIfCancelled(signal, when) {
10005
+ if (signal?.aborted) throw new Error(`SBOM upload cancelled by caller ${when}`);
10006
+ }
10007
+ function imageResult(startedAt, capability, issues, errors, toolResults, patternSummary) {
10008
+ const completedAt = /* @__PURE__ */ new Date();
10009
+ return {
10010
+ metadata: {
10011
+ startedAt: startedAt.toISOString(),
10012
+ completedAt: completedAt.toISOString(),
10013
+ durationMs: completedAt.getTime() - startedAt.getTime(),
10014
+ repositoryRoot: process.cwd(),
10015
+ executionMode: "auto-install"
10016
+ },
10017
+ capability,
10018
+ issues,
10019
+ errors,
10020
+ toolResults,
10021
+ ...patternSummary ? { patternSummary } : {}
10022
+ };
10023
+ }
10024
+ function describeUploadError(err) {
10025
+ if (err instanceof Error && err.name === "AbortError") {
10026
+ return "cancelled by caller";
10027
+ }
10028
+ if (err instanceof Error && err.name === "TimeoutError") {
10029
+ return `timed out after ${SBOM_UPLOAD_TIMEOUT_MS / 6e4} minutes`;
10030
+ }
10031
+ const apiError = err;
10032
+ if (typeof apiError?.status === "number") {
10033
+ const body = typeof apiError.body === "string" ? apiError.body : JSON.stringify(apiError.body ?? "");
10034
+ return `API returned status ${apiError.status}${body && body !== '""' ? `: ${body.slice(0, 500)}` : ""}`;
10035
+ }
10036
+ return err instanceof Error ? err.message : String(err);
10037
+ }
8878
10038
  // Annotate the CommonJS export names for ESM import in node:
8879
10039
  0 && (module.exports = {
10040
+ DEFAULT_IMAGE_SCAN_PKG_TYPES,
10041
+ DEFAULT_IMAGE_SCAN_SEVERITIES,
8880
10042
  analyze,
8881
10043
  applyCodacyYamlExcludes,
8882
10044
  applyConfigOperation,
10045
+ buildCodacyrc,
10046
+ buildPatternTranslator,
8883
10047
  buildPayloads,
8884
10048
  buildToolConfig,
8885
10049
  clearAdapters,
@@ -8912,17 +10076,22 @@ async function safeReadBody(res) {
8912
10076
  loadCredentials,
8913
10077
  mergeConfigs,
8914
10078
  parseAutoFilters,
10079
+ parseCodacyrc,
8915
10080
  parseReport,
10081
+ partitionContainerErrors,
8916
10082
  pruneEmptyTools,
8917
10083
  readBaselineConfig,
8918
10084
  readCodacyConfig,
8919
10085
  readCodacyYaml,
10086
+ readCodacyrc,
8920
10087
  registerAdapter,
8921
10088
  registerLazyAdapter,
8922
10089
  registerUnsupportedTool,
10090
+ registryLookup,
8923
10091
  relativizeLocalConfigPaths,
8924
10092
  resolveApiToken,
8925
10093
  resolveBaselinePath,
10094
+ resolveCodacyrc,
8926
10095
  resolveConfigPath,
8927
10096
  resolveDiscoveryExcludes,
8928
10097
  resolveEngineToAdapter,
@@ -8932,9 +10101,14 @@ async function safeReadBody(res) {
8932
10101
  resolveRemoteAuth,
8933
10102
  resolveRepositoryRoot,
8934
10103
  resolveResultsApiBaseUrl,
10104
+ resolveToolName,
8935
10105
  rotateLogFiles,
8936
10106
  saveCredentials,
10107
+ scanContainerImage,
10108
+ serializeCodacyrc,
10109
+ toEnginePatternId,
8937
10110
  updateConfigIncremental,
10111
+ uploadImageSbom,
8938
10112
  uploadReport,
8939
10113
  validateApiToken,
8940
10114
  writeBaselineConfig,