@codacy/analysis-runner 0.23.1 → 0.25.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 +492 -12
- package/dist/index.js +1503 -256
- 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_tooling10.configureProxy,
|
|
42
46
|
createCodacyConfig: () => createCodacyConfig,
|
|
43
47
|
createLogger: () => createLogger,
|
|
44
48
|
createNullLogger: () => createNullLogger,
|
|
@@ -57,6 +61,7 @@ __export(index_exports, {
|
|
|
57
61
|
initDefaultConfig: () => initDefaultConfig,
|
|
58
62
|
initLocalConfig: () => initLocalConfig,
|
|
59
63
|
initRemoteConfig: () => initRemoteConfig,
|
|
64
|
+
installTools: () => installTools,
|
|
60
65
|
intersectConfigs: () => intersectConfigs,
|
|
61
66
|
listDiffFiles: () => listDiffFiles,
|
|
62
67
|
listIgnoredFiles: () => listIgnoredFiles,
|
|
@@ -65,29 +70,39 @@ __export(index_exports, {
|
|
|
65
70
|
loadCredentials: () => loadCredentials,
|
|
66
71
|
mergeConfigs: () => mergeConfigs,
|
|
67
72
|
parseAutoFilters: () => parseAutoFilters,
|
|
73
|
+
parseCodacyrc: () => parseCodacyrc,
|
|
68
74
|
parseReport: () => parseReport,
|
|
75
|
+
partitionContainerErrors: () => partitionContainerErrors,
|
|
69
76
|
pruneEmptyTools: () => pruneEmptyTools,
|
|
70
77
|
readBaselineConfig: () => readBaselineConfig,
|
|
71
78
|
readCodacyConfig: () => readCodacyConfig,
|
|
72
79
|
readCodacyYaml: () => readCodacyYaml,
|
|
80
|
+
readCodacyrc: () => readCodacyrc,
|
|
73
81
|
registerAdapter: () => registerAdapter,
|
|
74
82
|
registerLazyAdapter: () => registerLazyAdapter,
|
|
75
83
|
registerUnsupportedTool: () => registerUnsupportedTool,
|
|
84
|
+
registryLookup: () => registryLookup,
|
|
76
85
|
relativizeLocalConfigPaths: () => relativizeLocalConfigPaths,
|
|
77
86
|
resolveApiToken: () => resolveApiToken,
|
|
78
87
|
resolveBaselinePath: () => resolveBaselinePath,
|
|
88
|
+
resolveCodacyrc: () => resolveCodacyrc,
|
|
79
89
|
resolveConfigPath: () => resolveConfigPath,
|
|
80
90
|
resolveDiscoveryExcludes: () => resolveDiscoveryExcludes,
|
|
81
91
|
resolveEngineToAdapter: () => resolveEngineToAdapter,
|
|
82
92
|
resolveHeadCommit: () => resolveHeadCommit,
|
|
83
93
|
resolveLocalConfigPaths: () => resolveLocalConfigPaths,
|
|
84
|
-
resolveProxyConfig: () =>
|
|
94
|
+
resolveProxyConfig: () => import_tooling10.resolveProxyConfig,
|
|
85
95
|
resolveRemoteAuth: () => resolveRemoteAuth,
|
|
86
96
|
resolveRepositoryRoot: () => resolveRepositoryRoot,
|
|
87
97
|
resolveResultsApiBaseUrl: () => resolveResultsApiBaseUrl,
|
|
98
|
+
resolveToolName: () => resolveToolName,
|
|
88
99
|
rotateLogFiles: () => rotateLogFiles,
|
|
89
100
|
saveCredentials: () => saveCredentials,
|
|
101
|
+
scanContainerImage: () => scanContainerImage,
|
|
102
|
+
serializeCodacyrc: () => serializeCodacyrc,
|
|
103
|
+
toEnginePatternId: () => toEnginePatternId,
|
|
90
104
|
updateConfigIncremental: () => updateConfigIncremental,
|
|
105
|
+
uploadImageSbom: () => uploadImageSbom2,
|
|
91
106
|
uploadReport: () => uploadReport,
|
|
92
107
|
validateApiToken: () => validateApiToken,
|
|
93
108
|
writeBaselineConfig: () => writeBaselineConfig,
|
|
@@ -98,7 +113,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
98
113
|
|
|
99
114
|
// src/analyze.ts
|
|
100
115
|
var import_os2 = __toESM(require("os"));
|
|
101
|
-
var
|
|
116
|
+
var import_path5 = __toESM(require("path"));
|
|
102
117
|
var import_fs3 = __toESM(require("fs"));
|
|
103
118
|
var import_child_process3 = require("child_process");
|
|
104
119
|
var import_util = require("util");
|
|
@@ -348,7 +363,7 @@ function createCodacyConfig(params = {}) {
|
|
|
348
363
|
tools: params.tools ?? [],
|
|
349
364
|
exclude: []
|
|
350
365
|
};
|
|
351
|
-
if (params.includeFiles
|
|
366
|
+
if (params.includeFiles !== void 0) {
|
|
352
367
|
config.includeFiles = params.includeFiles;
|
|
353
368
|
}
|
|
354
369
|
return config;
|
|
@@ -602,6 +617,11 @@ function listRepoFiles(repositoryRoot, exclude) {
|
|
|
602
617
|
encoding: "utf-8",
|
|
603
618
|
maxBuffer: 50 * 1024 * 1024
|
|
604
619
|
}).trim();
|
|
620
|
+
const deleted = (0, import_child_process2.execSync)("git ls-files --deleted", {
|
|
621
|
+
cwd: repositoryRoot,
|
|
622
|
+
encoding: "utf-8",
|
|
623
|
+
maxBuffer: 50 * 1024 * 1024
|
|
624
|
+
}).trim();
|
|
605
625
|
const files = /* @__PURE__ */ new Set();
|
|
606
626
|
for (const f of tracked.split("\n")) {
|
|
607
627
|
if (f) files.add(f);
|
|
@@ -609,10 +629,15 @@ function listRepoFiles(repositoryRoot, exclude) {
|
|
|
609
629
|
for (const f of untracked.split("\n")) {
|
|
610
630
|
if (f) files.add(f);
|
|
611
631
|
}
|
|
632
|
+
for (const f of deleted.split("\n")) {
|
|
633
|
+
if (f) files.delete(f);
|
|
634
|
+
}
|
|
612
635
|
const all = Array.from(files).sort();
|
|
613
636
|
return exclude && exclude.length > 0 ? applyExclusions(all, [...exclude], []) : all;
|
|
614
|
-
} catch {
|
|
615
|
-
|
|
637
|
+
} catch (err) {
|
|
638
|
+
throw new Error(
|
|
639
|
+
`Could not list repository files in ${repositoryRoot}: ${err instanceof Error ? err.message : String(err)}`
|
|
640
|
+
);
|
|
616
641
|
}
|
|
617
642
|
}
|
|
618
643
|
function filterOversizedFiles(files, repositoryRoot, logger) {
|
|
@@ -652,6 +677,22 @@ function filterOversizedFiles(files, repositoryRoot, logger) {
|
|
|
652
677
|
}
|
|
653
678
|
return { files: result, skipped };
|
|
654
679
|
}
|
|
680
|
+
function filterSkippedFilesToTools(skipped, tools, globalExclude = []) {
|
|
681
|
+
if (skipped.length === 0 || tools.length === 0) return [];
|
|
682
|
+
const afterGlobal = applyExclusions(
|
|
683
|
+
skipped.map((s) => s.filePath),
|
|
684
|
+
[...globalExclude],
|
|
685
|
+
[]
|
|
686
|
+
);
|
|
687
|
+
if (afterGlobal.length === 0) return [];
|
|
688
|
+
const routable = /* @__PURE__ */ new Set();
|
|
689
|
+
for (const { adapter, toolConfig } of tools) {
|
|
690
|
+
const toolExclude = toolConfig.exclude ?? [];
|
|
691
|
+
const visible = toolExclude.length > 0 ? applyExclusions(afterGlobal, [], [...toolExclude]) : afterGlobal;
|
|
692
|
+
for (const file of routeFilesToTool(visible, adapter, toolConfig)) routable.add(file);
|
|
693
|
+
}
|
|
694
|
+
return skipped.filter((s) => routable.has(s.filePath));
|
|
695
|
+
}
|
|
655
696
|
function applyExclusions(files, globalExclude, toolExclude) {
|
|
656
697
|
if (globalExclude.length === 0 && toolExclude.length === 0) return files;
|
|
657
698
|
const matcher = new CompiledMatcher([...globalExclude, ...toolExclude]);
|
|
@@ -683,6 +724,211 @@ function filterByTargetFiles(allFiles, targetPatterns) {
|
|
|
683
724
|
);
|
|
684
725
|
}
|
|
685
726
|
|
|
727
|
+
// src/codacyrc.ts
|
|
728
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
729
|
+
|
|
730
|
+
// src/pattern-id.ts
|
|
731
|
+
function toEnginePatternId(patternId, toolId) {
|
|
732
|
+
const prefix = `${toolId}_`;
|
|
733
|
+
return patternId.startsWith(prefix) ? patternId.slice(prefix.length) : patternId;
|
|
734
|
+
}
|
|
735
|
+
function buildPatternTranslator(toolId, patternsMetadata) {
|
|
736
|
+
const prefix = `${toolId}_`;
|
|
737
|
+
const byEngineId = /* @__PURE__ */ new Map();
|
|
738
|
+
let prefixedCount = 0;
|
|
739
|
+
let total = 0;
|
|
740
|
+
for (const entry of patternsMetadata ?? []) {
|
|
741
|
+
if (typeof entry?.id !== "string" || entry.id === "") continue;
|
|
742
|
+
total++;
|
|
743
|
+
byEngineId.set(entry.id, entry.id);
|
|
744
|
+
const unprefixed = toEnginePatternId(entry.id, toolId);
|
|
745
|
+
if (unprefixed !== entry.id) {
|
|
746
|
+
byEngineId.set(unprefixed, entry.id);
|
|
747
|
+
prefixedCount++;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const hasCatalogue = total > 0;
|
|
751
|
+
const toolIsPrefixed = hasCatalogue ? prefixedCount === total : true;
|
|
752
|
+
return {
|
|
753
|
+
toolId,
|
|
754
|
+
hasCatalogue,
|
|
755
|
+
isKnown: (enginePatternId) => !hasCatalogue || byEngineId.has(enginePatternId),
|
|
756
|
+
toInternal(enginePatternId) {
|
|
757
|
+
const known = byEngineId.get(enginePatternId);
|
|
758
|
+
if (known) return known;
|
|
759
|
+
if (!toolIsPrefixed) return enginePatternId;
|
|
760
|
+
return enginePatternId.startsWith(prefix) ? enginePatternId : prefix + enginePatternId;
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/codacyrc.ts
|
|
766
|
+
function registryLookup() {
|
|
767
|
+
return { descriptors: getRegisteredDescriptors(), load: getAdapter };
|
|
768
|
+
}
|
|
769
|
+
function parseCodacyrc(raw, label = ".codacyrc") {
|
|
770
|
+
let parsed;
|
|
771
|
+
try {
|
|
772
|
+
parsed = JSON.parse(raw);
|
|
773
|
+
} catch {
|
|
774
|
+
try {
|
|
775
|
+
parsed = JSON.parse(Buffer.from(raw.trim(), "base64").toString("utf-8"));
|
|
776
|
+
} catch {
|
|
777
|
+
throw new Error(`Failed to parse ${label}: not valid JSON or base64-encoded JSON`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
781
|
+
throw new Error(`Failed to parse ${label}: expected a JSON object`);
|
|
782
|
+
}
|
|
783
|
+
return parsed;
|
|
784
|
+
}
|
|
785
|
+
async function readCodacyrc(filePath) {
|
|
786
|
+
let raw;
|
|
787
|
+
try {
|
|
788
|
+
raw = await import_promises2.default.readFile(filePath, "utf-8");
|
|
789
|
+
} catch (err) {
|
|
790
|
+
if (err?.code === "ENOENT") return null;
|
|
791
|
+
throw err;
|
|
792
|
+
}
|
|
793
|
+
return parseCodacyrc(raw, filePath);
|
|
794
|
+
}
|
|
795
|
+
function resolveToolName(name, descriptors) {
|
|
796
|
+
const runnable = descriptors.filter((d) => !d.unsupported);
|
|
797
|
+
const lower = name.toLowerCase();
|
|
798
|
+
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);
|
|
799
|
+
}
|
|
800
|
+
function toParameterRecord(parameters) {
|
|
801
|
+
const out = {};
|
|
802
|
+
if (!Array.isArray(parameters)) return out;
|
|
803
|
+
for (const parameter of parameters) {
|
|
804
|
+
if (!parameter || typeof parameter.name !== "string" || parameter.name === "") continue;
|
|
805
|
+
const { value } = parameter;
|
|
806
|
+
if (value === null || value === void 0) continue;
|
|
807
|
+
out[parameter.name] = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
808
|
+
}
|
|
809
|
+
return out;
|
|
810
|
+
}
|
|
811
|
+
function withSpecDefaults(entry, configured) {
|
|
812
|
+
const defaults = {};
|
|
813
|
+
for (const spec of entry?.parameters ?? []) {
|
|
814
|
+
if (typeof spec?.name !== "string" || spec.name === "") continue;
|
|
815
|
+
if (spec.default === void 0 || spec.default === null) continue;
|
|
816
|
+
defaults[spec.name] = String(spec.default);
|
|
817
|
+
}
|
|
818
|
+
return { ...defaults, ...configured };
|
|
819
|
+
}
|
|
820
|
+
async function resolveCodacyrc(codacyrc, opts = {}) {
|
|
821
|
+
const lookup = opts.lookup ?? registryLookup();
|
|
822
|
+
const logger = opts.logger;
|
|
823
|
+
const allowed = opts.toolIds && opts.toolIds.length > 0 ? new Set(opts.toolIds.map((id) => id.toLowerCase())) : void 0;
|
|
824
|
+
const descriptors = allowed ? lookup.descriptors.filter((d) => allowed.has(d.id.toLowerCase())) : lookup.descriptors;
|
|
825
|
+
const tools = [];
|
|
826
|
+
const matchedToolIds = [];
|
|
827
|
+
const unmatchedToolNames = [];
|
|
828
|
+
const unknownPatterns = [];
|
|
829
|
+
for (const entry of codacyrc?.tools ?? []) {
|
|
830
|
+
if (typeof entry !== "object" || entry === null || typeof entry.name !== "string") continue;
|
|
831
|
+
const descriptor = resolveToolName(entry.name, descriptors);
|
|
832
|
+
if (!descriptor) {
|
|
833
|
+
unmatchedToolNames.push(entry.name);
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (matchedToolIds.includes(descriptor.id)) continue;
|
|
837
|
+
const adapter = await lookup.load(descriptor.id);
|
|
838
|
+
const translator = buildPatternTranslator(descriptor.id, adapter?.patternsMetadata);
|
|
839
|
+
const byId = new Map((adapter?.patternsMetadata ?? []).map((p) => [p.id, p]));
|
|
840
|
+
const patterns = [];
|
|
841
|
+
for (const raw of entry.patterns ?? []) {
|
|
842
|
+
if (typeof raw !== "object" || raw === null || typeof raw.patternId !== "string") continue;
|
|
843
|
+
const translated = translator.toInternal(raw.patternId);
|
|
844
|
+
if (!translator.isKnown(raw.patternId)) {
|
|
845
|
+
unknownPatterns.push({ toolId: descriptor.id, patternId: raw.patternId });
|
|
846
|
+
}
|
|
847
|
+
const parameters = withSpecDefaults(byId.get(translated), toParameterRecord(raw.parameters));
|
|
848
|
+
patterns.push({
|
|
849
|
+
patternId: translated,
|
|
850
|
+
...Object.keys(parameters).length > 0 ? { parameters } : {}
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
tools.push({ toolId: descriptor.id, patterns });
|
|
854
|
+
matchedToolIds.push(descriptor.id);
|
|
855
|
+
}
|
|
856
|
+
if (unmatchedToolNames.length > 0) {
|
|
857
|
+
logger?.warn(
|
|
858
|
+
`.codacyrc names ${unmatchedToolNames.length} tool(s) with no local analyzer here: ${unmatchedToolNames.join(", ")}`
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
for (const unknown of unknownPatterns) {
|
|
862
|
+
logger?.warn(
|
|
863
|
+
`.codacyrc enables "${unknown.patternId}", which ${unknown.toolId} does not ship; passing it through`
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
const files = Array.isArray(codacyrc?.files) ? codacyrc.files.filter((f) => typeof f === "string") : void 0;
|
|
867
|
+
const config = createCodacyConfig({ source: "container", tools, includeFiles: files });
|
|
868
|
+
return {
|
|
869
|
+
config,
|
|
870
|
+
matchedToolIds,
|
|
871
|
+
unmatchedToolNames,
|
|
872
|
+
unknownPatterns,
|
|
873
|
+
options: codacyrc?.options ?? {}
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function toWireValue(raw, spec) {
|
|
877
|
+
const declared = spec?.default;
|
|
878
|
+
if (typeof declared === "number") {
|
|
879
|
+
const parsed = Number(raw);
|
|
880
|
+
return raw.trim() !== "" && Number.isFinite(parsed) ? parsed : raw;
|
|
881
|
+
}
|
|
882
|
+
if (typeof declared === "boolean") {
|
|
883
|
+
if (raw === "true") return true;
|
|
884
|
+
if (raw === "false") return false;
|
|
885
|
+
return raw;
|
|
886
|
+
}
|
|
887
|
+
if (declared !== null && typeof declared === "object") {
|
|
888
|
+
try {
|
|
889
|
+
return JSON.parse(raw);
|
|
890
|
+
} catch {
|
|
891
|
+
return raw;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return raw;
|
|
895
|
+
}
|
|
896
|
+
function toParameterList(entry, configured) {
|
|
897
|
+
const merged = withSpecDefaults(entry, configured ?? {});
|
|
898
|
+
const names = Object.keys(merged).sort();
|
|
899
|
+
if (names.length === 0) return void 0;
|
|
900
|
+
const specByName = /* @__PURE__ */ new Map();
|
|
901
|
+
for (const spec of entry?.parameters ?? []) {
|
|
902
|
+
if (typeof spec?.name === "string" && spec.name !== "") specByName.set(spec.name, spec);
|
|
903
|
+
}
|
|
904
|
+
return names.map((name) => ({ name, value: toWireValue(merged[name], specByName.get(name)) }));
|
|
905
|
+
}
|
|
906
|
+
function buildCodacyrc(tools, opts = {}) {
|
|
907
|
+
const files = /* @__PURE__ */ new Set();
|
|
908
|
+
const wireTools = [];
|
|
909
|
+
for (const { adapter, toolConfig, files: routed } of tools) {
|
|
910
|
+
for (const file of routed) files.add(file);
|
|
911
|
+
if (!adapter.shortName) {
|
|
912
|
+
opts.logger?.warn(
|
|
913
|
+
`${adapter.id} declares no Codacy short-name; emitting "${adapter.id}" as tools[].name, which a Codacy engine image will not recognize`
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
const byId = new Map((adapter.patternsMetadata ?? []).map((p) => [p.id, p]));
|
|
917
|
+
const patterns = toolConfig.useLocalConfigurationFile ? [] : (toolConfig.patterns ?? []).map((pattern) => {
|
|
918
|
+
const parameters = toParameterList(byId.get(pattern.patternId), pattern.parameters);
|
|
919
|
+
return {
|
|
920
|
+
patternId: toEnginePatternId(pattern.patternId, adapter.id),
|
|
921
|
+
...parameters ? { parameters } : {}
|
|
922
|
+
};
|
|
923
|
+
});
|
|
924
|
+
wireTools.push({ name: adapter.shortName ?? adapter.id, patterns });
|
|
925
|
+
}
|
|
926
|
+
return { files: [...files].sort(), tools: wireTools };
|
|
927
|
+
}
|
|
928
|
+
function serializeCodacyrc(codacyrc) {
|
|
929
|
+
return JSON.stringify(codacyrc, null, 2) + "\n";
|
|
930
|
+
}
|
|
931
|
+
|
|
686
932
|
// src/executor.ts
|
|
687
933
|
var import_tooling2 = require("@codacy/tooling");
|
|
688
934
|
var HARD_TIMEOUT_GRACE_MS = 2e3;
|
|
@@ -1081,6 +1327,81 @@ function computePatternSummary(issues, lookup) {
|
|
|
1081
1327
|
};
|
|
1082
1328
|
}
|
|
1083
1329
|
|
|
1330
|
+
// src/tool-install.ts
|
|
1331
|
+
var import_path4 = __toESM(require("path"));
|
|
1332
|
+
function makeToolContext(base, adapter, targetFiles, generatedRoot) {
|
|
1333
|
+
return {
|
|
1334
|
+
...base,
|
|
1335
|
+
targetFiles,
|
|
1336
|
+
toolInstallDir: import_path4.default.join(base.globalDir, "tools", adapter.id),
|
|
1337
|
+
generatedConfigDir: import_path4.default.join(
|
|
1338
|
+
generatedRoot ?? import_path4.default.join(base.repositoryRoot, ".codacy", "generated"),
|
|
1339
|
+
adapter.id
|
|
1340
|
+
)
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
async function installMissingTool(adapter, ctx, logger) {
|
|
1344
|
+
logger.info(`${adapter.displayName} is missing, attempting install...`);
|
|
1345
|
+
try {
|
|
1346
|
+
logger.info(`Downloading ${adapter.displayName}...`);
|
|
1347
|
+
const installResult = await adapter.install(ctx);
|
|
1348
|
+
if (installResult.success) {
|
|
1349
|
+
logger.info(`Checking ${adapter.displayName} installation...`);
|
|
1350
|
+
const recheck = await adapter.checkAvailability(ctx);
|
|
1351
|
+
if (recheck.available) {
|
|
1352
|
+
logger.info(`${adapter.displayName} is now available`);
|
|
1353
|
+
return {
|
|
1354
|
+
kind: "installed",
|
|
1355
|
+
version: recheck.version ?? installResult.installedVersion ?? adapter.preferredVersion,
|
|
1356
|
+
installation: recheck.installation ?? "codacy"
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
logger.error(
|
|
1360
|
+
`${adapter.displayName} post-install check failed: ${recheck.reason ?? "unknown"}`
|
|
1361
|
+
);
|
|
1362
|
+
return {
|
|
1363
|
+
kind: "failed",
|
|
1364
|
+
error: {
|
|
1365
|
+
toolId: adapter.id,
|
|
1366
|
+
phase: "toolInstall",
|
|
1367
|
+
kind: "PostInstallCheckFailed",
|
|
1368
|
+
message: `Installed ${adapter.id} but availability check still fails: ${recheck.reason ?? "unknown"}`,
|
|
1369
|
+
level: "error"
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
if (installResult.error?.kind === "NotSupported") {
|
|
1374
|
+
logger.info(`${adapter.displayName} does not support automated install, skipping.`);
|
|
1375
|
+
return { kind: "notSupported" };
|
|
1376
|
+
}
|
|
1377
|
+
const errMsg = installResult.error?.message ?? `Failed to install ${adapter.id}`;
|
|
1378
|
+
logger.error(`${adapter.displayName} install failed: ${errMsg}`);
|
|
1379
|
+
return {
|
|
1380
|
+
kind: "failed",
|
|
1381
|
+
error: installResult.error ?? {
|
|
1382
|
+
toolId: adapter.id,
|
|
1383
|
+
phase: "toolInstall",
|
|
1384
|
+
kind: "InstallFailed",
|
|
1385
|
+
message: errMsg,
|
|
1386
|
+
level: "error"
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
} catch (err) {
|
|
1390
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1391
|
+
logger.error(`${adapter.displayName} install failed: ${errMsg}`);
|
|
1392
|
+
return {
|
|
1393
|
+
kind: "failed",
|
|
1394
|
+
error: {
|
|
1395
|
+
toolId: adapter.id,
|
|
1396
|
+
phase: "toolInstall",
|
|
1397
|
+
kind: "InstallError",
|
|
1398
|
+
message: `Install failed for ${adapter.id}: ${errMsg}`,
|
|
1399
|
+
level: "error"
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1084
1405
|
// src/analyze.ts
|
|
1085
1406
|
var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
|
|
1086
1407
|
var CHECK_CONCURRENCY = 12;
|
|
@@ -1111,7 +1432,7 @@ async function analyze(options) {
|
|
|
1111
1432
|
toolExecutionMs: 0,
|
|
1112
1433
|
installMs: 0
|
|
1113
1434
|
};
|
|
1114
|
-
const
|
|
1435
|
+
const emptyCapability2 = {
|
|
1115
1436
|
ready: [],
|
|
1116
1437
|
unavailable: [],
|
|
1117
1438
|
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -1122,7 +1443,7 @@ async function analyze(options) {
|
|
|
1122
1443
|
startedAt,
|
|
1123
1444
|
options.repositoryRoot,
|
|
1124
1445
|
mode,
|
|
1125
|
-
|
|
1446
|
+
emptyCapability2,
|
|
1126
1447
|
[],
|
|
1127
1448
|
[
|
|
1128
1449
|
{
|
|
@@ -1142,7 +1463,7 @@ async function analyze(options) {
|
|
|
1142
1463
|
startedAt,
|
|
1143
1464
|
options.repositoryRoot,
|
|
1144
1465
|
mode,
|
|
1145
|
-
|
|
1466
|
+
emptyCapability2,
|
|
1146
1467
|
[],
|
|
1147
1468
|
[
|
|
1148
1469
|
{
|
|
@@ -1156,34 +1477,42 @@ async function analyze(options) {
|
|
|
1156
1477
|
[]
|
|
1157
1478
|
);
|
|
1158
1479
|
}
|
|
1480
|
+
let codacyConfig;
|
|
1481
|
+
let configError;
|
|
1159
1482
|
try {
|
|
1160
|
-
await
|
|
1161
|
-
cwd: options.repositoryRoot
|
|
1162
|
-
});
|
|
1483
|
+
codacyConfig = options.config ?? await readCodacyConfig(options.repositoryRoot, options.configFile);
|
|
1163
1484
|
} catch (err) {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1485
|
+
codacyConfig = null;
|
|
1486
|
+
configError = err;
|
|
1487
|
+
}
|
|
1488
|
+
if (configError === void 0 && codacyConfig?.includeFiles === void 0) {
|
|
1489
|
+
try {
|
|
1490
|
+
await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
1491
|
+
cwd: options.repositoryRoot
|
|
1492
|
+
});
|
|
1493
|
+
} catch (err) {
|
|
1494
|
+
const isGitMissing = err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
1495
|
+
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.`;
|
|
1496
|
+
const kind = isGitMissing ? "GitNotInstalled" : "NotAGitRepository";
|
|
1497
|
+
return buildResult(
|
|
1498
|
+
startedAt,
|
|
1499
|
+
options.repositoryRoot,
|
|
1500
|
+
mode,
|
|
1501
|
+
emptyCapability2,
|
|
1502
|
+
[],
|
|
1503
|
+
[{ toolId: "runner", phase: "requirementCheck", kind, message, level: "error" }],
|
|
1504
|
+
[]
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1176
1507
|
}
|
|
1177
1508
|
const configLabel = options.configFile ?? ".codacy/codacy.config.json";
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
codacyConfig = await readCodacyConfig(options.repositoryRoot, options.configFile);
|
|
1181
|
-
} catch (err) {
|
|
1509
|
+
if (configError !== void 0) {
|
|
1510
|
+
const err = configError;
|
|
1182
1511
|
return buildResult(
|
|
1183
1512
|
startedAt,
|
|
1184
1513
|
options.repositoryRoot,
|
|
1185
1514
|
mode,
|
|
1186
|
-
|
|
1515
|
+
emptyCapability2,
|
|
1187
1516
|
[],
|
|
1188
1517
|
[
|
|
1189
1518
|
{
|
|
@@ -1202,7 +1531,7 @@ async function analyze(options) {
|
|
|
1202
1531
|
startedAt,
|
|
1203
1532
|
options.repositoryRoot,
|
|
1204
1533
|
mode,
|
|
1205
|
-
|
|
1534
|
+
emptyCapability2,
|
|
1206
1535
|
[],
|
|
1207
1536
|
[
|
|
1208
1537
|
{
|
|
@@ -1219,7 +1548,7 @@ async function analyze(options) {
|
|
|
1219
1548
|
const requestedTools = options.tools ?? codacyConfig.tools.map((t) => t.toolId);
|
|
1220
1549
|
const adapters = await getRegisteredAdapters(requestedTools);
|
|
1221
1550
|
logger.info("Pipeline started", { mode, toolCount: adapters.length, parallelTools, toolTimeout });
|
|
1222
|
-
const globalDir =
|
|
1551
|
+
const globalDir = (0, import_tooling3.resolveGlobalDir)();
|
|
1223
1552
|
const runnerEnv = options.runner ?? "local";
|
|
1224
1553
|
const baseCtx = {
|
|
1225
1554
|
repositoryRoot: options.repositoryRoot,
|
|
@@ -1266,7 +1595,7 @@ async function analyze(options) {
|
|
|
1266
1595
|
displayName: adapter.displayName,
|
|
1267
1596
|
version: availability.version ?? adapter.preferredVersion,
|
|
1268
1597
|
installation: availability.installation ?? "global",
|
|
1269
|
-
runner:
|
|
1598
|
+
runner: runnerEnv
|
|
1270
1599
|
}
|
|
1271
1600
|
};
|
|
1272
1601
|
}
|
|
@@ -1366,6 +1695,25 @@ async function analyze(options) {
|
|
|
1366
1695
|
logger.info(`Updated ${updatedCount} managed tool(s) to their pinned versions`);
|
|
1367
1696
|
}
|
|
1368
1697
|
}
|
|
1698
|
+
if (mode === "auto-install" && runnerEnv === "container") {
|
|
1699
|
+
return buildResult(
|
|
1700
|
+
startedAt,
|
|
1701
|
+
options.repositoryRoot,
|
|
1702
|
+
mode,
|
|
1703
|
+
capability,
|
|
1704
|
+
[],
|
|
1705
|
+
[
|
|
1706
|
+
{
|
|
1707
|
+
toolId: "runner",
|
|
1708
|
+
phase: "requirementCheck",
|
|
1709
|
+
kind: "InstallNotAllowed",
|
|
1710
|
+
message: "Installs are disabled under the container runner; the image must ship its tools.",
|
|
1711
|
+
level: "error"
|
|
1712
|
+
}
|
|
1713
|
+
],
|
|
1714
|
+
[]
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1369
1717
|
if (mode === "auto-install" && capability.unavailable.length > 0) {
|
|
1370
1718
|
const installStartMs = Date.now();
|
|
1371
1719
|
const installErrors = [];
|
|
@@ -1373,62 +1721,24 @@ async function analyze(options) {
|
|
|
1373
1721
|
const status = capability.unavailable[i];
|
|
1374
1722
|
const adapter = adapters.find((a) => a.id === status.toolId);
|
|
1375
1723
|
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({
|
|
1724
|
+
const outcome = await installMissingTool(
|
|
1725
|
+
adapter,
|
|
1726
|
+
makeToolContext(baseCtx, adapter, []),
|
|
1727
|
+
logger
|
|
1728
|
+
);
|
|
1729
|
+
if (outcome.kind === "installed") {
|
|
1730
|
+
capability.ready.push({
|
|
1426
1731
|
toolId: adapter.id,
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1732
|
+
displayName: adapter.displayName,
|
|
1733
|
+
version: outcome.version,
|
|
1734
|
+
installation: outcome.installation,
|
|
1735
|
+
runner: runnerEnv
|
|
1431
1736
|
});
|
|
1737
|
+
capability.unavailable.splice(i, 1);
|
|
1738
|
+
} else if (outcome.kind === "notSupported") {
|
|
1739
|
+
capability.unavailable.splice(i, 1);
|
|
1740
|
+
} else {
|
|
1741
|
+
installErrors.push(outcome.error);
|
|
1432
1742
|
}
|
|
1433
1743
|
}
|
|
1434
1744
|
phaseTimings.installMs = Date.now() - installStartMs;
|
|
@@ -1497,8 +1807,59 @@ async function analyze(options) {
|
|
|
1497
1807
|
}
|
|
1498
1808
|
const fileDiscoveryStartMs = Date.now();
|
|
1499
1809
|
const globalExclude = codacyConfig.exclude || [];
|
|
1500
|
-
let allFiles
|
|
1501
|
-
|
|
1810
|
+
let allFiles;
|
|
1811
|
+
const runnerErrors = [];
|
|
1812
|
+
if (codacyConfig.includeFiles === void 0) {
|
|
1813
|
+
try {
|
|
1814
|
+
allFiles = listRepoFiles(options.repositoryRoot);
|
|
1815
|
+
} catch (err) {
|
|
1816
|
+
return buildResult(
|
|
1817
|
+
startedAt,
|
|
1818
|
+
options.repositoryRoot,
|
|
1819
|
+
mode,
|
|
1820
|
+
capability,
|
|
1821
|
+
[],
|
|
1822
|
+
[
|
|
1823
|
+
{
|
|
1824
|
+
toolId: "runner",
|
|
1825
|
+
phase: "requirementCheck",
|
|
1826
|
+
kind: "FileDiscoveryFailed",
|
|
1827
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1828
|
+
level: "error"
|
|
1829
|
+
}
|
|
1830
|
+
],
|
|
1831
|
+
[]
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1834
|
+
logger.debug("File discovery", { source: "git", totalFiles: allFiles.length });
|
|
1835
|
+
} else {
|
|
1836
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1837
|
+
allFiles = [];
|
|
1838
|
+
for (const raw of codacyConfig.includeFiles) {
|
|
1839
|
+
const rel = raw.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1840
|
+
if (rel === "" || seen.has(rel)) continue;
|
|
1841
|
+
seen.add(rel);
|
|
1842
|
+
const absolute = import_path5.default.resolve(options.repositoryRoot, rel);
|
|
1843
|
+
const relativeToRoot = import_path5.default.relative(options.repositoryRoot, absolute);
|
|
1844
|
+
const escapesRoot = relativeToRoot === ".." || relativeToRoot.startsWith(`..${import_path5.default.sep}`) || import_path5.default.isAbsolute(relativeToRoot);
|
|
1845
|
+
const stat = escapesRoot ? void 0 : import_fs3.default.statSync(absolute, { throwIfNoEntry: false });
|
|
1846
|
+
if (!stat?.isFile()) {
|
|
1847
|
+
runnerErrors.push({
|
|
1848
|
+
toolId: "runner",
|
|
1849
|
+
phase: "requirementCheck",
|
|
1850
|
+
kind: "FileNotFound",
|
|
1851
|
+
message: escapesRoot ? `${rel} was requested for analysis but is outside the repository` : `${rel} was requested for analysis but does not exist`,
|
|
1852
|
+
level: "warning",
|
|
1853
|
+
filePath: rel,
|
|
1854
|
+
fileNotAnalyzed: true
|
|
1855
|
+
});
|
|
1856
|
+
continue;
|
|
1857
|
+
}
|
|
1858
|
+
allFiles.push(rel);
|
|
1859
|
+
}
|
|
1860
|
+
allFiles.sort();
|
|
1861
|
+
logger.debug("File discovery", { source: "config", totalFiles: allFiles.length });
|
|
1862
|
+
}
|
|
1502
1863
|
if (options.gitScope) {
|
|
1503
1864
|
const scopeFiles = resolveGitScope(options.repositoryRoot, options.gitScope);
|
|
1504
1865
|
logger.debug("Git scope resolved", {
|
|
@@ -1507,7 +1868,7 @@ async function analyze(options) {
|
|
|
1507
1868
|
});
|
|
1508
1869
|
if (scopeFiles.length === 0) {
|
|
1509
1870
|
logger.info("Git scope resolved to zero files, skipping analysis");
|
|
1510
|
-
return buildResult(startedAt, options.repositoryRoot, mode, capability, [],
|
|
1871
|
+
return buildResult(startedAt, options.repositoryRoot, mode, capability, [], runnerErrors, []);
|
|
1511
1872
|
}
|
|
1512
1873
|
const scopeSet = new Set(scopeFiles);
|
|
1513
1874
|
allFiles = allFiles.filter((f) => scopeSet.has(f));
|
|
@@ -1524,6 +1885,8 @@ async function analyze(options) {
|
|
|
1524
1885
|
const fileRoutingStartMs = Date.now();
|
|
1525
1886
|
const readyById = new Map(capability.ready.map((r) => [r.toolId, r]));
|
|
1526
1887
|
const readyAdapters = adapters.filter((a) => readyById.has(a.id));
|
|
1888
|
+
const generatedRoot = resolveGeneratedRoot(options.repositoryRoot, runnerEnv, logger);
|
|
1889
|
+
const generatedRootIsTemporary = runnerEnv === "container";
|
|
1527
1890
|
const filesAfterGlobalExclude = applyExclusions(allFiles, globalExclude, []);
|
|
1528
1891
|
const toolRuns = [];
|
|
1529
1892
|
const candidates = [];
|
|
@@ -1540,7 +1903,7 @@ async function analyze(options) {
|
|
|
1540
1903
|
candidates.push({
|
|
1541
1904
|
adapter,
|
|
1542
1905
|
toolFiles,
|
|
1543
|
-
configCtx: makeToolContext(baseCtx, adapter, toolFiles),
|
|
1906
|
+
configCtx: makeToolContext(baseCtx, adapter, toolFiles, generatedRoot),
|
|
1544
1907
|
readyEntry
|
|
1545
1908
|
});
|
|
1546
1909
|
}
|
|
@@ -1588,6 +1951,32 @@ async function analyze(options) {
|
|
|
1588
1951
|
toolRuns.push({ adapter, ctx: configCtx, codacyToolConfig });
|
|
1589
1952
|
}
|
|
1590
1953
|
phaseTimings.fileRoutingMs = Date.now() - fileRoutingStartMs;
|
|
1954
|
+
const analyzedSkippedFiles = filterSkippedFilesToTools(
|
|
1955
|
+
skippedFiles,
|
|
1956
|
+
toolRuns.map(({ adapter, codacyToolConfig }) => ({
|
|
1957
|
+
adapter,
|
|
1958
|
+
toolConfig: codacyToolConfig
|
|
1959
|
+
})),
|
|
1960
|
+
globalExclude
|
|
1961
|
+
);
|
|
1962
|
+
if (options.onCodacyrc && toolRuns.length > 0) {
|
|
1963
|
+
if (toolRuns.length > 1) {
|
|
1964
|
+
logger.info("Emitting .codacyrc for multiple tools", {
|
|
1965
|
+
toolCount: toolRuns.length,
|
|
1966
|
+
hint: "`files` is the union of their routed lists; use --tool to pin one"
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
await options.onCodacyrc(
|
|
1970
|
+
buildCodacyrc(
|
|
1971
|
+
toolRuns.map(({ adapter, ctx, codacyToolConfig }) => ({
|
|
1972
|
+
adapter,
|
|
1973
|
+
toolConfig: codacyToolConfig,
|
|
1974
|
+
files: ctx.targetFiles
|
|
1975
|
+
})),
|
|
1976
|
+
{ logger }
|
|
1977
|
+
)
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1591
1980
|
const toolExecStartMs = Date.now();
|
|
1592
1981
|
logger.info("Starting tool execution", {
|
|
1593
1982
|
toolCount: toolRuns.length,
|
|
@@ -1613,7 +2002,17 @@ async function analyze(options) {
|
|
|
1613
2002
|
}
|
|
1614
2003
|
phaseTimings.toolExecutionMs = Date.now() - toolExecStartMs;
|
|
1615
2004
|
const allIssues = normalizeIssues(results.flatMap((r) => r.issues));
|
|
1616
|
-
|
|
2005
|
+
if (generatedRootIsTemporary) {
|
|
2006
|
+
try {
|
|
2007
|
+
import_fs3.default.rmSync(generatedRoot, { recursive: true, force: true });
|
|
2008
|
+
} catch (err) {
|
|
2009
|
+
logger.debug("Could not remove the temporary generated-config directory", {
|
|
2010
|
+
path: generatedRoot,
|
|
2011
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
const allErrors = [...runnerErrors, ...results.flatMap((r) => r.errors)];
|
|
1617
2016
|
const allSummaries = results.map((r) => r.summary);
|
|
1618
2017
|
const patternLookup = /* @__PURE__ */ new Map();
|
|
1619
2018
|
for (const { adapter } of toolRuns) {
|
|
@@ -1643,16 +2042,14 @@ async function analyze(options) {
|
|
|
1643
2042
|
allSummaries,
|
|
1644
2043
|
patternSummary,
|
|
1645
2044
|
phaseTimings,
|
|
1646
|
-
|
|
2045
|
+
analyzedSkippedFiles
|
|
1647
2046
|
);
|
|
1648
2047
|
}
|
|
1649
|
-
function
|
|
1650
|
-
return
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
generatedConfigDir: import_path4.default.join(base.repositoryRoot, ".codacy", "generated", adapter.id)
|
|
1655
|
-
};
|
|
2048
|
+
function resolveGeneratedRoot(repositoryRoot, runnerEnv, logger) {
|
|
2049
|
+
if (runnerEnv !== "container") return import_path5.default.join(repositoryRoot, ".codacy", "generated");
|
|
2050
|
+
const dir = import_fs3.default.mkdtempSync(import_path5.default.join(import_os2.default.tmpdir(), "codacy-generated-"));
|
|
2051
|
+
logger.info("Generated tool configs will be written outside the source directory", { path: dir });
|
|
2052
|
+
return dir;
|
|
1656
2053
|
}
|
|
1657
2054
|
async function updateStaleManagedTools(capability, adapters, baseCtx, logger) {
|
|
1658
2055
|
const adaptersById = new Map(adapters.map((a) => [a.id, a]));
|
|
@@ -1662,7 +2059,7 @@ async function updateStaleManagedTools(capability, adapters, baseCtx, logger) {
|
|
|
1662
2059
|
if (!adapter) continue;
|
|
1663
2060
|
if (status.installation === "bundled") continue;
|
|
1664
2061
|
if (status.version === adapter.preferredVersion) continue;
|
|
1665
|
-
if (status.installation === "global") {
|
|
2062
|
+
if (status.installation === "global" || status.installation === "preinstalled") {
|
|
1666
2063
|
status.expectedVersion = adapter.preferredVersion;
|
|
1667
2064
|
logger.debug("System tool version differs from the pinned version", {
|
|
1668
2065
|
toolId: adapter.id,
|
|
@@ -1758,6 +2155,8 @@ async function repairMissingManagedTools(capability, adapters, baseCtx, logger)
|
|
|
1758
2155
|
displayName: adapter.displayName,
|
|
1759
2156
|
version: recheck.version ?? installResult.installedVersion ?? adapter.preferredVersion,
|
|
1760
2157
|
installation: recheck.installation ?? "codacy",
|
|
2158
|
+
// Always local: this repair pass is skipped entirely under the container
|
|
2159
|
+
// runner, which never installs anything.
|
|
1761
2160
|
runner: "local"
|
|
1762
2161
|
});
|
|
1763
2162
|
capability.unavailable.splice(i, 1);
|
|
@@ -1807,9 +2206,81 @@ function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors
|
|
|
1807
2206
|
};
|
|
1808
2207
|
}
|
|
1809
2208
|
|
|
1810
|
-
// src/
|
|
1811
|
-
var
|
|
2209
|
+
// src/install-tools.ts
|
|
2210
|
+
var import_os3 = __toESM(require("os"));
|
|
1812
2211
|
var import_tooling4 = require("@codacy/tooling");
|
|
2212
|
+
function supportsThisPlatform(adapter) {
|
|
2213
|
+
return adapter.supportedOS.includes(import_os3.default.platform()) && adapter.supportedArch.includes(import_os3.default.arch());
|
|
2214
|
+
}
|
|
2215
|
+
async function installTools(options) {
|
|
2216
|
+
const logger = options.logger ?? createNullLogger();
|
|
2217
|
+
const unknown = [];
|
|
2218
|
+
let adapters;
|
|
2219
|
+
if (options.all) {
|
|
2220
|
+
adapters = await getRegisteredAdapters();
|
|
2221
|
+
} else {
|
|
2222
|
+
const requested = options.tools ?? [];
|
|
2223
|
+
for (const id of requested) {
|
|
2224
|
+
const match = getRegisteredDescriptors([id]);
|
|
2225
|
+
if (match.length === 0 || match[0].unsupported) unknown.push(id);
|
|
2226
|
+
}
|
|
2227
|
+
const known = requested.filter((id) => !unknown.includes(id));
|
|
2228
|
+
adapters = known.length > 0 ? await getRegisteredAdapters(known) : [];
|
|
2229
|
+
}
|
|
2230
|
+
const base = {
|
|
2231
|
+
repositoryRoot: process.cwd(),
|
|
2232
|
+
globalDir: (0, import_tooling4.resolveGlobalDir)(),
|
|
2233
|
+
platform: { os: import_os3.default.platform(), arch: import_os3.default.arch() },
|
|
2234
|
+
logger,
|
|
2235
|
+
runner: "local",
|
|
2236
|
+
...options.onProgress ? { onProgress: options.onProgress } : {}
|
|
2237
|
+
};
|
|
2238
|
+
const tools = [];
|
|
2239
|
+
for (const adapter of adapters) {
|
|
2240
|
+
const report = {
|
|
2241
|
+
toolId: adapter.id,
|
|
2242
|
+
displayName: adapter.displayName,
|
|
2243
|
+
status: "failed"
|
|
2244
|
+
};
|
|
2245
|
+
tools.push(report);
|
|
2246
|
+
if (!supportsThisPlatform(adapter)) {
|
|
2247
|
+
report.status = "unsupportedPlatform";
|
|
2248
|
+
report.message = `${adapter.displayName} does not support ${import_os3.default.platform()}-${import_os3.default.arch()}`;
|
|
2249
|
+
continue;
|
|
2250
|
+
}
|
|
2251
|
+
const ctx = makeToolContext(base, adapter, []);
|
|
2252
|
+
try {
|
|
2253
|
+
const availability = await adapter.checkAvailability(ctx);
|
|
2254
|
+
if (availability.available) {
|
|
2255
|
+
report.status = "present";
|
|
2256
|
+
report.version = availability.version;
|
|
2257
|
+
report.installation = availability.installation;
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
} catch (err) {
|
|
2261
|
+
logger.debug("Availability check threw before install", {
|
|
2262
|
+
toolId: adapter.id,
|
|
2263
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
const outcome = await installMissingTool(adapter, ctx, logger);
|
|
2267
|
+
if (outcome.kind === "installed") {
|
|
2268
|
+
report.status = "installed";
|
|
2269
|
+
report.version = outcome.version;
|
|
2270
|
+
report.installation = outcome.installation;
|
|
2271
|
+
} else if (outcome.kind === "notSupported") {
|
|
2272
|
+
report.status = "notSupported";
|
|
2273
|
+
report.message = `${adapter.displayName} cannot be installed automatically; install it on PATH`;
|
|
2274
|
+
} else {
|
|
2275
|
+
report.message = outcome.error.message;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
return { tools, unknown };
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
// src/format.ts
|
|
2282
|
+
var import_path6 = __toESM(require("path"));
|
|
2283
|
+
var import_tooling5 = require("@codacy/tooling");
|
|
1813
2284
|
var import_ansis = __toESM(require("ansis"));
|
|
1814
2285
|
var import_cli_table3 = __toESM(require("cli-table3"));
|
|
1815
2286
|
var MAX_SKIPPED_FILES_SHOWN = 5;
|
|
@@ -1924,17 +2395,48 @@ function mapSeverityToSarif(severity) {
|
|
|
1924
2395
|
return "warning";
|
|
1925
2396
|
}
|
|
1926
2397
|
}
|
|
2398
|
+
function partitionContainerErrors(errors) {
|
|
2399
|
+
const fileErrors = [];
|
|
2400
|
+
const otherErrors = [];
|
|
2401
|
+
for (const error of errors) {
|
|
2402
|
+
if (error.fileNotAnalyzed && error.filePath) fileErrors.push(error);
|
|
2403
|
+
else otherErrors.push(error);
|
|
2404
|
+
}
|
|
2405
|
+
return { fileErrors, otherErrors };
|
|
2406
|
+
}
|
|
1927
2407
|
function formatContainer(result) {
|
|
1928
|
-
|
|
1929
|
-
|
|
2408
|
+
const lines = [];
|
|
2409
|
+
const fileErrors = /* @__PURE__ */ new Map();
|
|
2410
|
+
for (const error of partitionContainerErrors(result.errors).fileErrors) {
|
|
2411
|
+
const filename = error.filePath;
|
|
2412
|
+
if (fileErrors.has(filename)) continue;
|
|
2413
|
+
fileErrors.set(filename, flattenErrorMessage(error.message) || void 0);
|
|
2414
|
+
}
|
|
2415
|
+
for (const skipped of result.skippedFiles ?? []) {
|
|
2416
|
+
if (fileErrors.has(skipped.filePath)) continue;
|
|
2417
|
+
fileErrors.set(
|
|
2418
|
+
skipped.filePath,
|
|
2419
|
+
`File skipped: ${skipped.sizeBytes} bytes exceeds the ${MAX_FILE_SIZE_BYTES}-byte limit`
|
|
2420
|
+
);
|
|
2421
|
+
}
|
|
2422
|
+
for (const issue of result.issues) {
|
|
2423
|
+
if (issue.line <= 0) {
|
|
2424
|
+
if (!fileErrors.has(issue.filePath)) {
|
|
2425
|
+
fileErrors.set(issue.filePath, import_tooling5.LINE_NUMBERS_NOT_SUPPORTED);
|
|
2426
|
+
}
|
|
2427
|
+
continue;
|
|
2428
|
+
}
|
|
2429
|
+
lines.push({
|
|
1930
2430
|
filename: issue.filePath,
|
|
1931
|
-
patternId: issue.patternId,
|
|
2431
|
+
patternId: toEnginePatternId(issue.patternId, issue.toolId),
|
|
1932
2432
|
line: issue.line,
|
|
1933
2433
|
message: issue.message,
|
|
1934
2434
|
suggestion: issue.suggestion,
|
|
1935
2435
|
sourceId: issue.sourceId
|
|
1936
|
-
})
|
|
1937
|
-
|
|
2436
|
+
});
|
|
2437
|
+
}
|
|
2438
|
+
for (const [filename, message] of fileErrors) lines.push({ filename, message });
|
|
2439
|
+
return lines.map((line) => JSON.stringify(line)).join("\n");
|
|
1938
2440
|
}
|
|
1939
2441
|
function formatText(result) {
|
|
1940
2442
|
const lines = [];
|
|
@@ -2015,7 +2517,7 @@ function formatTextSummary(result, isInspect) {
|
|
|
2015
2517
|
} else {
|
|
2016
2518
|
const headerLine = `Analysis complete: ${totalIssues} issue${totalIssues !== 1 ? "s" : ""} found`;
|
|
2017
2519
|
lines.push(totalIssues > 0 ? import_ansis.default.bold.red(headerLine) : import_ansis.default.bold(headerLine));
|
|
2018
|
-
lines.push(`${toolCount} tool${toolCount !== 1 ? "s" : ""} ran in ${(0,
|
|
2520
|
+
lines.push(`${toolCount} tool${toolCount !== 1 ? "s" : ""} ran in ${(0, import_tooling5.formatDuration)(duration)}`);
|
|
2019
2521
|
}
|
|
2020
2522
|
if (totalErrors > 0) {
|
|
2021
2523
|
lines.push(import_ansis.default.red(`${totalErrors} error${totalErrors !== 1 ? "s" : ""} occurred`));
|
|
@@ -2077,7 +2579,7 @@ function formatTextSummary(result, isInspect) {
|
|
|
2077
2579
|
formatConfigCell(tool, result.metadata.repositoryRoot),
|
|
2078
2580
|
filesCell,
|
|
2079
2581
|
issueCount > 0 ? import_ansis.default.bold.red(String(issueCount)) : String(issueCount),
|
|
2080
|
-
(0,
|
|
2582
|
+
(0, import_tooling5.formatDuration)(durationMs)
|
|
2081
2583
|
]);
|
|
2082
2584
|
}
|
|
2083
2585
|
for (const tool of unavailableTools) {
|
|
@@ -2118,7 +2620,7 @@ function formatTextSummary(result, isInspect) {
|
|
|
2118
2620
|
entries2.push(["Other", other]);
|
|
2119
2621
|
}
|
|
2120
2622
|
for (const [label, ms] of entries2) {
|
|
2121
|
-
lines.push(` ${label.padEnd(22)} ${(0,
|
|
2623
|
+
lines.push(` ${label.padEnd(22)} ${(0, import_tooling5.formatDuration)(ms)}`);
|
|
2122
2624
|
}
|
|
2123
2625
|
}
|
|
2124
2626
|
}
|
|
@@ -2235,7 +2737,7 @@ function formatMultilineIssue(output, issue) {
|
|
|
2235
2737
|
}
|
|
2236
2738
|
function formatInstallCell(tool) {
|
|
2237
2739
|
if (!tool.expectedVersion) return tool.installation;
|
|
2238
|
-
const mismatch = (0,
|
|
2740
|
+
const mismatch = (0, import_tooling5.describeVersionMismatch)(tool.version, tool.expectedVersion);
|
|
2239
2741
|
if (!mismatch) return tool.installation;
|
|
2240
2742
|
return `${tool.installation} ${import_ansis.default.yellow(`(${mismatch})`)}`;
|
|
2241
2743
|
}
|
|
@@ -2246,7 +2748,7 @@ function formatFilesCell(filesRouted) {
|
|
|
2246
2748
|
}
|
|
2247
2749
|
function formatConfigCell(tool, repositoryRoot) {
|
|
2248
2750
|
if (tool.configSource === "local" && tool.configPath) {
|
|
2249
|
-
const rawPath =
|
|
2751
|
+
const rawPath = import_path6.default.isAbsolute(tool.configPath) ? import_path6.default.relative(repositoryRoot, tool.configPath) : tool.configPath;
|
|
2250
2752
|
return rawPath.startsWith("./") ? rawPath : `./${rawPath}`;
|
|
2251
2753
|
}
|
|
2252
2754
|
if (tool.configSource === "codacy") return "Codacy";
|
|
@@ -2541,18 +3043,16 @@ function updateConfigIncremental(base, current, next) {
|
|
|
2541
3043
|
}
|
|
2542
3044
|
|
|
2543
3045
|
// src/init.ts
|
|
2544
|
-
var
|
|
2545
|
-
var import_os3 = __toESM(require("os"));
|
|
2546
|
-
var import_path8 = __toESM(require("path"));
|
|
3046
|
+
var import_path9 = __toESM(require("path"));
|
|
2547
3047
|
var import_child_process4 = require("child_process");
|
|
2548
|
-
var
|
|
3048
|
+
var import_tooling8 = require("@codacy/tooling");
|
|
2549
3049
|
|
|
2550
3050
|
// src/discover/index.ts
|
|
2551
|
-
var
|
|
2552
|
-
var
|
|
3051
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
3052
|
+
var import_path7 = __toESM(require("path"));
|
|
2553
3053
|
|
|
2554
3054
|
// src/discover/classify.ts
|
|
2555
|
-
var
|
|
3055
|
+
var import_tooling7 = require("@codacy/tooling");
|
|
2556
3056
|
|
|
2557
3057
|
// src/discover/ecosystems/npm.ts
|
|
2558
3058
|
function parsePackageJson(content) {
|
|
@@ -3687,7 +4187,7 @@ function resolveManifest(filePath) {
|
|
|
3687
4187
|
}
|
|
3688
4188
|
|
|
3689
4189
|
// src/discover/signals.ts
|
|
3690
|
-
var
|
|
4190
|
+
var import_tooling6 = require("@codacy/tooling");
|
|
3691
4191
|
function pathBasename(filePath) {
|
|
3692
4192
|
return filePath.split("/").pop() ?? filePath;
|
|
3693
4193
|
}
|
|
@@ -3888,7 +4388,7 @@ function detectFileSignals(files) {
|
|
|
3888
4388
|
for (const rule of FILE_SIGNAL_RULES) {
|
|
3889
4389
|
const hit = files.find((f) => rule.matches(f));
|
|
3890
4390
|
if (!hit) continue;
|
|
3891
|
-
const entity =
|
|
4391
|
+
const entity = import_tooling6.DEPENDENCY_ENTITIES[rule.entity];
|
|
3892
4392
|
result.push({
|
|
3893
4393
|
name: rule.name,
|
|
3894
4394
|
key: entity.key,
|
|
@@ -3912,7 +4412,7 @@ function detectPackageJsonSignals(content) {
|
|
|
3912
4412
|
}
|
|
3913
4413
|
const name = pkg && typeof pkg.name === "string" ? pkg.name : "";
|
|
3914
4414
|
if (name && isEslintPluginName(name)) {
|
|
3915
|
-
const entity =
|
|
4415
|
+
const entity = import_tooling6.DEPENDENCY_ENTITIES["eslint-plugin"];
|
|
3916
4416
|
return [
|
|
3917
4417
|
{
|
|
3918
4418
|
name,
|
|
@@ -3940,14 +4440,14 @@ function telltaleIndex(ecosystem) {
|
|
|
3940
4440
|
}
|
|
3941
4441
|
function classifyDependency(name, ecosystem) {
|
|
3942
4442
|
const exact = telltaleIndex(ecosystem).get(name);
|
|
3943
|
-
if (exact) return
|
|
4443
|
+
if (exact) return import_tooling7.DEPENDENCY_ENTITIES[exact];
|
|
3944
4444
|
for (const [prefix, entity] of getEcosystem(ecosystem).prefixes ?? []) {
|
|
3945
|
-
if (name.startsWith(prefix)) return
|
|
4445
|
+
if (name.startsWith(prefix)) return import_tooling7.DEPENDENCY_ENTITIES[entity];
|
|
3946
4446
|
}
|
|
3947
4447
|
return void 0;
|
|
3948
4448
|
}
|
|
3949
4449
|
function getKnownDisplayNames() {
|
|
3950
|
-
return new Set(Object.values(
|
|
4450
|
+
return new Set(Object.values(import_tooling7.DEPENDENCY_ENTITIES).map((entity) => entity.displayName));
|
|
3951
4451
|
}
|
|
3952
4452
|
|
|
3953
4453
|
// src/discover/notable-files.ts
|
|
@@ -4049,7 +4549,12 @@ function isNotableFile(filePath) {
|
|
|
4049
4549
|
|
|
4050
4550
|
// src/discover/index.ts
|
|
4051
4551
|
async function discover(repositoryRoot, options) {
|
|
4052
|
-
|
|
4552
|
+
let unfiltered;
|
|
4553
|
+
try {
|
|
4554
|
+
unfiltered = listRepoFiles(repositoryRoot);
|
|
4555
|
+
} catch {
|
|
4556
|
+
unfiltered = [];
|
|
4557
|
+
}
|
|
4053
4558
|
const files = applyExclusions(unfiltered, [...options?.exclude ?? []], []);
|
|
4054
4559
|
const excludedFiles = unfiltered.length - files.length;
|
|
4055
4560
|
const filePatterns = extractFilePatterns(files);
|
|
@@ -4072,7 +4577,7 @@ async function discover(repositoryRoot, options) {
|
|
|
4072
4577
|
frameworks,
|
|
4073
4578
|
libraries,
|
|
4074
4579
|
metadata: {
|
|
4075
|
-
repositoryRoot:
|
|
4580
|
+
repositoryRoot: import_path7.default.resolve(repositoryRoot),
|
|
4076
4581
|
totalFiles: files.length,
|
|
4077
4582
|
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4078
4583
|
// Surfaced so a scan narrowed by excludes is never mistaken for a repo
|
|
@@ -4084,7 +4589,7 @@ async function discover(repositoryRoot, options) {
|
|
|
4084
4589
|
function extractFilePatterns(files) {
|
|
4085
4590
|
const extensions = /* @__PURE__ */ new Set();
|
|
4086
4591
|
for (const file of files) {
|
|
4087
|
-
const ext =
|
|
4592
|
+
const ext = import_path7.default.extname(file).toLowerCase();
|
|
4088
4593
|
if (ext) {
|
|
4089
4594
|
extensions.add(`*${ext}`);
|
|
4090
4595
|
}
|
|
@@ -4107,7 +4612,7 @@ async function extractDependencies(repositoryRoot, files) {
|
|
|
4107
4612
|
if (!manifest) continue;
|
|
4108
4613
|
let content;
|
|
4109
4614
|
try {
|
|
4110
|
-
content = await
|
|
4615
|
+
content = await import_promises3.default.readFile(import_path7.default.join(repositoryRoot, filePath), "utf-8");
|
|
4111
4616
|
} catch {
|
|
4112
4617
|
continue;
|
|
4113
4618
|
}
|
|
@@ -4147,15 +4652,15 @@ function deduplicateDeps(deps) {
|
|
|
4147
4652
|
}
|
|
4148
4653
|
|
|
4149
4654
|
// src/codacy-yaml.ts
|
|
4150
|
-
var
|
|
4151
|
-
var
|
|
4655
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
4656
|
+
var import_path8 = __toESM(require("path"));
|
|
4152
4657
|
var import_yaml = require("yaml");
|
|
4153
4658
|
async function readCodacyYaml(repositoryRoot) {
|
|
4154
4659
|
const candidates = [".codacy.yaml", ".codacy.yml"];
|
|
4155
4660
|
let raw = null;
|
|
4156
4661
|
for (const filename of candidates) {
|
|
4157
4662
|
try {
|
|
4158
|
-
raw = await
|
|
4663
|
+
raw = await import_promises4.default.readFile(import_path8.default.join(repositoryRoot, filename), "utf-8");
|
|
4159
4664
|
break;
|
|
4160
4665
|
} catch {
|
|
4161
4666
|
}
|
|
@@ -4256,7 +4761,7 @@ async function resolveDiscoveryExcludes(repositoryRoot, adapters, existingConfig
|
|
|
4256
4761
|
|
|
4257
4762
|
// src/api/client/core/OpenAPI.ts
|
|
4258
4763
|
var OpenAPI = {
|
|
4259
|
-
BASE: "https://
|
|
4764
|
+
BASE: "https://api.codacy.com/api/v3",
|
|
4260
4765
|
VERSION: "3.1.0",
|
|
4261
4766
|
WITH_CREDENTIALS: false,
|
|
4262
4767
|
CREDENTIALS: "include",
|
|
@@ -4435,13 +4940,13 @@ var getQueryString = (params) => {
|
|
|
4435
4940
|
};
|
|
4436
4941
|
var getUrl = (config, options) => {
|
|
4437
4942
|
const encoder = config.ENCODE_PATH || encodeURI;
|
|
4438
|
-
const
|
|
4943
|
+
const path12 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
|
|
4439
4944
|
if (options.path?.hasOwnProperty(group)) {
|
|
4440
4945
|
return encoder(String(options.path[group]));
|
|
4441
4946
|
}
|
|
4442
4947
|
return substring;
|
|
4443
4948
|
});
|
|
4444
|
-
const url = `${config.BASE}${
|
|
4949
|
+
const url = `${config.BASE}${path12}`;
|
|
4445
4950
|
if (options.query) {
|
|
4446
4951
|
return `${url}${getQueryString(options.query)}`;
|
|
4447
4952
|
}
|
|
@@ -5176,10 +5681,10 @@ var AnalysisService = class {
|
|
|
5176
5681
|
* Patterns configuration for (repository, tool). Uses standard if applied, repository settings otherwise.
|
|
5177
5682
|
*
|
|
5178
5683
|
* @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
|
|
5684
|
+
* * @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
5685
|
* @throws ApiError
|
|
5181
5686
|
*/
|
|
5182
|
-
static listRepositoryToolPatterns(provider, remoteOrganizationName, repositoryName, toolUuid, languages, categories, severityLevels, tags, search, enabled, recommended, sort, direction, cursor, limit = 100, abortSignal) {
|
|
5687
|
+
static listRepositoryToolPatterns(provider, remoteOrganizationName, repositoryName, toolUuid, languages, categories, severityLevels, tags, search, enabled, recommended, matchesStack, sort, direction, cursor, limit = 100, abortSignal) {
|
|
5183
5688
|
return request(OpenAPI, {
|
|
5184
5689
|
method: "GET",
|
|
5185
5690
|
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tools/{toolUuid}/patterns",
|
|
@@ -5197,6 +5702,7 @@ var AnalysisService = class {
|
|
|
5197
5702
|
"search": search,
|
|
5198
5703
|
"enabled": enabled,
|
|
5199
5704
|
"recommended": recommended,
|
|
5705
|
+
"matchesStack": matchesStack,
|
|
5200
5706
|
"sort": sort,
|
|
5201
5707
|
"direction": direction,
|
|
5202
5708
|
"cursor": cursor,
|
|
@@ -5218,10 +5724,10 @@ var AnalysisService = class {
|
|
|
5218
5724
|
* Use filters to specify the code patterns to update, or omit the filters to update all code patterns.
|
|
5219
5725
|
*
|
|
5220
5726
|
* @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
|
|
5727
|
+
* * @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
5728
|
* @throws ApiError
|
|
5223
5729
|
*/
|
|
5224
|
-
static updateRepositoryToolPatterns(provider, remoteOrganizationName, repositoryName, toolUuid, requestBody, languages, categories, severityLevels, tags, search, recommended, abortSignal) {
|
|
5730
|
+
static updateRepositoryToolPatterns(provider, remoteOrganizationName, repositoryName, toolUuid, requestBody, languages, categories, severityLevels, tags, search, recommended, matchesStack, abortSignal) {
|
|
5225
5731
|
return request(OpenAPI, {
|
|
5226
5732
|
method: "PATCH",
|
|
5227
5733
|
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tools/{toolUuid}/patterns",
|
|
@@ -5237,7 +5743,8 @@ var AnalysisService = class {
|
|
|
5237
5743
|
"severityLevels": severityLevels,
|
|
5238
5744
|
"tags": tags,
|
|
5239
5745
|
"search": search,
|
|
5240
|
-
"recommended": recommended
|
|
5746
|
+
"recommended": recommended,
|
|
5747
|
+
"matchesStack": matchesStack
|
|
5241
5748
|
},
|
|
5242
5749
|
abortSignal,
|
|
5243
5750
|
body: requestBody,
|
|
@@ -5285,10 +5792,10 @@ var AnalysisService = class {
|
|
|
5285
5792
|
* Patterns overview for tool. Uses standard if applied, repository settings otherwise.
|
|
5286
5793
|
*
|
|
5287
5794
|
* @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
|
|
5795
|
+
* * @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
5796
|
* @throws ApiError
|
|
5290
5797
|
*/
|
|
5291
|
-
static toolPatternsOverview(provider, remoteOrganizationName, repositoryName, toolUuid, languages, categories, severityLevels, tags, search, enabled, recommended, abortSignal) {
|
|
5798
|
+
static toolPatternsOverview(provider, remoteOrganizationName, repositoryName, toolUuid, languages, categories, severityLevels, tags, search, enabled, recommended, matchesStack, abortSignal) {
|
|
5292
5799
|
return request(OpenAPI, {
|
|
5293
5800
|
method: "GET",
|
|
5294
5801
|
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tools/{toolUuid}/patterns/overview",
|
|
@@ -5305,7 +5812,8 @@ var AnalysisService = class {
|
|
|
5305
5812
|
"tags": tags,
|
|
5306
5813
|
"search": search,
|
|
5307
5814
|
"enabled": enabled,
|
|
5308
|
-
"recommended": recommended
|
|
5815
|
+
"recommended": recommended,
|
|
5816
|
+
"matchesStack": matchesStack
|
|
5309
5817
|
},
|
|
5310
5818
|
abortSignal,
|
|
5311
5819
|
errors: {
|
|
@@ -5377,16 +5885,134 @@ var AnalysisService = class {
|
|
|
5377
5885
|
}
|
|
5378
5886
|
});
|
|
5379
5887
|
}
|
|
5888
|
+
/**
|
|
5889
|
+
* Recover a stuck repository
|
|
5890
|
+
* Triggers a recovery action when the repository is stuck on its first analysis.
|
|
5891
|
+
* The exact action taken depends on the stuck state.
|
|
5892
|
+
* Returns `422 Unprocessable Entity` when the repository is not stuck.
|
|
5893
|
+
*
|
|
5894
|
+
* @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/),
|
|
5895
|
+
* as returned by the endpoint [listRepositoryBranches](#listrepositorybranches).
|
|
5896
|
+
* By default, uses the main branch defined on the Codacy repository settings.
|
|
5897
|
+
* * @returns void
|
|
5898
|
+
* @throws ApiError
|
|
5899
|
+
*/
|
|
5900
|
+
static recoverRepository(provider, remoteOrganizationName, repositoryName, branch, abortSignal) {
|
|
5901
|
+
return request(OpenAPI, {
|
|
5902
|
+
method: "POST",
|
|
5903
|
+
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/recover",
|
|
5904
|
+
path: {
|
|
5905
|
+
"provider": provider,
|
|
5906
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
5907
|
+
"repositoryName": repositoryName
|
|
5908
|
+
},
|
|
5909
|
+
query: {
|
|
5910
|
+
"branch": branch
|
|
5911
|
+
},
|
|
5912
|
+
abortSignal,
|
|
5913
|
+
errors: {
|
|
5914
|
+
400: `Bad Request`,
|
|
5915
|
+
401: `Unauthorized`,
|
|
5916
|
+
403: `Forbidden`,
|
|
5917
|
+
404: `Not Found`,
|
|
5918
|
+
422: `Unprocessable Entity`,
|
|
5919
|
+
500: `Internal Server Error`
|
|
5920
|
+
}
|
|
5921
|
+
});
|
|
5922
|
+
}
|
|
5923
|
+
/**
|
|
5924
|
+
* Add repository autoconfiguration run
|
|
5925
|
+
* **[Experimental]** Autoconfig enqueuing endpoint
|
|
5926
|
+
*
|
|
5927
|
+
* @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
|
|
5928
|
+
* @throws ApiError
|
|
5929
|
+
*/
|
|
5930
|
+
static addAutoconfig(provider, remoteOrganizationName, repositoryName, abortSignal) {
|
|
5931
|
+
return request(OpenAPI, {
|
|
5932
|
+
method: "POST",
|
|
5933
|
+
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/autoconfig",
|
|
5934
|
+
path: {
|
|
5935
|
+
"provider": provider,
|
|
5936
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
5937
|
+
"repositoryName": repositoryName
|
|
5938
|
+
},
|
|
5939
|
+
abortSignal,
|
|
5940
|
+
errors: {
|
|
5941
|
+
400: `Bad Request`,
|
|
5942
|
+
401: `Unauthorized`,
|
|
5943
|
+
403: `Forbidden`,
|
|
5944
|
+
404: `Not Found`,
|
|
5945
|
+
422: `Unprocessable Entity`,
|
|
5946
|
+
429: `Too Many Requests`,
|
|
5947
|
+
500: `Internal Server Error`
|
|
5948
|
+
}
|
|
5949
|
+
});
|
|
5950
|
+
}
|
|
5951
|
+
/**
|
|
5952
|
+
* Get latest autoconfig run summary
|
|
5953
|
+
* @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
|
|
5954
|
+
* @throws ApiError
|
|
5955
|
+
*/
|
|
5956
|
+
static getLatestAutoconfigRunSummary(provider, remoteOrganizationName, repositoryName, abortSignal) {
|
|
5957
|
+
return request(OpenAPI, {
|
|
5958
|
+
method: "GET",
|
|
5959
|
+
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/autoconfig/runs",
|
|
5960
|
+
path: {
|
|
5961
|
+
"provider": provider,
|
|
5962
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
5963
|
+
"repositoryName": repositoryName
|
|
5964
|
+
},
|
|
5965
|
+
abortSignal,
|
|
5966
|
+
errors: {
|
|
5967
|
+
400: `Bad Request`,
|
|
5968
|
+
401: `Unauthorized`,
|
|
5969
|
+
403: `Forbidden`,
|
|
5970
|
+
404: `Not Found`,
|
|
5971
|
+
500: `Internal Server Error`
|
|
5972
|
+
}
|
|
5973
|
+
});
|
|
5974
|
+
}
|
|
5975
|
+
/**
|
|
5976
|
+
* Get autoconfig run status
|
|
5977
|
+
* **[Experimental]** Fetch latest autoconfig run state managed (can be `Requested`, `Running`, `Done` or `Failed`)
|
|
5978
|
+
*
|
|
5979
|
+
* @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
|
|
5980
|
+
* @throws ApiError
|
|
5981
|
+
*/
|
|
5982
|
+
static getAutoconfigStatus(provider, remoteOrganizationName, repositoryName, abortSignal) {
|
|
5983
|
+
return request(OpenAPI, {
|
|
5984
|
+
method: "GET",
|
|
5985
|
+
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/autoconfig/status",
|
|
5986
|
+
path: {
|
|
5987
|
+
"provider": provider,
|
|
5988
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
5989
|
+
"repositoryName": repositoryName
|
|
5990
|
+
},
|
|
5991
|
+
abortSignal,
|
|
5992
|
+
errors: {
|
|
5993
|
+
400: `Bad Request`,
|
|
5994
|
+
401: `Unauthorized`,
|
|
5995
|
+
403: `Forbidden`,
|
|
5996
|
+
404: `Not Found`,
|
|
5997
|
+
500: `Internal Server Error`
|
|
5998
|
+
}
|
|
5999
|
+
});
|
|
6000
|
+
}
|
|
5380
6001
|
/**
|
|
5381
6002
|
* List pull requests from a repository that the user has access to
|
|
5382
|
-
* You can search this endpoint for either `last-updated` (default), `
|
|
6003
|
+
* You can search this endpoint for either `last-updated` (default), `merged`
|
|
5383
6004
|
*
|
|
5384
6005
|
* **Note:** When applied to public repositories, this operation does not require authentication.
|
|
5385
6006
|
*
|
|
5386
|
-
* @param provider Git provider identifier (e.g., gh for GitHub, gl for GitLab, bb for Bitbucket)* @param remoteOrganizationName Organization name on the Git provider* @param repositoryName Repository name on the Git provider organization* @param limit Maximum number of items to return* @param cursor Cursor to [specify a batch of results to request](https://docs.codacy.com/codacy-api/using-the-codacy-api/#using-pagination)* @param search Filter results by searching for this string* @param
|
|
6007
|
+
* @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
|
|
6008
|
+
* pull request title or the author's handle.
|
|
6009
|
+
* * @param targetBranch Filter pull requests by the name of their target branch.
|
|
6010
|
+
* Pull requests targeting any other branch are excluded from the results.
|
|
6011
|
+
* By default, pull requests targeting any branch are returned.
|
|
6012
|
+
* * @param includeNotAnalyzed If true, also return pull requests that weren't analyzed * @returns PullRequestWithAnalysisListResponse Successful operation
|
|
5387
6013
|
* @throws ApiError
|
|
5388
6014
|
*/
|
|
5389
|
-
static listRepositoryPullRequests(provider, remoteOrganizationName, repositoryName, limit = 100, cursor, search, includeNotAnalyzed = false, abortSignal) {
|
|
6015
|
+
static listRepositoryPullRequests(provider, remoteOrganizationName, repositoryName, limit = 100, cursor, search, textQuery, targetBranch, includeNotAnalyzed = false, abortSignal) {
|
|
5390
6016
|
return request(OpenAPI, {
|
|
5391
6017
|
method: "GET",
|
|
5392
6018
|
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/pull-requests",
|
|
@@ -5399,6 +6025,8 @@ var AnalysisService = class {
|
|
|
5399
6025
|
"limit": limit,
|
|
5400
6026
|
"cursor": cursor,
|
|
5401
6027
|
"search": search,
|
|
6028
|
+
"textQuery": textQuery,
|
|
6029
|
+
"targetBranch": targetBranch,
|
|
5402
6030
|
"includeNotAnalyzed": includeNotAnalyzed
|
|
5403
6031
|
},
|
|
5404
6032
|
abortSignal,
|
|
@@ -5494,6 +6122,33 @@ var AnalysisService = class {
|
|
|
5494
6122
|
}
|
|
5495
6123
|
});
|
|
5496
6124
|
}
|
|
6125
|
+
/**
|
|
6126
|
+
* Triggers an AI review for a pull request
|
|
6127
|
+
* @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
|
|
6128
|
+
* @throws ApiError
|
|
6129
|
+
*/
|
|
6130
|
+
static triggerPullRequestAiReview(provider, remoteOrganizationName, repositoryName, pullRequestNumber, abortSignal) {
|
|
6131
|
+
return request(OpenAPI, {
|
|
6132
|
+
method: "POST",
|
|
6133
|
+
url: "/analysis/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/pull-requests/{pullRequestNumber}/ai-reviewer/trigger",
|
|
6134
|
+
path: {
|
|
6135
|
+
"provider": provider,
|
|
6136
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
6137
|
+
"repositoryName": repositoryName,
|
|
6138
|
+
"pullRequestNumber": pullRequestNumber
|
|
6139
|
+
},
|
|
6140
|
+
abortSignal,
|
|
6141
|
+
errors: {
|
|
6142
|
+
400: `Bad Request`,
|
|
6143
|
+
401: `Unauthorized`,
|
|
6144
|
+
403: `Forbidden`,
|
|
6145
|
+
404: `Not Found`,
|
|
6146
|
+
409: `Conflict`,
|
|
6147
|
+
422: `Unprocessable Entity`,
|
|
6148
|
+
500: `Internal Server Error`
|
|
6149
|
+
}
|
|
6150
|
+
});
|
|
6151
|
+
}
|
|
5497
6152
|
/**
|
|
5498
6153
|
* List all coverage reports uploaded for the common ancestor commit and head commit of a pull request branch
|
|
5499
6154
|
* @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 +6422,7 @@ var AnalysisService = class {
|
|
|
5767
6422
|
}
|
|
5768
6423
|
/**
|
|
5769
6424
|
* List organization pull requests from repositories that the user has access to
|
|
5770
|
-
* You can sort by `last-updated` (default), `
|
|
6425
|
+
* You can sort by `last-updated` (default), `merged`
|
|
5771
6426
|
* @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
6427
|
* @throws ApiError
|
|
5773
6428
|
*/
|
|
@@ -5896,7 +6551,7 @@ var AnalysisService = class {
|
|
|
5896
6551
|
}
|
|
5897
6552
|
/**
|
|
5898
6553
|
* Bulk ignore issues in a repository
|
|
5899
|
-
* Receives a list of issue ids and ignores them in the specified repository. Limited to
|
|
6554
|
+
* Receives a list of issue ids and ignores them in the specified repository. Limited to 100 issues at once
|
|
5900
6555
|
* @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
6556
|
* @throws ApiError
|
|
5902
6557
|
*/
|
|
@@ -7114,10 +7769,16 @@ var RepositoryService = class {
|
|
|
7114
7769
|
* @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
7770
|
* as returned by the endpoint [listRepositoryBranches](#listrepositorybranches).
|
|
7116
7771
|
* By default, uses the main branch defined on the Codacy repository settings.
|
|
7117
|
-
* * @param
|
|
7772
|
+
* * @param path Relative path of the folder whose direct child files are listed.
|
|
7773
|
+
* When absent, all files in the repository are returned (recursive).
|
|
7774
|
+
* When empty, only files at the repository root are returned.
|
|
7775
|
+
* * @param search Filter files that include this string anywhere in their relative path* @param sort Field used to sort the results.
|
|
7776
|
+
* For files the valid values are `filename`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.
|
|
7777
|
+
* For folders the valid values are `name`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.
|
|
7778
|
+
* * @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
7779
|
* @throws ApiError
|
|
7119
7780
|
*/
|
|
7120
|
-
static listFiles(provider, remoteOrganizationName, repositoryName, branch, search, sort, direction, cursor, limit = 100, abortSignal) {
|
|
7781
|
+
static listFiles(provider, remoteOrganizationName, repositoryName, branch, path12, search, sort, direction, cursor, limit = 100, abortSignal) {
|
|
7121
7782
|
return request(OpenAPI, {
|
|
7122
7783
|
method: "GET",
|
|
7123
7784
|
url: "/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/files",
|
|
@@ -7128,6 +7789,7 @@ var RepositoryService = class {
|
|
|
7128
7789
|
},
|
|
7129
7790
|
query: {
|
|
7130
7791
|
"branch": branch,
|
|
7792
|
+
"path": path12,
|
|
7131
7793
|
"search": search,
|
|
7132
7794
|
"sort": sort,
|
|
7133
7795
|
"direction": direction,
|
|
@@ -7144,6 +7806,48 @@ var RepositoryService = class {
|
|
|
7144
7806
|
}
|
|
7145
7807
|
});
|
|
7146
7808
|
}
|
|
7809
|
+
/**
|
|
7810
|
+
* List folders in a repository
|
|
7811
|
+
* 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.
|
|
7812
|
+
* Use the `path` parameter to list the folders inside a folder; when absent or empty, the folders at the repository root are returned.
|
|
7813
|
+
*
|
|
7814
|
+
* @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/),
|
|
7815
|
+
* as returned by the endpoint [listRepositoryBranches](#listrepositorybranches).
|
|
7816
|
+
* By default, uses the main branch defined on the Codacy repository settings.
|
|
7817
|
+
* * @param path Path of the folder whose child folders are listed.
|
|
7818
|
+
* When absent or empty, the folders at the repository root are returned.
|
|
7819
|
+
* * @param sort Field used to sort the results.
|
|
7820
|
+
* For files the valid values are `filename`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.
|
|
7821
|
+
* For folders the valid values are `name`, `issues`, `grade`, `duplication`, `complexity`, and `coverage`.
|
|
7822
|
+
* * @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
|
|
7823
|
+
* @throws ApiError
|
|
7824
|
+
*/
|
|
7825
|
+
static listDirectories(provider, remoteOrganizationName, repositoryName, branch, path12, sort, direction, cursor, limit = 100, abortSignal) {
|
|
7826
|
+
return request(OpenAPI, {
|
|
7827
|
+
method: "GET",
|
|
7828
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/directories",
|
|
7829
|
+
path: {
|
|
7830
|
+
"provider": provider,
|
|
7831
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
7832
|
+
"repositoryName": repositoryName
|
|
7833
|
+
},
|
|
7834
|
+
query: {
|
|
7835
|
+
"branch": branch,
|
|
7836
|
+
"path": path12,
|
|
7837
|
+
"sort": sort,
|
|
7838
|
+
"direction": direction,
|
|
7839
|
+
"cursor": cursor,
|
|
7840
|
+
"limit": limit
|
|
7841
|
+
},
|
|
7842
|
+
abortSignal,
|
|
7843
|
+
errors: {
|
|
7844
|
+
400: `Bad Request`,
|
|
7845
|
+
401: `Unauthorized`,
|
|
7846
|
+
404: `Not Found`,
|
|
7847
|
+
500: `Internal Server Error`
|
|
7848
|
+
}
|
|
7849
|
+
});
|
|
7850
|
+
}
|
|
7147
7851
|
/**
|
|
7148
7852
|
* List ignored files in a repository
|
|
7149
7853
|
* Returns the most recent ignored files information.
|
|
@@ -7267,6 +7971,9 @@ var RepositoryService = class {
|
|
|
7267
7971
|
}
|
|
7268
7972
|
/**
|
|
7269
7973
|
* List the [repository API tokens](https://docs.codacy.com/codacy-api/api-tokens/)
|
|
7974
|
+
* Each token reports its name, expiration and creation date. Tokens created before creation dates were
|
|
7975
|
+
* recorded have no `createdAt`
|
|
7976
|
+
*
|
|
7270
7977
|
* @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
7978
|
* @throws ApiError
|
|
7272
7979
|
*/
|
|
@@ -7291,10 +7998,10 @@ var RepositoryService = class {
|
|
|
7291
7998
|
}
|
|
7292
7999
|
/**
|
|
7293
8000
|
* 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
|
|
8001
|
+
* @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
8002
|
* @throws ApiError
|
|
7296
8003
|
*/
|
|
7297
|
-
static createRepositoryApiToken(provider, remoteOrganizationName, repositoryName, abortSignal) {
|
|
8004
|
+
static createRepositoryApiToken(provider, remoteOrganizationName, repositoryName, requestBody, abortSignal) {
|
|
7298
8005
|
return request(OpenAPI, {
|
|
7299
8006
|
method: "POST",
|
|
7300
8007
|
url: "/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tokens",
|
|
@@ -7304,6 +8011,35 @@ var RepositoryService = class {
|
|
|
7304
8011
|
"repositoryName": repositoryName
|
|
7305
8012
|
},
|
|
7306
8013
|
abortSignal,
|
|
8014
|
+
body: requestBody,
|
|
8015
|
+
mediaType: "application/json",
|
|
8016
|
+
errors: {
|
|
8017
|
+
400: `Bad Request`,
|
|
8018
|
+
401: `Unauthorized`,
|
|
8019
|
+
404: `Not Found`,
|
|
8020
|
+
422: `Unprocessable Entity`,
|
|
8021
|
+
500: `Internal Server Error`
|
|
8022
|
+
}
|
|
8023
|
+
});
|
|
8024
|
+
}
|
|
8025
|
+
/**
|
|
8026
|
+
* Delete multiple [repository API tokens](https://docs.codacy.com/codacy-api/api-tokens/) by id
|
|
8027
|
+
* Deletes the repository API tokens with the given ids
|
|
8028
|
+
* @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
|
|
8029
|
+
* @throws ApiError
|
|
8030
|
+
*/
|
|
8031
|
+
static deleteRepositoryApiTokens(provider, remoteOrganizationName, repositoryName, requestBody, abortSignal) {
|
|
8032
|
+
return request(OpenAPI, {
|
|
8033
|
+
method: "POST",
|
|
8034
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/repositories/{repositoryName}/tokens/delete",
|
|
8035
|
+
path: {
|
|
8036
|
+
"provider": provider,
|
|
8037
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
8038
|
+
"repositoryName": repositoryName
|
|
8039
|
+
},
|
|
8040
|
+
abortSignal,
|
|
8041
|
+
body: requestBody,
|
|
8042
|
+
mediaType: "application/json",
|
|
7307
8043
|
errors: {
|
|
7308
8044
|
400: `Bad Request`,
|
|
7309
8045
|
401: `Unauthorized`,
|
|
@@ -7555,97 +8291,347 @@ var RepositoryService = class {
|
|
|
7555
8291
|
}
|
|
7556
8292
|
};
|
|
7557
8293
|
|
|
7558
|
-
// src/api/client/services/
|
|
7559
|
-
var
|
|
8294
|
+
// src/api/client/services/SbomService.ts
|
|
8295
|
+
var SbomService = class {
|
|
7560
8296
|
/**
|
|
7561
|
-
*
|
|
7562
|
-
* @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
|
|
8297
|
+
* Return a list of SBOM dependencies, used across the organization.
|
|
8298
|
+
* @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
|
|
7563
8299
|
* @throws ApiError
|
|
7564
8300
|
*/
|
|
7565
|
-
static
|
|
8301
|
+
static searchSbomDependencies(provider, remoteOrganizationName, cursor, limit = 100, sortColumn = "severity", columnOrder = "desc", requestBody, abortSignal) {
|
|
7566
8302
|
return request(OpenAPI, {
|
|
7567
|
-
method: "
|
|
7568
|
-
url: "/
|
|
8303
|
+
method: "POST",
|
|
8304
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/sbom/dependencies/search",
|
|
8305
|
+
path: {
|
|
8306
|
+
"provider": provider,
|
|
8307
|
+
"remoteOrganizationName": remoteOrganizationName
|
|
8308
|
+
},
|
|
7569
8309
|
query: {
|
|
7570
8310
|
"cursor": cursor,
|
|
7571
|
-
"limit": limit
|
|
8311
|
+
"limit": limit,
|
|
8312
|
+
"sortColumn": sortColumn,
|
|
8313
|
+
"columnOrder": columnOrder
|
|
7572
8314
|
},
|
|
7573
8315
|
abortSignal,
|
|
8316
|
+
body: requestBody,
|
|
8317
|
+
mediaType: "application/json",
|
|
7574
8318
|
errors: {
|
|
7575
8319
|
400: `Bad Request`,
|
|
8320
|
+
401: `Unauthorized`,
|
|
8321
|
+
403: `Forbidden`,
|
|
8322
|
+
404: `Not Found`,
|
|
7576
8323
|
422: `Unprocessable Entity`,
|
|
7577
|
-
500: `Internal Server Error
|
|
8324
|
+
500: `Internal Server Error`,
|
|
8325
|
+
501: `Not Implemented`,
|
|
8326
|
+
502: `Bad Gateway`
|
|
7578
8327
|
}
|
|
7579
8328
|
});
|
|
7580
8329
|
}
|
|
7581
8330
|
/**
|
|
7582
|
-
*
|
|
7583
|
-
* @param
|
|
8331
|
+
* Return a paginated list of repositories where a version of the specified SBOM dependency is used.
|
|
8332
|
+
* @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
|
|
7584
8333
|
* @throws ApiError
|
|
7585
8334
|
*/
|
|
7586
|
-
static
|
|
8335
|
+
static searchRepositoriesOfSbomDependency(provider, remoteOrganizationName, requestBody, cursor, limit = 100, abortSignal) {
|
|
7587
8336
|
return request(OpenAPI, {
|
|
7588
|
-
method: "
|
|
7589
|
-
url: "/
|
|
8337
|
+
method: "POST",
|
|
8338
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/sbom/dependencies/repositories/search",
|
|
7590
8339
|
path: {
|
|
7591
|
-
"
|
|
8340
|
+
"provider": provider,
|
|
8341
|
+
"remoteOrganizationName": remoteOrganizationName
|
|
7592
8342
|
},
|
|
7593
8343
|
query: {
|
|
7594
8344
|
"cursor": cursor,
|
|
7595
|
-
"limit": limit
|
|
7596
|
-
"enabled": enabled
|
|
8345
|
+
"limit": limit
|
|
7597
8346
|
},
|
|
7598
8347
|
abortSignal,
|
|
8348
|
+
body: requestBody,
|
|
8349
|
+
mediaType: "application/json",
|
|
7599
8350
|
errors: {
|
|
7600
8351
|
400: `Bad Request`,
|
|
8352
|
+
401: `Unauthorized`,
|
|
8353
|
+
403: `Forbidden`,
|
|
7601
8354
|
404: `Not Found`,
|
|
7602
8355
|
422: `Unprocessable Entity`,
|
|
7603
|
-
500: `Internal Server Error
|
|
8356
|
+
500: `Internal Server Error`,
|
|
8357
|
+
501: `Not Implemented`,
|
|
8358
|
+
502: `Bad Gateway`
|
|
7604
8359
|
}
|
|
7605
8360
|
});
|
|
7606
8361
|
}
|
|
7607
8362
|
/**
|
|
7608
|
-
*
|
|
7609
|
-
*
|
|
7610
|
-
*
|
|
7611
|
-
* @param toolUuid Unique identifier (UUID) for the tool* @param patternId Pattern identifier* @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 * @returns any Successful operation
|
|
8363
|
+
* List repositories with SBOM dependency information
|
|
8364
|
+
* @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
|
|
7612
8365
|
* @throws ApiError
|
|
7613
8366
|
*/
|
|
7614
|
-
static
|
|
8367
|
+
static searchSbomRepositories(provider, remoteOrganizationName, cursor, limit = 100, requestBody, abortSignal) {
|
|
7615
8368
|
return request(OpenAPI, {
|
|
7616
8369
|
method: "POST",
|
|
7617
|
-
url: "/
|
|
8370
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/sbom/repositories/search",
|
|
7618
8371
|
path: {
|
|
7619
|
-
"toolUuid": toolUuid,
|
|
7620
|
-
"patternId": patternId,
|
|
7621
8372
|
"provider": provider,
|
|
7622
8373
|
"remoteOrganizationName": remoteOrganizationName
|
|
7623
8374
|
},
|
|
8375
|
+
query: {
|
|
8376
|
+
"cursor": cursor,
|
|
8377
|
+
"limit": limit
|
|
8378
|
+
},
|
|
7624
8379
|
abortSignal,
|
|
7625
8380
|
body: requestBody,
|
|
7626
8381
|
mediaType: "application/json",
|
|
7627
8382
|
errors: {
|
|
7628
8383
|
400: `Bad Request`,
|
|
7629
8384
|
401: `Unauthorized`,
|
|
8385
|
+
403: `Forbidden`,
|
|
7630
8386
|
404: `Not Found`,
|
|
7631
8387
|
422: `Unprocessable Entity`,
|
|
7632
|
-
500: `Internal Server Error
|
|
8388
|
+
500: `Internal Server Error`,
|
|
8389
|
+
501: `Not Implemented`,
|
|
8390
|
+
502: `Bad Gateway`
|
|
7633
8391
|
}
|
|
7634
8392
|
});
|
|
7635
8393
|
}
|
|
7636
8394
|
/**
|
|
7637
|
-
*
|
|
7638
|
-
*
|
|
7639
|
-
* @param toolUuid Unique identifier (UUID) for the tool* @param patternId Pattern identifier * @returns PatternResponse Successful operation
|
|
8395
|
+
* Get a presigned URL for the latest SBOM of the repository
|
|
8396
|
+
* @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.
|
|
7640
8397
|
* @throws ApiError
|
|
7641
8398
|
*/
|
|
7642
|
-
static
|
|
8399
|
+
static getRepositorySbomPresignedUrl(provider, remoteOrganizationName, repositoryName, abortSignal) {
|
|
7643
8400
|
return request(OpenAPI, {
|
|
7644
8401
|
method: "GET",
|
|
7645
|
-
url: "/
|
|
8402
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/projects/{repositoryName}/sbom",
|
|
7646
8403
|
path: {
|
|
7647
|
-
"
|
|
7648
|
-
"
|
|
8404
|
+
"provider": provider,
|
|
8405
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
8406
|
+
"repositoryName": repositoryName
|
|
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
|
+
* Upload an SBOM for a Docker image
|
|
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 formData * @returns void
|
|
8420
|
+
* @throws ApiError
|
|
8421
|
+
*/
|
|
8422
|
+
static uploadImageSbom(provider, remoteOrganizationName, formData, abortSignal) {
|
|
8423
|
+
return request(OpenAPI, {
|
|
8424
|
+
method: "POST",
|
|
8425
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/image-sboms",
|
|
8426
|
+
path: {
|
|
8427
|
+
"provider": provider,
|
|
8428
|
+
"remoteOrganizationName": remoteOrganizationName
|
|
8429
|
+
},
|
|
8430
|
+
abortSignal,
|
|
8431
|
+
formData,
|
|
8432
|
+
mediaType: "multipart/form-data",
|
|
8433
|
+
errors: {
|
|
8434
|
+
400: `Bad Request`,
|
|
8435
|
+
401: `Unauthorized`,
|
|
8436
|
+
404: `Not Found`,
|
|
8437
|
+
409: `Conflict`,
|
|
8438
|
+
500: `Internal Server Error`
|
|
8439
|
+
}
|
|
8440
|
+
});
|
|
8441
|
+
}
|
|
8442
|
+
/**
|
|
8443
|
+
* Delete all SBOMs for a given image
|
|
8444
|
+
* @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
|
|
8445
|
+
* @throws ApiError
|
|
8446
|
+
*/
|
|
8447
|
+
static deleteImageSboms(provider, remoteOrganizationName, imageName, abortSignal) {
|
|
8448
|
+
return request(OpenAPI, {
|
|
8449
|
+
method: "DELETE",
|
|
8450
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/image-sboms/{imageName}",
|
|
8451
|
+
path: {
|
|
8452
|
+
"provider": provider,
|
|
8453
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
8454
|
+
"imageName": imageName
|
|
8455
|
+
},
|
|
8456
|
+
abortSignal,
|
|
8457
|
+
errors: {
|
|
8458
|
+
400: `Bad Request`,
|
|
8459
|
+
401: `Unauthorized`,
|
|
8460
|
+
404: `Not Found`,
|
|
8461
|
+
500: `Internal Server Error`
|
|
8462
|
+
}
|
|
8463
|
+
});
|
|
8464
|
+
}
|
|
8465
|
+
/**
|
|
8466
|
+
* Delete SBOM for a given image/tag combination
|
|
8467
|
+
* @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
|
|
8468
|
+
* @throws ApiError
|
|
8469
|
+
*/
|
|
8470
|
+
static deleteImageTag(provider, remoteOrganizationName, imageName, tag, abortSignal) {
|
|
8471
|
+
return request(OpenAPI, {
|
|
8472
|
+
method: "DELETE",
|
|
8473
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/image-sboms/{imageName}/tags/{tag}",
|
|
8474
|
+
path: {
|
|
8475
|
+
"provider": provider,
|
|
8476
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
8477
|
+
"imageName": imageName,
|
|
8478
|
+
"tag": tag
|
|
8479
|
+
},
|
|
8480
|
+
abortSignal,
|
|
8481
|
+
errors: {
|
|
8482
|
+
400: `Bad Request`,
|
|
8483
|
+
401: `Unauthorized`,
|
|
8484
|
+
404: `Not Found`,
|
|
8485
|
+
500: `Internal Server Error`
|
|
8486
|
+
}
|
|
8487
|
+
});
|
|
8488
|
+
}
|
|
8489
|
+
/**
|
|
8490
|
+
* List Docker images for an organization
|
|
8491
|
+
* @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
|
|
8492
|
+
* @throws ApiError
|
|
8493
|
+
*/
|
|
8494
|
+
static listOrganizationImages(provider, remoteOrganizationName, cursor, limit = 100, abortSignal) {
|
|
8495
|
+
return request(OpenAPI, {
|
|
8496
|
+
method: "GET",
|
|
8497
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/images",
|
|
8498
|
+
path: {
|
|
8499
|
+
"provider": provider,
|
|
8500
|
+
"remoteOrganizationName": remoteOrganizationName
|
|
8501
|
+
},
|
|
8502
|
+
query: {
|
|
8503
|
+
"cursor": cursor,
|
|
8504
|
+
"limit": limit
|
|
8505
|
+
},
|
|
8506
|
+
abortSignal,
|
|
8507
|
+
errors: {
|
|
8508
|
+
400: `Bad Request`,
|
|
8509
|
+
401: `Unauthorized`,
|
|
8510
|
+
404: `Not Found`,
|
|
8511
|
+
500: `Internal Server Error`
|
|
8512
|
+
}
|
|
8513
|
+
});
|
|
8514
|
+
}
|
|
8515
|
+
/**
|
|
8516
|
+
* List Docker image tags for an organization
|
|
8517
|
+
* @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
|
|
8518
|
+
* @throws ApiError
|
|
8519
|
+
*/
|
|
8520
|
+
static listImageTags(provider, remoteOrganizationName, imageName, cursor, limit = 100, abortSignal) {
|
|
8521
|
+
return request(OpenAPI, {
|
|
8522
|
+
method: "GET",
|
|
8523
|
+
url: "/organizations/{provider}/{remoteOrganizationName}/images/{imageName}/tags",
|
|
8524
|
+
path: {
|
|
8525
|
+
"provider": provider,
|
|
8526
|
+
"remoteOrganizationName": remoteOrganizationName,
|
|
8527
|
+
"imageName": imageName
|
|
8528
|
+
},
|
|
8529
|
+
query: {
|
|
8530
|
+
"cursor": cursor,
|
|
8531
|
+
"limit": limit
|
|
8532
|
+
},
|
|
8533
|
+
abortSignal,
|
|
8534
|
+
errors: {
|
|
8535
|
+
400: `Bad Request`,
|
|
8536
|
+
401: `Unauthorized`,
|
|
8537
|
+
404: `Not Found`,
|
|
8538
|
+
500: `Internal Server Error`
|
|
8539
|
+
}
|
|
8540
|
+
});
|
|
8541
|
+
}
|
|
8542
|
+
};
|
|
8543
|
+
|
|
8544
|
+
// src/api/client/services/ToolsService.ts
|
|
8545
|
+
var ToolsService = class {
|
|
8546
|
+
/**
|
|
8547
|
+
* Retrieve the list of tools
|
|
8548
|
+
* @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 ToolListResponse Successful operation
|
|
8549
|
+
* @throws ApiError
|
|
8550
|
+
*/
|
|
8551
|
+
static listTools(cursor, limit = 100, abortSignal) {
|
|
8552
|
+
return request(OpenAPI, {
|
|
8553
|
+
method: "GET",
|
|
8554
|
+
url: "/tools",
|
|
8555
|
+
query: {
|
|
8556
|
+
"cursor": cursor,
|
|
8557
|
+
"limit": limit
|
|
8558
|
+
},
|
|
8559
|
+
abortSignal,
|
|
8560
|
+
errors: {
|
|
8561
|
+
400: `Bad Request`,
|
|
8562
|
+
422: `Unprocessable Entity`,
|
|
8563
|
+
500: `Internal Server Error`
|
|
8564
|
+
}
|
|
8565
|
+
});
|
|
8566
|
+
}
|
|
8567
|
+
/**
|
|
8568
|
+
* Retrieve the list of tool patterns
|
|
8569
|
+
* @param toolUuid Unique identifier (UUID) for the tool* @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 enabled Filter by enabled status. Set to `true` to return only enabled patterns, or `false` to return only disabled patterns. * @returns PatternListResponse Successful operations
|
|
8570
|
+
* @throws ApiError
|
|
8571
|
+
*/
|
|
8572
|
+
static listPatterns(toolUuid, cursor, limit = 100, enabled, abortSignal) {
|
|
8573
|
+
return request(OpenAPI, {
|
|
8574
|
+
method: "GET",
|
|
8575
|
+
url: "/tools/{toolUuid}/patterns",
|
|
8576
|
+
path: {
|
|
8577
|
+
"toolUuid": toolUuid
|
|
8578
|
+
},
|
|
8579
|
+
query: {
|
|
8580
|
+
"cursor": cursor,
|
|
8581
|
+
"limit": limit,
|
|
8582
|
+
"enabled": enabled
|
|
8583
|
+
},
|
|
8584
|
+
abortSignal,
|
|
8585
|
+
errors: {
|
|
8586
|
+
400: `Bad Request`,
|
|
8587
|
+
404: `Not Found`,
|
|
8588
|
+
422: `Unprocessable Entity`,
|
|
8589
|
+
500: `Internal Server Error`
|
|
8590
|
+
}
|
|
8591
|
+
});
|
|
8592
|
+
}
|
|
8593
|
+
/**
|
|
8594
|
+
* Add feedback relating to the tool pattern
|
|
8595
|
+
* Add feedback relating to the tool pattern.
|
|
8596
|
+
*
|
|
8597
|
+
* @param toolUuid Unique identifier (UUID) for the tool* @param patternId Pattern identifier* @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 * @returns any Successful operation
|
|
8598
|
+
* @throws ApiError
|
|
8599
|
+
*/
|
|
8600
|
+
static addPatternFeedback(toolUuid, patternId, provider, remoteOrganizationName, requestBody, abortSignal) {
|
|
8601
|
+
return request(OpenAPI, {
|
|
8602
|
+
method: "POST",
|
|
8603
|
+
url: "/tools/{toolUuid}/patterns/{patternId}/organizations/{provider}/{remoteOrganizationName}/feedback",
|
|
8604
|
+
path: {
|
|
8605
|
+
"toolUuid": toolUuid,
|
|
8606
|
+
"patternId": patternId,
|
|
8607
|
+
"provider": provider,
|
|
8608
|
+
"remoteOrganizationName": remoteOrganizationName
|
|
8609
|
+
},
|
|
8610
|
+
abortSignal,
|
|
8611
|
+
body: requestBody,
|
|
8612
|
+
mediaType: "application/json",
|
|
8613
|
+
errors: {
|
|
8614
|
+
400: `Bad Request`,
|
|
8615
|
+
401: `Unauthorized`,
|
|
8616
|
+
404: `Not Found`,
|
|
8617
|
+
422: `Unprocessable Entity`,
|
|
8618
|
+
500: `Internal Server Error`
|
|
8619
|
+
}
|
|
8620
|
+
});
|
|
8621
|
+
}
|
|
8622
|
+
/**
|
|
8623
|
+
* Retrieve a tool pattern
|
|
8624
|
+
* Get the definition of a specific pattern
|
|
8625
|
+
* @param toolUuid Unique identifier (UUID) for the tool* @param patternId Pattern identifier * @returns PatternResponse Successful operation
|
|
8626
|
+
* @throws ApiError
|
|
8627
|
+
*/
|
|
8628
|
+
static getPattern(toolUuid, patternId, abortSignal) {
|
|
8629
|
+
return request(OpenAPI, {
|
|
8630
|
+
method: "GET",
|
|
8631
|
+
url: "/tools/{toolUuid}/patterns/{patternId}",
|
|
8632
|
+
path: {
|
|
8633
|
+
"toolUuid": toolUuid,
|
|
8634
|
+
"patternId": patternId
|
|
7649
8635
|
},
|
|
7650
8636
|
abortSignal,
|
|
7651
8637
|
errors: {
|
|
@@ -7753,6 +8739,8 @@ async function listRepositoryToolPatterns(provider, org, repo, toolUuid) {
|
|
|
7753
8739
|
void 0,
|
|
7754
8740
|
// recommended
|
|
7755
8741
|
void 0,
|
|
8742
|
+
// matchesStack
|
|
8743
|
+
void 0,
|
|
7756
8744
|
// sort
|
|
7757
8745
|
void 0,
|
|
7758
8746
|
// direction
|
|
@@ -7790,6 +8778,20 @@ async function listIgnoredFiles(provider, org, repo) {
|
|
|
7790
8778
|
capped: pages >= MAX_PAGES && cursor !== void 0
|
|
7791
8779
|
};
|
|
7792
8780
|
}
|
|
8781
|
+
async function uploadImageSbom(provider, organization, upload, signal) {
|
|
8782
|
+
await SbomService.uploadImageSbom(
|
|
8783
|
+
provider,
|
|
8784
|
+
organization,
|
|
8785
|
+
{
|
|
8786
|
+
sbom: upload.sbom,
|
|
8787
|
+
imageName: upload.imageName,
|
|
8788
|
+
tag: upload.tag,
|
|
8789
|
+
...upload.repositoryName ? { repositoryName: upload.repositoryName } : {},
|
|
8790
|
+
...upload.environment ? { environment: upload.environment } : {}
|
|
8791
|
+
},
|
|
8792
|
+
signal
|
|
8793
|
+
);
|
|
8794
|
+
}
|
|
7793
8795
|
|
|
7794
8796
|
// src/init.ts
|
|
7795
8797
|
function isAuthFailure(err) {
|
|
@@ -7827,6 +8829,13 @@ function buildAdapterByUuid(adapters, apiTools) {
|
|
|
7827
8829
|
}
|
|
7828
8830
|
return result;
|
|
7829
8831
|
}
|
|
8832
|
+
function listRepoFilesOrEmpty(repositoryRoot, exclude) {
|
|
8833
|
+
try {
|
|
8834
|
+
return listRepoFiles(repositoryRoot, exclude);
|
|
8835
|
+
} catch {
|
|
8836
|
+
return [];
|
|
8837
|
+
}
|
|
8838
|
+
}
|
|
7830
8839
|
async function detectLanguages(repositoryRoot, precomputedFiles, exclude) {
|
|
7831
8840
|
let files;
|
|
7832
8841
|
if (precomputedFiles) {
|
|
@@ -7848,9 +8857,9 @@ async function detectLanguages(repositoryRoot, precomputedFiles, exclude) {
|
|
|
7848
8857
|
}
|
|
7849
8858
|
const detected = /* @__PURE__ */ new Set();
|
|
7850
8859
|
for (const file of files) {
|
|
7851
|
-
const basename =
|
|
7852
|
-
const ext =
|
|
7853
|
-
for (const [lang, config] of Object.entries(
|
|
8860
|
+
const basename = import_path9.default.basename(file);
|
|
8861
|
+
const ext = import_path9.default.extname(file).toLowerCase();
|
|
8862
|
+
for (const [lang, config] of Object.entries(import_tooling8.LANGUAGE_CONFIG)) {
|
|
7854
8863
|
if (config.extensions.includes(ext)) {
|
|
7855
8864
|
detected.add(lang);
|
|
7856
8865
|
} else if (config.files.includes(basename)) {
|
|
@@ -7866,9 +8875,9 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
|
|
|
7866
8875
|
adapters.map(async (adapter) => {
|
|
7867
8876
|
const result = await adapter.checkLocalConfigurationFile({
|
|
7868
8877
|
repositoryRoot,
|
|
7869
|
-
generatedConfigDir:
|
|
7870
|
-
globalDir:
|
|
7871
|
-
toolInstallDir:
|
|
8878
|
+
generatedConfigDir: import_path9.default.join(repositoryRoot, ".codacy", "generated"),
|
|
8879
|
+
globalDir: (0, import_tooling8.resolveGlobalDir)(),
|
|
8880
|
+
toolInstallDir: import_path9.default.join((0, import_tooling8.resolveGlobalDir)(), "tools", adapter.id),
|
|
7872
8881
|
targetFiles: [],
|
|
7873
8882
|
platform: { os: process.platform, arch: process.arch },
|
|
7874
8883
|
logger: createNullLogger(),
|
|
@@ -7894,7 +8903,7 @@ function enrichWithCodacyYaml(config, adapters, excludes) {
|
|
|
7894
8903
|
async function initLocalConfigBase(repositoryRoot, adapters, excludes) {
|
|
7895
8904
|
const languages = await detectLanguages(
|
|
7896
8905
|
repositoryRoot,
|
|
7897
|
-
|
|
8906
|
+
listRepoFilesOrEmpty(repositoryRoot, excludes.global)
|
|
7898
8907
|
);
|
|
7899
8908
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
7900
8909
|
const tools = [];
|
|
@@ -8078,60 +9087,11 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
8078
9087
|
}
|
|
8079
9088
|
};
|
|
8080
9089
|
}
|
|
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 });
|
|
9090
|
+
async function initContainerConfig(repositoryRoot, adapters, codacyrcPath) {
|
|
9091
|
+
const resolvedPath = codacyrcPath ?? import_path9.default.join(repositoryRoot, ".codacyrc");
|
|
9092
|
+
const codacyrc = await readCodacyrc(resolvedPath);
|
|
9093
|
+
const { config } = await resolveCodacyrc(codacyrc, { toolIds: adapters.map((a) => a.id) });
|
|
9094
|
+
return config;
|
|
8135
9095
|
}
|
|
8136
9096
|
function hasMatchingFiles(files, filePatterns) {
|
|
8137
9097
|
const matcher = new CompiledMatcher(filePatterns);
|
|
@@ -8147,12 +9107,12 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
8147
9107
|
const discoveryResult = await discover(repositoryRoot, { exclude: excludes.global });
|
|
8148
9108
|
onDiscoveryComplete?.(discoveryResult);
|
|
8149
9109
|
const detectedKeys = /* @__PURE__ */ new Set();
|
|
8150
|
-
for (const lang of discoveryResult.languages) detectedKeys.add((0,
|
|
9110
|
+
for (const lang of discoveryResult.languages) detectedKeys.add((0, import_tooling8.languageKey)(lang));
|
|
8151
9111
|
for (const fw of discoveryResult.frameworks) detectedKeys.add(fw.key);
|
|
8152
9112
|
for (const lib of discoveryResult.libraries) detectedKeys.add(lib.key);
|
|
8153
9113
|
const repoLanguages = new Set(discoveryResult.languages);
|
|
8154
9114
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
8155
|
-
const repoFiles =
|
|
9115
|
+
const repoFiles = listRepoFilesOrEmpty(repositoryRoot, excludes.global);
|
|
8156
9116
|
const descriptorMap = new Map(descriptors.map((d) => [d.id, d]));
|
|
8157
9117
|
const perToolFiles = /* @__PURE__ */ new Map();
|
|
8158
9118
|
const filesVisibleTo = (toolId) => {
|
|
@@ -8279,12 +9239,12 @@ async function applyAdapterConfigSuggestions(adapter, toolEntry, repositoryRoot,
|
|
|
8279
9239
|
}
|
|
8280
9240
|
};
|
|
8281
9241
|
try {
|
|
8282
|
-
const globalDir =
|
|
9242
|
+
const globalDir = (0, import_tooling8.resolveGlobalDir)();
|
|
8283
9243
|
const ctx = {
|
|
8284
9244
|
repositoryRoot,
|
|
8285
|
-
generatedConfigDir:
|
|
9245
|
+
generatedConfigDir: import_path9.default.join(repositoryRoot, ".codacy", "generated", adapter.id),
|
|
8286
9246
|
globalDir,
|
|
8287
|
-
toolInstallDir:
|
|
9247
|
+
toolInstallDir: import_path9.default.join(globalDir, "tools", adapter.id),
|
|
8288
9248
|
targetFiles: routeFilesToTool(repoFiles, adapter, toolEntry),
|
|
8289
9249
|
platform: { os: process.platform, arch: process.arch },
|
|
8290
9250
|
logger,
|
|
@@ -8545,15 +9505,15 @@ function parseAutoFilters(raw) {
|
|
|
8545
9505
|
}
|
|
8546
9506
|
|
|
8547
9507
|
// src/index.ts
|
|
8548
|
-
var
|
|
9508
|
+
var import_tooling10 = require("@codacy/tooling");
|
|
8549
9509
|
|
|
8550
9510
|
// src/credentials.ts
|
|
8551
9511
|
var import_crypto = __toESM(require("crypto"));
|
|
8552
9512
|
var import_promises5 = __toESM(require("fs/promises"));
|
|
8553
9513
|
var import_os4 = __toESM(require("os"));
|
|
8554
|
-
var
|
|
9514
|
+
var import_path10 = __toESM(require("path"));
|
|
8555
9515
|
function credentialsPath() {
|
|
8556
|
-
return
|
|
9516
|
+
return import_path10.default.join(import_os4.default.homedir(), ".codacy", "credentials");
|
|
8557
9517
|
}
|
|
8558
9518
|
function deriveKey(salt) {
|
|
8559
9519
|
const info = import_os4.default.userInfo();
|
|
@@ -8574,7 +9534,7 @@ async function saveCredentials(token) {
|
|
|
8574
9534
|
authTag: authTag.toString("hex"),
|
|
8575
9535
|
encrypted: encrypted.toString("hex")
|
|
8576
9536
|
});
|
|
8577
|
-
await import_promises5.default.mkdir(
|
|
9537
|
+
await import_promises5.default.mkdir(import_path10.default.dirname(credentialsFile), { recursive: true });
|
|
8578
9538
|
await import_promises5.default.writeFile(credentialsFile, payload, { encoding: "utf-8", mode: 384 });
|
|
8579
9539
|
}
|
|
8580
9540
|
async function loadCredentials() {
|
|
@@ -8875,11 +9835,287 @@ async function safeReadBody(res) {
|
|
|
8875
9835
|
return "";
|
|
8876
9836
|
}
|
|
8877
9837
|
}
|
|
9838
|
+
|
|
9839
|
+
// src/container-image.ts
|
|
9840
|
+
var import_promises7 = __toESM(require("fs/promises"));
|
|
9841
|
+
var import_os5 = __toESM(require("os"));
|
|
9842
|
+
var import_path11 = __toESM(require("path"));
|
|
9843
|
+
var import_tooling9 = require("@codacy/tooling");
|
|
9844
|
+
var DEFAULT_IMAGE_SCAN_SEVERITIES = ["HIGH", "CRITICAL"];
|
|
9845
|
+
var DEFAULT_IMAGE_SCAN_PKG_TYPES = ["os"];
|
|
9846
|
+
var SBOM_UPLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
9847
|
+
var PROVIDERS = ["gh", "gl", "bb"];
|
|
9848
|
+
async function scanContainerImage(options) {
|
|
9849
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
9850
|
+
(0, import_tooling9.validateImageReference)(options.image);
|
|
9851
|
+
const severities = parseVocabulary(
|
|
9852
|
+
options.severities ?? DEFAULT_IMAGE_SCAN_SEVERITIES,
|
|
9853
|
+
import_tooling9.IMAGE_SCAN_SEVERITIES,
|
|
9854
|
+
"severity",
|
|
9855
|
+
(v) => v.toUpperCase()
|
|
9856
|
+
);
|
|
9857
|
+
const pkgTypes = parseVocabulary(
|
|
9858
|
+
options.pkgTypes ?? DEFAULT_IMAGE_SCAN_PKG_TYPES,
|
|
9859
|
+
import_tooling9.IMAGE_SCAN_PKG_TYPES,
|
|
9860
|
+
"package type",
|
|
9861
|
+
(v) => v.toLowerCase()
|
|
9862
|
+
);
|
|
9863
|
+
if (options.signal?.aborted) {
|
|
9864
|
+
return imageResult(startedAt, emptyCapability(), [], [cancelledError("before it started")], []);
|
|
9865
|
+
}
|
|
9866
|
+
const logger = options.logger ?? createNullLogger();
|
|
9867
|
+
const adapter = await resolveImageAdapter();
|
|
9868
|
+
const baseCtx = makeBaseContext(logger, options.signal);
|
|
9869
|
+
const prepared = await prepareTool(adapter, baseCtx, logger);
|
|
9870
|
+
if (!prepared.ok) {
|
|
9871
|
+
return imageResult(startedAt, prepared.capability, [], [prepared.error], []);
|
|
9872
|
+
}
|
|
9873
|
+
if (options.signal?.aborted) {
|
|
9874
|
+
return imageResult(startedAt, prepared.capability, [], [cancelledError("before the scan")], []);
|
|
9875
|
+
}
|
|
9876
|
+
const run = await adapter.containerImage.scan(makeToolContext(baseCtx, adapter, []), {
|
|
9877
|
+
image: options.image,
|
|
9878
|
+
severities,
|
|
9879
|
+
pkgTypes,
|
|
9880
|
+
ignoreUnfixed: options.ignoreUnfixed ?? true
|
|
9881
|
+
});
|
|
9882
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
9883
|
+
for (const entry of adapter.patternsMetadata ?? []) {
|
|
9884
|
+
lookup.set(`${adapter.id}|${entry.id}`, {
|
|
9885
|
+
title: entry.title,
|
|
9886
|
+
toolDisplayName: adapter.displayName
|
|
9887
|
+
});
|
|
9888
|
+
}
|
|
9889
|
+
const patternSummary = run.issues.length > 0 ? computePatternSummary(run.issues, lookup) : void 0;
|
|
9890
|
+
return imageResult(
|
|
9891
|
+
startedAt,
|
|
9892
|
+
prepared.capability,
|
|
9893
|
+
run.issues,
|
|
9894
|
+
run.errors,
|
|
9895
|
+
[run.summary],
|
|
9896
|
+
patternSummary
|
|
9897
|
+
);
|
|
9898
|
+
}
|
|
9899
|
+
async function uploadImageSbom2(options) {
|
|
9900
|
+
const target = (0, import_tooling9.resolveImageTarget)(options.image, options.tag);
|
|
9901
|
+
const format = parseSbomFormat(options.format);
|
|
9902
|
+
if (!PROVIDERS.includes(options.provider)) {
|
|
9903
|
+
throw new Error(
|
|
9904
|
+
`Invalid provider "${options.provider}": expected one of ${PROVIDERS.join(", ")}`
|
|
9905
|
+
);
|
|
9906
|
+
}
|
|
9907
|
+
assertValidCoordinate("organization", options.organization);
|
|
9908
|
+
if (options.repositoryName) assertValidCoordinate("repository", options.repositoryName);
|
|
9909
|
+
if (!options.apiToken) {
|
|
9910
|
+
throw new Error("An account API token is required to upload an SBOM.");
|
|
9911
|
+
}
|
|
9912
|
+
throwIfCancelled(options.signal, "before it started");
|
|
9913
|
+
const logger = options.logger ?? createNullLogger();
|
|
9914
|
+
const progress = options.onProgress ?? (() => {
|
|
9915
|
+
});
|
|
9916
|
+
const adapter = await resolveImageAdapter();
|
|
9917
|
+
const baseCtx = makeBaseContext(logger, options.signal);
|
|
9918
|
+
const prepared = await prepareTool(adapter, baseCtx, logger);
|
|
9919
|
+
if (!prepared.ok) {
|
|
9920
|
+
throw new Error(`Could not install ${adapter.displayName}: ${prepared.error.message}`);
|
|
9921
|
+
}
|
|
9922
|
+
throwIfCancelled(options.signal, "before SBOM generation");
|
|
9923
|
+
const tempDir = await import_promises7.default.mkdtemp(import_path11.default.join(import_os5.default.tmpdir(), "codacy-sbom-"));
|
|
9924
|
+
try {
|
|
9925
|
+
const outputPath = import_path11.default.join(
|
|
9926
|
+
tempDir,
|
|
9927
|
+
format === "cyclonedx" ? "sbom.cdx.json" : "sbom.spdx.json"
|
|
9928
|
+
);
|
|
9929
|
+
progress(`Generating SBOM for image: ${target.scanRef}`);
|
|
9930
|
+
const generated = await adapter.containerImage.generateSbom(
|
|
9931
|
+
makeToolContext(baseCtx, adapter, []),
|
|
9932
|
+
{ image: target.scanRef, format, outputPath }
|
|
9933
|
+
);
|
|
9934
|
+
if (!generated.success) {
|
|
9935
|
+
throw new Error(`Failed to generate SBOM: ${generated.error?.message ?? "unknown error"}`);
|
|
9936
|
+
}
|
|
9937
|
+
const contents = await import_promises7.default.readFile(outputPath);
|
|
9938
|
+
const sbom = new File([contents], import_path11.default.basename(outputPath), { type: "application/json" });
|
|
9939
|
+
throwIfCancelled(options.signal, "before the upload");
|
|
9940
|
+
progress(`Uploading SBOM to Codacy (org: ${options.provider}/${options.organization})...`);
|
|
9941
|
+
configureApiToken({ kind: "account-token", token: options.apiToken });
|
|
9942
|
+
const timeout = AbortSignal.timeout(SBOM_UPLOAD_TIMEOUT_MS);
|
|
9943
|
+
try {
|
|
9944
|
+
await uploadImageSbom(
|
|
9945
|
+
options.provider,
|
|
9946
|
+
options.organization,
|
|
9947
|
+
{
|
|
9948
|
+
sbom,
|
|
9949
|
+
imageName: target.name,
|
|
9950
|
+
tag: target.tag,
|
|
9951
|
+
repositoryName: options.repositoryName,
|
|
9952
|
+
environment: options.environment
|
|
9953
|
+
},
|
|
9954
|
+
options.signal ? AbortSignal.any([options.signal, timeout]) : timeout
|
|
9955
|
+
);
|
|
9956
|
+
} catch (err) {
|
|
9957
|
+
throw new Error(`Failed to upload SBOM: ${describeUploadError(err)}`);
|
|
9958
|
+
}
|
|
9959
|
+
return { imageName: target.name, tag: target.tag, format };
|
|
9960
|
+
} finally {
|
|
9961
|
+
await import_promises7.default.rm(tempDir, { recursive: true, force: true });
|
|
9962
|
+
}
|
|
9963
|
+
}
|
|
9964
|
+
function parseVocabulary(values, allowed, label, normalize) {
|
|
9965
|
+
const parsed = values.flatMap((v) => v.split(",")).map((v) => normalize(v.trim())).filter((v) => v.length > 0);
|
|
9966
|
+
if (parsed.length === 0) {
|
|
9967
|
+
throw new Error(`At least one ${label} is required (accepted: ${allowed.join(", ")})`);
|
|
9968
|
+
}
|
|
9969
|
+
for (const value of parsed) {
|
|
9970
|
+
if (!allowed.includes(value)) {
|
|
9971
|
+
throw new Error(`Invalid ${label} "${value}": expected one of ${allowed.join(", ")}`);
|
|
9972
|
+
}
|
|
9973
|
+
}
|
|
9974
|
+
return [...new Set(parsed)];
|
|
9975
|
+
}
|
|
9976
|
+
function parseSbomFormat(format) {
|
|
9977
|
+
const value = format ?? import_tooling9.SBOM_FORMATS[0];
|
|
9978
|
+
if (!import_tooling9.SBOM_FORMATS.includes(value)) {
|
|
9979
|
+
throw new Error(`Invalid format "${value}": expected one of ${import_tooling9.SBOM_FORMATS.join(", ")}`);
|
|
9980
|
+
}
|
|
9981
|
+
return value;
|
|
9982
|
+
}
|
|
9983
|
+
async function resolveImageAdapter() {
|
|
9984
|
+
const descriptor = getRegisteredDescriptors().find(
|
|
9985
|
+
(d) => d.scansContainerImages && !d.unsupported
|
|
9986
|
+
);
|
|
9987
|
+
const adapter = descriptor ? await getAdapter(descriptor.id) : void 0;
|
|
9988
|
+
if (!adapter?.containerImage) {
|
|
9989
|
+
throw new Error(
|
|
9990
|
+
"No registered tool can scan container images. Call registerBuiltinAdapters() first."
|
|
9991
|
+
);
|
|
9992
|
+
}
|
|
9993
|
+
return adapter;
|
|
9994
|
+
}
|
|
9995
|
+
function makeBaseContext(logger, signal) {
|
|
9996
|
+
return {
|
|
9997
|
+
repositoryRoot: process.cwd(),
|
|
9998
|
+
globalDir: (0, import_tooling9.resolveGlobalDir)(),
|
|
9999
|
+
platform: { os: import_os5.default.platform(), arch: import_os5.default.arch() },
|
|
10000
|
+
logger,
|
|
10001
|
+
runner: "local",
|
|
10002
|
+
...signal ? { signal } : {}
|
|
10003
|
+
};
|
|
10004
|
+
}
|
|
10005
|
+
async function prepareTool(adapter, baseCtx, logger) {
|
|
10006
|
+
const ctx = makeToolContext(baseCtx, adapter, []);
|
|
10007
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10008
|
+
const availability = await adapter.checkAvailability(ctx);
|
|
10009
|
+
if (availability.available) {
|
|
10010
|
+
const status = {
|
|
10011
|
+
toolId: adapter.id,
|
|
10012
|
+
displayName: adapter.displayName,
|
|
10013
|
+
version: availability.version ?? adapter.preferredVersion,
|
|
10014
|
+
installation: availability.installation ?? "global",
|
|
10015
|
+
runner: "local"
|
|
10016
|
+
};
|
|
10017
|
+
const capability = { ready: [status], unavailable: [], checkedAt };
|
|
10018
|
+
await updateStaleManagedTools(capability, [adapter], baseCtx, logger);
|
|
10019
|
+
return { ok: true, capability };
|
|
10020
|
+
}
|
|
10021
|
+
const outcome = await installMissingTool(adapter, ctx, logger);
|
|
10022
|
+
if (outcome.kind === "installed") {
|
|
10023
|
+
return {
|
|
10024
|
+
ok: true,
|
|
10025
|
+
capability: {
|
|
10026
|
+
ready: [
|
|
10027
|
+
{
|
|
10028
|
+
toolId: adapter.id,
|
|
10029
|
+
displayName: adapter.displayName,
|
|
10030
|
+
version: outcome.version,
|
|
10031
|
+
installation: outcome.installation,
|
|
10032
|
+
runner: "local"
|
|
10033
|
+
}
|
|
10034
|
+
],
|
|
10035
|
+
unavailable: [],
|
|
10036
|
+
checkedAt
|
|
10037
|
+
}
|
|
10038
|
+
};
|
|
10039
|
+
}
|
|
10040
|
+
const error = outcome.kind === "failed" ? outcome.error : {
|
|
10041
|
+
toolId: adapter.id,
|
|
10042
|
+
phase: "toolInstall",
|
|
10043
|
+
kind: "NotInstalled",
|
|
10044
|
+
message: `${adapter.displayName} is not installed and cannot be installed automatically: ${availability.reason ?? "not found"}`,
|
|
10045
|
+
level: "error"
|
|
10046
|
+
};
|
|
10047
|
+
return {
|
|
10048
|
+
ok: false,
|
|
10049
|
+
error,
|
|
10050
|
+
capability: {
|
|
10051
|
+
ready: [],
|
|
10052
|
+
unavailable: [
|
|
10053
|
+
{
|
|
10054
|
+
toolId: adapter.id,
|
|
10055
|
+
displayName: adapter.displayName,
|
|
10056
|
+
reason: error.message,
|
|
10057
|
+
...availability.installHint ? { remediation: availability.installHint } : {}
|
|
10058
|
+
}
|
|
10059
|
+
],
|
|
10060
|
+
checkedAt
|
|
10061
|
+
}
|
|
10062
|
+
};
|
|
10063
|
+
}
|
|
10064
|
+
function emptyCapability() {
|
|
10065
|
+
return { ready: [], unavailable: [], checkedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
10066
|
+
}
|
|
10067
|
+
function cancelledError(when) {
|
|
10068
|
+
return {
|
|
10069
|
+
toolId: "",
|
|
10070
|
+
phase: "toolInvoke",
|
|
10071
|
+
kind: "Cancelled",
|
|
10072
|
+
message: `Container image scan cancelled by caller ${when}`,
|
|
10073
|
+
level: "error"
|
|
10074
|
+
};
|
|
10075
|
+
}
|
|
10076
|
+
function throwIfCancelled(signal, when) {
|
|
10077
|
+
if (signal?.aborted) throw new Error(`SBOM upload cancelled by caller ${when}`);
|
|
10078
|
+
}
|
|
10079
|
+
function imageResult(startedAt, capability, issues, errors, toolResults, patternSummary) {
|
|
10080
|
+
const completedAt = /* @__PURE__ */ new Date();
|
|
10081
|
+
return {
|
|
10082
|
+
metadata: {
|
|
10083
|
+
startedAt: startedAt.toISOString(),
|
|
10084
|
+
completedAt: completedAt.toISOString(),
|
|
10085
|
+
durationMs: completedAt.getTime() - startedAt.getTime(),
|
|
10086
|
+
repositoryRoot: process.cwd(),
|
|
10087
|
+
executionMode: "auto-install"
|
|
10088
|
+
},
|
|
10089
|
+
capability,
|
|
10090
|
+
issues,
|
|
10091
|
+
errors,
|
|
10092
|
+
toolResults,
|
|
10093
|
+
...patternSummary ? { patternSummary } : {}
|
|
10094
|
+
};
|
|
10095
|
+
}
|
|
10096
|
+
function describeUploadError(err) {
|
|
10097
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
10098
|
+
return "cancelled by caller";
|
|
10099
|
+
}
|
|
10100
|
+
if (err instanceof Error && err.name === "TimeoutError") {
|
|
10101
|
+
return `timed out after ${SBOM_UPLOAD_TIMEOUT_MS / 6e4} minutes`;
|
|
10102
|
+
}
|
|
10103
|
+
const apiError = err;
|
|
10104
|
+
if (typeof apiError?.status === "number") {
|
|
10105
|
+
const body = typeof apiError.body === "string" ? apiError.body : JSON.stringify(apiError.body ?? "");
|
|
10106
|
+
return `API returned status ${apiError.status}${body && body !== '""' ? `: ${body.slice(0, 500)}` : ""}`;
|
|
10107
|
+
}
|
|
10108
|
+
return err instanceof Error ? err.message : String(err);
|
|
10109
|
+
}
|
|
8878
10110
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8879
10111
|
0 && (module.exports = {
|
|
10112
|
+
DEFAULT_IMAGE_SCAN_PKG_TYPES,
|
|
10113
|
+
DEFAULT_IMAGE_SCAN_SEVERITIES,
|
|
8880
10114
|
analyze,
|
|
8881
10115
|
applyCodacyYamlExcludes,
|
|
8882
10116
|
applyConfigOperation,
|
|
10117
|
+
buildCodacyrc,
|
|
10118
|
+
buildPatternTranslator,
|
|
8883
10119
|
buildPayloads,
|
|
8884
10120
|
buildToolConfig,
|
|
8885
10121
|
clearAdapters,
|
|
@@ -8904,6 +10140,7 @@ async function safeReadBody(res) {
|
|
|
8904
10140
|
initDefaultConfig,
|
|
8905
10141
|
initLocalConfig,
|
|
8906
10142
|
initRemoteConfig,
|
|
10143
|
+
installTools,
|
|
8907
10144
|
intersectConfigs,
|
|
8908
10145
|
listDiffFiles,
|
|
8909
10146
|
listIgnoredFiles,
|
|
@@ -8912,17 +10149,22 @@ async function safeReadBody(res) {
|
|
|
8912
10149
|
loadCredentials,
|
|
8913
10150
|
mergeConfigs,
|
|
8914
10151
|
parseAutoFilters,
|
|
10152
|
+
parseCodacyrc,
|
|
8915
10153
|
parseReport,
|
|
10154
|
+
partitionContainerErrors,
|
|
8916
10155
|
pruneEmptyTools,
|
|
8917
10156
|
readBaselineConfig,
|
|
8918
10157
|
readCodacyConfig,
|
|
8919
10158
|
readCodacyYaml,
|
|
10159
|
+
readCodacyrc,
|
|
8920
10160
|
registerAdapter,
|
|
8921
10161
|
registerLazyAdapter,
|
|
8922
10162
|
registerUnsupportedTool,
|
|
10163
|
+
registryLookup,
|
|
8923
10164
|
relativizeLocalConfigPaths,
|
|
8924
10165
|
resolveApiToken,
|
|
8925
10166
|
resolveBaselinePath,
|
|
10167
|
+
resolveCodacyrc,
|
|
8926
10168
|
resolveConfigPath,
|
|
8927
10169
|
resolveDiscoveryExcludes,
|
|
8928
10170
|
resolveEngineToAdapter,
|
|
@@ -8932,9 +10174,14 @@ async function safeReadBody(res) {
|
|
|
8932
10174
|
resolveRemoteAuth,
|
|
8933
10175
|
resolveRepositoryRoot,
|
|
8934
10176
|
resolveResultsApiBaseUrl,
|
|
10177
|
+
resolveToolName,
|
|
8935
10178
|
rotateLogFiles,
|
|
8936
10179
|
saveCredentials,
|
|
10180
|
+
scanContainerImage,
|
|
10181
|
+
serializeCodacyrc,
|
|
10182
|
+
toEnginePatternId,
|
|
8937
10183
|
updateConfigIncremental,
|
|
10184
|
+
uploadImageSbom,
|
|
8938
10185
|
uploadReport,
|
|
8939
10186
|
validateApiToken,
|
|
8940
10187
|
writeBaselineConfig,
|