@codacy/analysis-runner 0.23.1 → 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.
- package/dist/index.d.ts +433 -12
- package/dist/index.js +1393 -219
- 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: () =>
|
|
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: () =>
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1161
|
-
cwd: options.repositoryRoot
|
|
1162
|
-
});
|
|
1482
|
+
codacyConfig = options.config ?? await readCodacyConfig(options.repositoryRoot, options.configFile);
|
|
1163
1483
|
} catch (err) {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
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
|
-
|
|
1179
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
logger
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
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
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
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
|
|
1501
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2044
|
+
analyzedSkippedFiles
|
|
1647
2045
|
);
|
|
1648
2046
|
}
|
|
1649
|
-
function
|
|
1650
|
-
return
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
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
|
|
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
|
-
|
|
1929
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
2552
|
-
var
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
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
|
|
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
|
|
4151
|
-
var
|
|
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
|
|
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://
|
|
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
|
|
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}${
|
|
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
|
-
*
|
|
5382
|
-
*
|
|
5383
|
-
*
|
|
5384
|
-
*
|
|
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
|
|
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
|
|
5828
|
+
static recoverRepository(provider, remoteOrganizationName, repositoryName, branch, abortSignal) {
|
|
5390
5829
|
return request(OpenAPI, {
|
|
5391
|
-
method: "
|
|
5392
|
-
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/
|
|
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
|
-
"
|
|
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
|
-
*
|
|
5416
|
-
* **
|
|
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
|
|
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
|
|
5858
|
+
static addAutoconfig(provider, remoteOrganizationName, repositoryName, abortSignal) {
|
|
5422
5859
|
return request(OpenAPI, {
|
|
5423
|
-
method: "
|
|
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), `
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
7852
|
-
const ext =
|
|
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:
|
|
7870
|
-
globalDir:
|
|
7871
|
-
toolInstallDir:
|
|
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
|
-
|
|
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
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
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 =
|
|
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 =
|
|
9170
|
+
const globalDir = import_path9.default.join(import_os3.default.homedir(), ".codacy");
|
|
8283
9171
|
const ctx = {
|
|
8284
9172
|
repositoryRoot,
|
|
8285
|
-
generatedConfigDir:
|
|
9173
|
+
generatedConfigDir: import_path9.default.join(repositoryRoot, ".codacy", "generated", adapter.id),
|
|
8286
9174
|
globalDir,
|
|
8287
|
-
toolInstallDir:
|
|
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
|
|
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
|
|
9442
|
+
var import_path10 = __toESM(require("path"));
|
|
8555
9443
|
function credentialsPath() {
|
|
8556
|
-
return
|
|
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(
|
|
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,
|