@codacy/analysis-runner 0.16.0 → 0.18.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 +103 -7
- package/dist/index.js +430 -72
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -469,8 +469,31 @@ declare function listIgnoredFiles(provider: string, org: string, repo: string):
|
|
|
469
469
|
* extensions/filenames against LANGUAGE_CONFIG. When `precomputedFiles` is
|
|
470
470
|
* provided, uses that list directly instead of running `git ls-files`.
|
|
471
471
|
* Returns languages sorted alphabetically for deterministic output.
|
|
472
|
+
*
|
|
473
|
+
* `exclude` (optional) drops files matching those globs before detection, so a
|
|
474
|
+
* language whose only files live in excluded paths is not reported. Every caller
|
|
475
|
+
* that turns the result into tool selection must pass the excludes `analyze`
|
|
476
|
+
* will apply — a language detected from a file `analyze` never reads produces a
|
|
477
|
+
* tool that analyzes zero files. Applied to `precomputedFiles` too (filtering is
|
|
478
|
+
* idempotent, so pre-filtered lists are safe to pass).
|
|
479
|
+
*/
|
|
480
|
+
declare function detectLanguages(repositoryRoot: string, precomputedFiles?: string[], exclude?: readonly string[]): Promise<Language[]>;
|
|
481
|
+
/**
|
|
482
|
+
* Options shared by the init modes that derive configuration from the local
|
|
483
|
+
* repository (`local`, `default`, `auto`).
|
|
472
484
|
*/
|
|
473
|
-
|
|
485
|
+
interface InitDiscoveryOptions {
|
|
486
|
+
/**
|
|
487
|
+
* The config currently on disk, when re-generating via `update-config`.
|
|
488
|
+
*
|
|
489
|
+
* Its `exclude` (global and per-tool) is unioned with the freshly-read
|
|
490
|
+
* `.codacy.yaml` to constrain re-discovery, so excludes the user hand-wrote
|
|
491
|
+
* into `codacy.config.json` — or that arrived from Codacy Cloud's
|
|
492
|
+
* ignored-files list — don't get re-populated with tools that analyze zero
|
|
493
|
+
* files. Omit for a fresh `init`, where only `.codacy.yaml` applies.
|
|
494
|
+
*/
|
|
495
|
+
existingConfig?: CodacyConfig;
|
|
496
|
+
}
|
|
474
497
|
/** Stats returned alongside the config from `initLocalConfig`. */
|
|
475
498
|
interface InitLocalStats {
|
|
476
499
|
/** Whether a .codacy.yaml file was found and its exclude paths were applied. */
|
|
@@ -482,7 +505,7 @@ interface InitLocalStats {
|
|
|
482
505
|
* that have a local configuration file present in the repository.
|
|
483
506
|
* If a .codacy.yaml file is present, its exclude paths are merged into the config.
|
|
484
507
|
*/
|
|
485
|
-
declare function initLocalConfig(repositoryRoot: string, adapters: Readonly<ToolAdapter>[]): Promise<{
|
|
508
|
+
declare function initLocalConfig(repositoryRoot: string, adapters: Readonly<ToolAdapter>[], options?: InitDiscoveryOptions): Promise<{
|
|
486
509
|
config: CodacyConfig;
|
|
487
510
|
stats: InitLocalStats;
|
|
488
511
|
}>;
|
|
@@ -507,7 +530,7 @@ interface InitDefaultStats {
|
|
|
507
530
|
*
|
|
508
531
|
* Returns both the config and stats so the caller can report what happened.
|
|
509
532
|
*/
|
|
510
|
-
declare function initDefaultConfig(repositoryRoot: string, adapters: Readonly<ToolAdapter>[]): Promise<{
|
|
533
|
+
declare function initDefaultConfig(repositoryRoot: string, adapters: Readonly<ToolAdapter>[], options?: InitDiscoveryOptions): Promise<{
|
|
511
534
|
config: CodacyConfig;
|
|
512
535
|
stats: InitDefaultStats;
|
|
513
536
|
}>;
|
|
@@ -586,7 +609,12 @@ interface InitAutoStats {
|
|
|
586
609
|
*
|
|
587
610
|
* Severity and category filters are ANDed across dimensions, ORed within.
|
|
588
611
|
*
|
|
589
|
-
*
|
|
612
|
+
* Tool-level overlap (two *different* tools that lint the same stack, e.g.
|
|
613
|
+
* ESLint and Biome) is resolved by `deduplicateToolFamilies` via each
|
|
614
|
+
* descriptor's `alternativeGroup` — only one member of a group is selected.
|
|
615
|
+
*
|
|
616
|
+
* **Known limitation (v1):** *Pattern*-level overlap within a single tool is
|
|
617
|
+
* not yet resolved — e.g. when both TypeScript and JavaScript are detected,
|
|
590
618
|
* ESLint core rules and their `@typescript-eslint` equivalents may both be
|
|
591
619
|
* enabled, potentially causing duplicate findings. This will be addressed in v2
|
|
592
620
|
* via conflict-resolution metadata in the mapping files.
|
|
@@ -599,7 +627,7 @@ interface InitAutoStats {
|
|
|
599
627
|
* those patterns are gated on detected frameworks exactly like supported tools;
|
|
600
628
|
* tools without mappings have every committed pattern treated as universal.
|
|
601
629
|
*/
|
|
602
|
-
declare function initAutoConfig(repositoryRoot: string, adapters: Readonly<ToolAdapter>[], descriptors: AdapterDescriptor[], filters?: AutoFilters, onDiscoveryComplete?: (result: DiscoveryResult) => void, rawFilterString?: string, loadUnsupportedPatterns?: (toolId: string) => readonly PatternEntry[]): Promise<{
|
|
630
|
+
declare function initAutoConfig(repositoryRoot: string, adapters: Readonly<ToolAdapter>[], descriptors: AdapterDescriptor[], filters?: AutoFilters, onDiscoveryComplete?: (result: DiscoveryResult) => void, rawFilterString?: string, loadUnsupportedPatterns?: (toolId: string) => readonly PatternEntry[], options?: InitDiscoveryOptions): Promise<{
|
|
603
631
|
config: CodacyConfig;
|
|
604
632
|
stats: InitAutoStats;
|
|
605
633
|
}>;
|
|
@@ -661,6 +689,41 @@ declare function resolveEngineToAdapter(engineName: string, adapters: Readonly<T
|
|
|
661
689
|
* Returns a new config object (does not mutate the input).
|
|
662
690
|
*/
|
|
663
691
|
declare function applyCodacyYamlExcludes(config: CodacyConfig, yamlConfig: CodacyYamlConfig, adapters: Readonly<ToolAdapter>[], logger?: Logger): CodacyConfig;
|
|
692
|
+
/**
|
|
693
|
+
* The exclude patterns repository discovery must honor, resolved from every
|
|
694
|
+
* source that will also constrain `analyze`.
|
|
695
|
+
*/
|
|
696
|
+
interface DiscoveryExcludes {
|
|
697
|
+
/** Parsed `.codacy.yaml`, or null when the repository has none. Reused by `applyCodacyYamlExcludes` so the file is read once per init. */
|
|
698
|
+
yamlConfig: CodacyYamlConfig | null;
|
|
699
|
+
/** Global patterns — applied to every file-derived decision (languages, frameworks, tool selection). */
|
|
700
|
+
global: string[];
|
|
701
|
+
/** Per-adapter-ID patterns, applied on top of `global` when deciding whether that specific tool has files. */
|
|
702
|
+
perTool: Map<string, string[]>;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Resolves the exclude patterns that repository discovery must honor so that
|
|
706
|
+
* tool/language selection sees the same file set `analyze` will.
|
|
707
|
+
*
|
|
708
|
+
* Without this, discovery reads the raw `git ls-files` output while `analyze`
|
|
709
|
+
* reads that output minus `exclude` — so `init --auto` enables tools whose only
|
|
710
|
+
* evidence sits in excluded paths (e.g. language fixtures under
|
|
711
|
+
* `**\/test/fixtures/**`), and every one of them then receives zero files.
|
|
712
|
+
*
|
|
713
|
+
* Sources, unioned:
|
|
714
|
+
* - `.codacy.yaml` global `exclude_paths` → `global`
|
|
715
|
+
* - `.codacy.yaml` per-engine `exclude_paths` → `perTool` (engine names resolved
|
|
716
|
+
* via {@link resolveEngineToAdapter}; unresolvable names are skipped)
|
|
717
|
+
* - `existingConfig.exclude` / `existingConfig.tools[].exclude` → both, for
|
|
718
|
+
* `update-config`, so excludes the user hand-wrote into `codacy.config.json`
|
|
719
|
+
* (or that arrived from Codacy Cloud's ignored-files list) also constrain
|
|
720
|
+
* re-discovery
|
|
721
|
+
*
|
|
722
|
+
* `.codacy.yaml` is always re-read rather than trusted from the existing config,
|
|
723
|
+
* since it is the user-editable source of truth and may have changed since the
|
|
724
|
+
* config was generated.
|
|
725
|
+
*/
|
|
726
|
+
declare function resolveDiscoveryExcludes(repositoryRoot: string, adapters: Readonly<ToolAdapter>[], existingConfig?: CodacyConfig): Promise<DiscoveryExcludes>;
|
|
664
727
|
|
|
665
728
|
/**
|
|
666
729
|
* Computes pattern-level summary statistics for analysis results.
|
|
@@ -829,6 +892,18 @@ declare function resolveApiToken(): Promise<string>;
|
|
|
829
892
|
interface DiscoverOptions {
|
|
830
893
|
/** Skip dependency extraction from manifests (only detect files and languages). */
|
|
831
894
|
skipDependencies?: boolean;
|
|
895
|
+
/**
|
|
896
|
+
* Glob patterns to exclude from the scan, on top of `.gitignore`.
|
|
897
|
+
*
|
|
898
|
+
* Callers that feed discovery into configuration decisions **must** pass the
|
|
899
|
+
* same excludes `analyze` will apply (see `resolveDiscoveryExcludes` in
|
|
900
|
+
* `./codacy-yaml`). Otherwise discovery reports languages, frameworks, and
|
|
901
|
+
* libraries that only exist in excluded paths — e.g. a tool-orchestration
|
|
902
|
+
* monorepo whose per-language sample files all live under
|
|
903
|
+
* `**\/test/fixtures/**` looks like a 20-language polyglot repo, and every
|
|
904
|
+
* tool selected from those languages then analyzes zero files.
|
|
905
|
+
*/
|
|
906
|
+
exclude?: readonly string[];
|
|
832
907
|
}
|
|
833
908
|
/**
|
|
834
909
|
* Scans a repository and returns a structured report of languages, file patterns,
|
|
@@ -897,6 +972,10 @@ declare function resolveHeadCommit(repositoryRoot: string): string;
|
|
|
897
972
|
* Codacy patterns). We normalize either format into a flat issue list and build the
|
|
898
973
|
* payload directly.
|
|
899
974
|
*
|
|
975
|
+
* Issues from tools that group many findings under one pattern additionally carry
|
|
976
|
+
* `sourceId` (e.g. Trivy's CVE id), gated on the adapter descriptor's
|
|
977
|
+
* `needsSourceIdUpload` — see {@link buildPayloads}.
|
|
978
|
+
*
|
|
900
979
|
* ## Where it uploads
|
|
901
980
|
*
|
|
902
981
|
* The **legacy v2 API** (`https://api.codacy.com/2.0`), matching the Go CLI: one
|
|
@@ -928,6 +1007,12 @@ interface NormalizedIssue {
|
|
|
928
1007
|
message: string;
|
|
929
1008
|
/** Codacy level string (Info/Warning/High/Error for json; Info/Warning/Error for sarif). */
|
|
930
1009
|
level: string;
|
|
1010
|
+
/**
|
|
1011
|
+
* The tool's own finding id (e.g. a CVE), when the report carries one. Only
|
|
1012
|
+
* forwarded to the payload for tools whose descriptor sets
|
|
1013
|
+
* `needsSourceIdUpload` — see {@link buildPayloads}.
|
|
1014
|
+
*/
|
|
1015
|
+
sourceId?: string;
|
|
931
1016
|
}
|
|
932
1017
|
/** Authentication + routing for the upload. */
|
|
933
1018
|
type UploadAuth = {
|
|
@@ -990,6 +1075,8 @@ interface PayloadIssue {
|
|
|
990
1075
|
line: number;
|
|
991
1076
|
};
|
|
992
1077
|
};
|
|
1078
|
+
/** Present only for tools that need it; see {@link buildPayloads}. */
|
|
1079
|
+
sourceId?: string;
|
|
993
1080
|
}
|
|
994
1081
|
/**
|
|
995
1082
|
* Resolves the base URL for the legacy v2 results API. Defaults to Codacy SaaS;
|
|
@@ -1007,8 +1094,17 @@ declare function parseReport(fileContents: string): NormalizedIssue[];
|
|
|
1007
1094
|
* Builds one v2 payload per tool, grouping issues by file. `resolveShortName` maps
|
|
1008
1095
|
* our internal tool id to the Codacy tool short-name expected in the `tool` field
|
|
1009
1096
|
* (which becomes an S3 key segment consumed downstream).
|
|
1097
|
+
*
|
|
1098
|
+
* `needsSourceId` decides, per tool, whether `sourceId` is included on each issue —
|
|
1099
|
+
* mirroring the Go CLI, which gates it on the tool's `needs_source_id_upload`
|
|
1100
|
+
* manifest flag. This is not cosmetic: `sourceId` is one of the four inputs to
|
|
1101
|
+
* Codacy's deterministic result UUID, so sending it for a tool the cloud engine
|
|
1102
|
+
* doesn't send it for would give the same finding a different UUID. Tools that
|
|
1103
|
+
* don't need it omit the field entirely rather than sending an empty string.
|
|
1104
|
+
* Defaults to never including it, so a caller that hasn't opted in can't change
|
|
1105
|
+
* UUIDs by accident.
|
|
1010
1106
|
*/
|
|
1011
|
-
declare function buildPayloads(issues: NormalizedIssue[], resolveShortName: (toolId: string) => string): ToolPayload[];
|
|
1107
|
+
declare function buildPayloads(issues: NormalizedIssue[], resolveShortName: (toolId: string) => string, needsSourceId?: (toolId: string) => boolean): ToolPayload[];
|
|
1012
1108
|
/**
|
|
1013
1109
|
* Uploads an analysis report to Codacy. Reads and parses the report, resolves tool
|
|
1014
1110
|
* short-names, POSTs one `issuesRemoteResults` per tool, then a single `resultsFinal`.
|
|
@@ -1023,4 +1119,4 @@ declare function buildPayloads(issues: NormalizedIssue[], resolveShortName: (too
|
|
|
1023
1119
|
*/
|
|
1024
1120
|
declare function uploadReport(options: UploadOptions): Promise<UploadResult>;
|
|
1025
1121
|
|
|
1026
|
-
export { type AdapterFactory, type AnalyzeOptions, type AutoFilters, type CategoryFilter, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ConfigOperation, type DiscoverOptions, type ExecutionMode, type IgnoredFilesResult, type InitAutoStats, type InitDefaultStats, type InitLocalStats, type InitRemoteStats, type LoggerOptions, type MergeOptions, type NormalizedIssue, type PatternMetadataLookup, type SeverityFilter, type UploadAuth, type UploadOptions, type UploadResult, analyze, applyCodacyYamlExcludes, applyConfigOperation, buildPayloads, buildToolConfig, clearAdapters, computePatternSummary, configureApiToken, createCodacyConfig, createLogger, createNullLogger, deleteCredentials, detectLanguages, diffConfigs, discover, formatOutput, formatTextSummary, getAdapter, getKnownDisplayNames, getRegisteredAdapters, getRegisteredDescriptors, initAutoConfig, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, intersectConfigs, listDiffFiles, listIgnoredFiles, listPrFiles, listStagedFiles, loadCredentials, mergeConfigs, parseAutoFilters, parseReport, pruneEmptyTools, readBaselineConfig, readCodacyConfig, readCodacyYaml, registerAdapter, registerLazyAdapter, registerUnsupportedTool, resolveApiToken, resolveBaselinePath, resolveConfigPath, resolveEngineToAdapter, resolveHeadCommit, resolveRepositoryRoot, resolveResultsApiBaseUrl, rotateLogFiles, saveCredentials, updateConfigIncremental, uploadReport, validateApiToken, writeBaselineConfig, writeCodacyConfig, writeCodacyConfigFile };
|
|
1122
|
+
export { type AdapterFactory, type AnalyzeOptions, type AutoFilters, type CategoryFilter, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ConfigOperation, type DiscoverOptions, type DiscoveryExcludes, type ExecutionMode, type IgnoredFilesResult, type InitAutoStats, type InitDefaultStats, type InitDiscoveryOptions, type InitLocalStats, type InitRemoteStats, type LoggerOptions, type MergeOptions, type NormalizedIssue, type PatternMetadataLookup, type SeverityFilter, type UploadAuth, type UploadOptions, type UploadResult, analyze, applyCodacyYamlExcludes, applyConfigOperation, buildPayloads, buildToolConfig, clearAdapters, computePatternSummary, configureApiToken, createCodacyConfig, createLogger, createNullLogger, deleteCredentials, detectLanguages, diffConfigs, discover, formatOutput, formatTextSummary, getAdapter, getKnownDisplayNames, getRegisteredAdapters, getRegisteredDescriptors, initAutoConfig, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, intersectConfigs, listDiffFiles, listIgnoredFiles, listPrFiles, listStagedFiles, loadCredentials, mergeConfigs, parseAutoFilters, parseReport, pruneEmptyTools, readBaselineConfig, readCodacyConfig, readCodacyYaml, registerAdapter, registerLazyAdapter, registerUnsupportedTool, resolveApiToken, resolveBaselinePath, resolveConfigPath, resolveDiscoveryExcludes, resolveEngineToAdapter, resolveHeadCommit, resolveRepositoryRoot, resolveResultsApiBaseUrl, rotateLogFiles, saveCredentials, updateConfigIncremental, uploadReport, validateApiToken, writeBaselineConfig, writeCodacyConfig, writeCodacyConfigFile };
|
package/dist/index.js
CHANGED
|
@@ -76,6 +76,7 @@ __export(index_exports, {
|
|
|
76
76
|
resolveApiToken: () => resolveApiToken,
|
|
77
77
|
resolveBaselinePath: () => resolveBaselinePath,
|
|
78
78
|
resolveConfigPath: () => resolveConfigPath,
|
|
79
|
+
resolveDiscoveryExcludes: () => resolveDiscoveryExcludes,
|
|
79
80
|
resolveEngineToAdapter: () => resolveEngineToAdapter,
|
|
80
81
|
resolveHeadCommit: () => resolveHeadCommit,
|
|
81
82
|
resolveProxyConfig: () => import_tooling6.resolveProxyConfig,
|
|
@@ -515,7 +516,7 @@ function isDependencyManifest(relativePath) {
|
|
|
515
516
|
if (DEPENDENCY_MANIFEST_FILES.has(base)) return true;
|
|
516
517
|
return DEPENDENCY_MANIFEST_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
517
518
|
}
|
|
518
|
-
function listRepoFiles(repositoryRoot) {
|
|
519
|
+
function listRepoFiles(repositoryRoot, exclude) {
|
|
519
520
|
try {
|
|
520
521
|
const tracked = (0, import_child_process2.execSync)("git ls-files", {
|
|
521
522
|
cwd: repositoryRoot,
|
|
@@ -534,7 +535,8 @@ function listRepoFiles(repositoryRoot) {
|
|
|
534
535
|
for (const f of untracked.split("\n")) {
|
|
535
536
|
if (f) files.add(f);
|
|
536
537
|
}
|
|
537
|
-
|
|
538
|
+
const all = Array.from(files).sort();
|
|
539
|
+
return exclude && exclude.length > 0 ? applyExclusions(all, [...exclude], []) : all;
|
|
538
540
|
} catch {
|
|
539
541
|
return [];
|
|
540
542
|
}
|
|
@@ -1652,6 +1654,11 @@ function formatSarif(result) {
|
|
|
1652
1654
|
ruleIndex: ruleIndex.get(issue.patternId) ?? 0,
|
|
1653
1655
|
level: mapSeverityToSarif(issue.severity),
|
|
1654
1656
|
message: { text: issue.message },
|
|
1657
|
+
// SARIF has no first-class field for a tool's own finding id, so it rides in
|
|
1658
|
+
// the result property bag. `upload` reads it back from here — without it the
|
|
1659
|
+
// SARIF path would silently drop `sourceId` and produce different Codacy
|
|
1660
|
+
// result UUIDs than the JSON path for the same analysis.
|
|
1661
|
+
...issue.sourceId ? { properties: { sourceId: issue.sourceId } } : {},
|
|
1655
1662
|
locations: [
|
|
1656
1663
|
{
|
|
1657
1664
|
physicalLocation: {
|
|
@@ -1716,7 +1723,8 @@ function formatContainer(result) {
|
|
|
1716
1723
|
patternId: issue.patternId,
|
|
1717
1724
|
line: issue.line,
|
|
1718
1725
|
message: issue.message,
|
|
1719
|
-
suggestion: issue.suggestion
|
|
1726
|
+
suggestion: issue.suggestion,
|
|
1727
|
+
sourceId: issue.sourceId
|
|
1720
1728
|
})
|
|
1721
1729
|
).join("\n");
|
|
1722
1730
|
}
|
|
@@ -2320,6 +2328,7 @@ function updateConfigIncremental(base, current, next) {
|
|
|
2320
2328
|
|
|
2321
2329
|
// src/init.ts
|
|
2322
2330
|
var import_promises4 = __toESM(require("fs/promises"));
|
|
2331
|
+
var import_os3 = __toESM(require("os"));
|
|
2323
2332
|
var import_path8 = __toESM(require("path"));
|
|
2324
2333
|
var import_child_process4 = require("child_process");
|
|
2325
2334
|
var import_tooling5 = require("@codacy/tooling");
|
|
@@ -2401,6 +2410,8 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2401
2410
|
["graphql", { displayName: "GraphQL", kind: "library" }],
|
|
2402
2411
|
["@apollo/client", { displayName: "Apollo Client", kind: "library" }],
|
|
2403
2412
|
["socket.io", { displayName: "Socket.IO", kind: "library" }],
|
|
2413
|
+
["jquery", { displayName: "jQuery", kind: "library" }],
|
|
2414
|
+
["mongodb", { displayName: "MongoDB", kind: "library" }],
|
|
2404
2415
|
["three", { displayName: "Three.js", kind: "library" }],
|
|
2405
2416
|
["d3", { displayName: "D3.js", kind: "library" }],
|
|
2406
2417
|
["chart.js", { displayName: "Chart.js", kind: "library" }],
|
|
@@ -2483,6 +2494,10 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2483
2494
|
["matplotlib", { displayName: "Matplotlib", kind: "library" }],
|
|
2484
2495
|
["seaborn", { displayName: "Seaborn", kind: "library" }],
|
|
2485
2496
|
["plotly", { displayName: "Plotly", kind: "library" }],
|
|
2497
|
+
// MongoDB drivers, one entry per ecosystem — they all gate the same
|
|
2498
|
+
// MongoDB-specific patterns (Opengrep's `java.mongodb.`/`python.pymongo.`).
|
|
2499
|
+
["pymongo", { displayName: "MongoDB", kind: "library" }],
|
|
2500
|
+
["motor", { displayName: "MongoDB", kind: "library" }],
|
|
2486
2501
|
// --- Python — Tools ---
|
|
2487
2502
|
["black", { displayName: "Black", kind: "tool" }],
|
|
2488
2503
|
["ruff", { displayName: "Ruff", kind: "tool" }],
|
|
@@ -2513,9 +2528,16 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2513
2528
|
["minitest", { displayName: "Minitest", kind: "library" }],
|
|
2514
2529
|
["factory_bot", { displayName: "FactoryBot", kind: "library" }],
|
|
2515
2530
|
["dry-validation", { displayName: "dry-validation", kind: "library" }],
|
|
2531
|
+
// NOTE: `faker` and `rake` are Gemfile-scoped (MANIFEST_SCOPED_CATALOG), not
|
|
2532
|
+
// global — they are bare words that also name npm/PyPI packages.
|
|
2516
2533
|
// --- Ruby — Tools ---
|
|
2517
2534
|
["rubocop", { displayName: "RuboCop", kind: "tool" }],
|
|
2518
2535
|
["bundler", { displayName: "Bundler", kind: "tool" }],
|
|
2536
|
+
// Sorbet ships as several gems; any of them means the project is Sorbet-typed.
|
|
2537
|
+
["sorbet", { displayName: "Sorbet", kind: "tool" }],
|
|
2538
|
+
["sorbet-runtime", { displayName: "Sorbet", kind: "tool" }],
|
|
2539
|
+
["sorbet-static", { displayName: "Sorbet", kind: "tool" }],
|
|
2540
|
+
["sorbet-static-and-runtime", { displayName: "Sorbet", kind: "tool" }],
|
|
2519
2541
|
["solargraph", { displayName: "Solargraph", kind: "tool" }],
|
|
2520
2542
|
// --- Java / Kotlin — Frameworks ---
|
|
2521
2543
|
["spring-boot-starter", { displayName: "Spring Boot", kind: "framework" }],
|
|
@@ -2546,6 +2568,10 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2546
2568
|
["okhttp", { displayName: "OkHttp", kind: "library" }],
|
|
2547
2569
|
["retrofit", { displayName: "Retrofit", kind: "library" }],
|
|
2548
2570
|
["spock-core", { displayName: "Spock", kind: "library" }],
|
|
2571
|
+
["mongodb-driver-sync", { displayName: "MongoDB", kind: "library" }],
|
|
2572
|
+
["mongodb-driver-core", { displayName: "MongoDB", kind: "library" }],
|
|
2573
|
+
["mongodb-driver-reactivestreams", { displayName: "MongoDB", kind: "library" }],
|
|
2574
|
+
["mongo-java-driver", { displayName: "MongoDB", kind: "library" }],
|
|
2549
2575
|
// --- Groovy — Frameworks ---
|
|
2550
2576
|
// Grails/GORM artifact IDs vary (grails-core, grails-web, gorm-hibernate5, …);
|
|
2551
2577
|
// the `grails-`/`gorm-` prefix rules below catch the rest.
|
|
@@ -2629,7 +2655,19 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2629
2655
|
["RxSwift", { displayName: "RxSwift", kind: "library" }],
|
|
2630
2656
|
["RealmSwift", { displayName: "Realm", kind: "library" }],
|
|
2631
2657
|
// --- Scala — Frameworks ---
|
|
2658
|
+
// Play is declared as an sbt *plugin*, so `group:artifact` (from
|
|
2659
|
+
// `addSbtPlugin`) is the reliable signal; the library artifacts below only
|
|
2660
|
+
// appear in some builds. `play-json` is deliberately absent — it is a
|
|
2661
|
+
// standalone JSON library used widely outside Play.
|
|
2662
|
+
["com.typesafe.play:sbt-plugin", { displayName: "Play Framework", kind: "framework" }],
|
|
2663
|
+
["org.playframework:sbt-plugin", { displayName: "Play Framework", kind: "framework" }],
|
|
2664
|
+
["play", { displayName: "Play Framework", kind: "framework" }],
|
|
2632
2665
|
["play-server", { displayName: "Play Framework", kind: "framework" }],
|
|
2666
|
+
["play-ws", { displayName: "Play Framework", kind: "framework" }],
|
|
2667
|
+
["play-test", { displayName: "Play Framework", kind: "framework" }],
|
|
2668
|
+
["play-slick", { displayName: "Play Framework", kind: "framework" }],
|
|
2669
|
+
["twirl-api", { displayName: "Play Framework", kind: "framework" }],
|
|
2670
|
+
["scalatestplus-play", { displayName: "Play Framework", kind: "framework" }],
|
|
2633
2671
|
["akka-actor", { displayName: "Akka", kind: "framework" }],
|
|
2634
2672
|
["spray-can", { displayName: "Spray", kind: "framework" }],
|
|
2635
2673
|
// --- Scala — Libraries ---
|
|
@@ -2637,6 +2675,22 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2637
2675
|
["specs2-core", { displayName: "Specs2", kind: "library" }],
|
|
2638
2676
|
["cats-core", { displayName: "Cats", kind: "library" }],
|
|
2639
2677
|
["circe-core", { displayName: "Circe", kind: "library" }],
|
|
2678
|
+
// Slick gates Opengrep's `scala.slick.` SQL-injection patterns.
|
|
2679
|
+
["slick", { displayName: "Slick", kind: "library" }],
|
|
2680
|
+
["slick-hikaricp", { displayName: "Slick", kind: "library" }],
|
|
2681
|
+
// jwt-scala publishes one artifact per JSON backend; all gate the same
|
|
2682
|
+
// hardcoded-secret patterns.
|
|
2683
|
+
["jwt-core", { displayName: "jwt-scala", kind: "library" }],
|
|
2684
|
+
["jwt-circe", { displayName: "jwt-scala", kind: "library" }],
|
|
2685
|
+
["jwt-play-json", { displayName: "jwt-scala", kind: "library" }],
|
|
2686
|
+
["jwt-json4s", { displayName: "jwt-scala", kind: "library" }],
|
|
2687
|
+
["jwt-spray-json", { displayName: "jwt-scala", kind: "library" }],
|
|
2688
|
+
["jwt-upickle", { displayName: "jwt-scala", kind: "library" }],
|
|
2689
|
+
["jwt-argonaut", { displayName: "jwt-scala", kind: "library" }],
|
|
2690
|
+
["munit", { displayName: "MUnit", kind: "library" }],
|
|
2691
|
+
["scalacheck", { displayName: "ScalaCheck", kind: "library" }],
|
|
2692
|
+
["scala-logging", { displayName: "scala-logging", kind: "library" }],
|
|
2693
|
+
["chimney", { displayName: "Chimney", kind: "library" }],
|
|
2640
2694
|
// --- Elixir — Frameworks ---
|
|
2641
2695
|
["phoenix", { displayName: "Phoenix", kind: "framework" }],
|
|
2642
2696
|
["plug", { displayName: "Plug", kind: "framework" }],
|
|
@@ -2712,6 +2766,25 @@ var DEPENDENCY_PREFIX_RULES = [
|
|
|
2712
2766
|
// Groovy — Grails/GORM artifact families (build.gradle artifactIds).
|
|
2713
2767
|
{ prefix: "grails-", classification: { displayName: "Grails", kind: "framework" } },
|
|
2714
2768
|
{ prefix: "gorm-", classification: { displayName: "GORM", kind: "library" } },
|
|
2769
|
+
// Scala — artifact families published one module per feature (sbt artifactIds).
|
|
2770
|
+
// Enumerating members would go stale on every release; the prefixes are
|
|
2771
|
+
// distinctive enough not to collide with other ecosystems.
|
|
2772
|
+
{ prefix: "http4s-", classification: { displayName: "http4s", kind: "framework" } },
|
|
2773
|
+
{ prefix: "akka-", classification: { displayName: "Akka", kind: "framework" } },
|
|
2774
|
+
{ prefix: "pekko-", classification: { displayName: "Pekko", kind: "framework" } },
|
|
2775
|
+
{ prefix: "zio-", classification: { displayName: "ZIO", kind: "library" } },
|
|
2776
|
+
// Must precede any broader `cats-` rule: cats-effect is its own project.
|
|
2777
|
+
{ prefix: "cats-effect", classification: { displayName: "Cats Effect", kind: "library" } },
|
|
2778
|
+
{ prefix: "doobie-", classification: { displayName: "Doobie", kind: "library" } },
|
|
2779
|
+
{ prefix: "fs2-", classification: { displayName: "FS2", kind: "library" } },
|
|
2780
|
+
{ prefix: "circe-", classification: { displayName: "Circe", kind: "library" } },
|
|
2781
|
+
{ prefix: "pureconfig", classification: { displayName: "PureConfig", kind: "library" } },
|
|
2782
|
+
{ prefix: "kamon-", classification: { displayName: "Kamon", kind: "library" } },
|
|
2783
|
+
{ prefix: "flyway-", classification: { displayName: "Flyway", kind: "library" } },
|
|
2784
|
+
{
|
|
2785
|
+
prefix: "testcontainers-scala",
|
|
2786
|
+
classification: { displayName: "Testcontainers", kind: "library" }
|
|
2787
|
+
},
|
|
2715
2788
|
// .NET — Microsoft package families and common test/logging libraries. More
|
|
2716
2789
|
// specific prefixes (Blazor) must precede broader ones (ASP.NET Core).
|
|
2717
2790
|
{
|
|
@@ -2739,7 +2812,14 @@ var DEPENDENCY_PREFIX_RULES = [
|
|
|
2739
2812
|
{ prefix: "Serilog", classification: { displayName: "Serilog", kind: "library" } }
|
|
2740
2813
|
];
|
|
2741
2814
|
var MANIFEST_SCOPED_CATALOG = /* @__PURE__ */ new Map([
|
|
2742
|
-
["package.json", /* @__PURE__ */ new Map([["angular", { displayName: "AngularJS", kind: "framework" }]])]
|
|
2815
|
+
["package.json", /* @__PURE__ */ new Map([["angular", { displayName: "AngularJS", kind: "framework" }]])],
|
|
2816
|
+
[
|
|
2817
|
+
"Gemfile",
|
|
2818
|
+
/* @__PURE__ */ new Map([
|
|
2819
|
+
["faker", { displayName: "Faker", kind: "library" }],
|
|
2820
|
+
["rake", { displayName: "Rake", kind: "tool" }]
|
|
2821
|
+
])
|
|
2822
|
+
]
|
|
2743
2823
|
]);
|
|
2744
2824
|
function classifyDependency(name, manifestBasename) {
|
|
2745
2825
|
if (manifestBasename) {
|
|
@@ -2811,6 +2891,43 @@ var FILE_SIGNAL_RULES = [
|
|
|
2811
2891
|
name: "azure-pipelines.yml",
|
|
2812
2892
|
matches: (f) => f === "azure-pipelines.yml" || f === "azure-pipelines.yaml",
|
|
2813
2893
|
classification: { displayName: "Azure Pipelines", kind: "framework" }
|
|
2894
|
+
},
|
|
2895
|
+
{
|
|
2896
|
+
// Jenkins pipelines are Groovy scripts with no manifest at all — the
|
|
2897
|
+
// Jenkinsfile *is* the declaration. Gates CodeNarc's `jenkins`/
|
|
2898
|
+
// `jenkinspipeline` tagged patterns.
|
|
2899
|
+
name: "Jenkinsfile",
|
|
2900
|
+
matches: (f) => {
|
|
2901
|
+
const base = pathBasename(f);
|
|
2902
|
+
return base === "Jenkinsfile" || base.startsWith("Jenkinsfile.");
|
|
2903
|
+
},
|
|
2904
|
+
classification: { displayName: "Jenkins", kind: "tool" }
|
|
2905
|
+
},
|
|
2906
|
+
{
|
|
2907
|
+
// A `sorbet/` directory (typically `sorbet/config` + RBI files) marks a
|
|
2908
|
+
// Sorbet-typed Ruby project even when the gem is pulled in transitively.
|
|
2909
|
+
name: "sorbet/config",
|
|
2910
|
+
matches: (f) => f === "sorbet/config" || f.endsWith("/sorbet/config"),
|
|
2911
|
+
classification: { displayName: "Sorbet", kind: "tool" }
|
|
2912
|
+
},
|
|
2913
|
+
{
|
|
2914
|
+
name: "Rakefile",
|
|
2915
|
+
matches: (f) => {
|
|
2916
|
+
const base = pathBasename(f);
|
|
2917
|
+
return base === "Rakefile" || base === "rakefile" || base.endsWith(".rake");
|
|
2918
|
+
},
|
|
2919
|
+
classification: { displayName: "Rake", kind: "tool" }
|
|
2920
|
+
},
|
|
2921
|
+
{
|
|
2922
|
+
// Play's routes file — `conf/routes`, plus the `conf/*.routes` sub-files a
|
|
2923
|
+
// multi-module app includes from it (Codacy's own website uses
|
|
2924
|
+
// `app/conf/website.routes`, `modules/legacy-api/conf/api.routes`). Nothing
|
|
2925
|
+
// but Play uses this layout, and it is version-independent, unlike the
|
|
2926
|
+
// artifact names. Note `conf/application.conf` is NOT a signal: plain
|
|
2927
|
+
// Typesafe Config uses the same filename without any Play involvement.
|
|
2928
|
+
name: "conf/routes",
|
|
2929
|
+
matches: (f) => /(^|\/)conf\/([^/]+\.)?routes$/.test(f),
|
|
2930
|
+
classification: { displayName: "Play Framework", kind: "framework" }
|
|
2814
2931
|
}
|
|
2815
2932
|
];
|
|
2816
2933
|
function detectFileSignals(files) {
|
|
@@ -2975,18 +3092,76 @@ function parsePackageJson(content) {
|
|
|
2975
3092
|
return [];
|
|
2976
3093
|
}
|
|
2977
3094
|
}
|
|
3095
|
+
function parseRequirementLine(rawLine) {
|
|
3096
|
+
const line = rawLine.trim();
|
|
3097
|
+
if (!line || line.startsWith("#") || line.startsWith("-")) return void 0;
|
|
3098
|
+
const noComment = line.split("#")[0].trim();
|
|
3099
|
+
if (!noComment) return void 0;
|
|
3100
|
+
const match = noComment.match(/^([a-zA-Z0-9_.-]+(?:\[[^\]]*\])?)\s*([<>=!~@;].*)?$/);
|
|
3101
|
+
if (!match) return void 0;
|
|
3102
|
+
const rawName = match[1].replace(/\[.*\]$/, "");
|
|
3103
|
+
return { name: rawName.toLowerCase().replace(/_/g, "-"), version: match[2]?.trim() };
|
|
3104
|
+
}
|
|
2978
3105
|
function parseRequirementsTxt(content) {
|
|
2979
3106
|
const deps = [];
|
|
2980
3107
|
for (const rawLine of content.split("\n")) {
|
|
2981
|
-
const
|
|
2982
|
-
if (
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3108
|
+
const dep = parseRequirementLine(rawLine);
|
|
3109
|
+
if (dep) deps.push(dep);
|
|
3110
|
+
}
|
|
3111
|
+
return deps;
|
|
3112
|
+
}
|
|
3113
|
+
function sliceBalanced(content, fromIndex, open, close) {
|
|
3114
|
+
const start = content.indexOf(open, fromIndex);
|
|
3115
|
+
if (start === -1) return void 0;
|
|
3116
|
+
let depth = 0;
|
|
3117
|
+
let quote;
|
|
3118
|
+
for (let i = start; i < content.length; i++) {
|
|
3119
|
+
const ch = content[i];
|
|
3120
|
+
if (quote) {
|
|
3121
|
+
if (ch === "\\") i++;
|
|
3122
|
+
else if (ch === quote) quote = void 0;
|
|
3123
|
+
continue;
|
|
3124
|
+
}
|
|
3125
|
+
if (ch === '"' || ch === "'") {
|
|
3126
|
+
quote = ch;
|
|
3127
|
+
continue;
|
|
3128
|
+
}
|
|
3129
|
+
if (ch === open) depth++;
|
|
3130
|
+
else if (ch === close && --depth === 0) {
|
|
3131
|
+
return { body: content.slice(start + 1, i), endIndex: i };
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
return void 0;
|
|
3135
|
+
}
|
|
3136
|
+
function parseSetupPy(content) {
|
|
3137
|
+
const deps = [];
|
|
3138
|
+
const collectQuoted = (block) => {
|
|
3139
|
+
for (const quoted of block.matchAll(/["']([^"']+)["']/g)) {
|
|
3140
|
+
const dep = parseRequirementLine(quoted[1]);
|
|
3141
|
+
if (dep) deps.push(dep);
|
|
3142
|
+
}
|
|
3143
|
+
};
|
|
3144
|
+
const valueStartOf = (key) => {
|
|
3145
|
+
const match = new RegExp(`\\b${key}\\s*=\\s*`).exec(content);
|
|
3146
|
+
return match ? match.index + match[0].length : -1;
|
|
3147
|
+
};
|
|
3148
|
+
for (const key of ["install_requires", "tests_require", "setup_requires"]) {
|
|
3149
|
+
const start = valueStartOf(key);
|
|
3150
|
+
if (start === -1 || content[start] !== "[") continue;
|
|
3151
|
+
const list = sliceBalanced(content, start, "[", "]");
|
|
3152
|
+
if (list) collectQuoted(list.body);
|
|
3153
|
+
}
|
|
3154
|
+
const extrasStart = valueStartOf("extras_require");
|
|
3155
|
+
if (extrasStart !== -1 && content[extrasStart] === "{") {
|
|
3156
|
+
const dict = sliceBalanced(content, extrasStart, "{", "}");
|
|
3157
|
+
if (dict) {
|
|
3158
|
+
let cursor = 0;
|
|
3159
|
+
while (cursor < dict.body.length) {
|
|
3160
|
+
const list = sliceBalanced(dict.body, cursor, "[", "]");
|
|
3161
|
+
if (!list) break;
|
|
3162
|
+
collectQuoted(list.body);
|
|
3163
|
+
cursor = list.endIndex + 1;
|
|
3164
|
+
}
|
|
2990
3165
|
}
|
|
2991
3166
|
}
|
|
2992
3167
|
return deps;
|
|
@@ -3104,15 +3279,52 @@ function parseBuildGradle(content) {
|
|
|
3104
3279
|
}
|
|
3105
3280
|
return deps;
|
|
3106
3281
|
}
|
|
3282
|
+
function parseVersionCatalogToml(content) {
|
|
3283
|
+
const deps = [];
|
|
3284
|
+
let inLibraries = false;
|
|
3285
|
+
for (const rawLine of content.split("\n")) {
|
|
3286
|
+
const line = rawLine.trim();
|
|
3287
|
+
if (line === "" || line.startsWith("#")) continue;
|
|
3288
|
+
if (line.startsWith("[")) {
|
|
3289
|
+
inLibraries = /^\[\s*libraries\s*\]/.test(line);
|
|
3290
|
+
continue;
|
|
3291
|
+
}
|
|
3292
|
+
if (!inLibraries) continue;
|
|
3293
|
+
const moduleMatch = line.match(/\bmodule\s*=\s*["'][^"':]+:([^"']+)["']/);
|
|
3294
|
+
const nameMatch = line.match(/\bname\s*=\s*["']([^"']+)["']/);
|
|
3295
|
+
const versionMatch = line.match(/\bversion\s*=\s*["']([^"']+)["']/);
|
|
3296
|
+
if (moduleMatch || nameMatch) {
|
|
3297
|
+
deps.push({
|
|
3298
|
+
name: (moduleMatch?.[1] ?? nameMatch?.[1] ?? "").trim(),
|
|
3299
|
+
version: versionMatch?.[1]?.trim()
|
|
3300
|
+
});
|
|
3301
|
+
continue;
|
|
3302
|
+
}
|
|
3303
|
+
const shorthand = line.match(/^[A-Za-z0-9_.-]+\s*=\s*["'][^"':]+:([^"':]+)(?::([^"']+))?["']/);
|
|
3304
|
+
if (shorthand) {
|
|
3305
|
+
deps.push({ name: shorthand[1].trim(), version: shorthand[2]?.trim() });
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
return deps;
|
|
3309
|
+
}
|
|
3107
3310
|
function parseBuildSbt(content) {
|
|
3108
3311
|
const deps = [];
|
|
3109
|
-
const depRegex = /"([^"]+)"\s*%%?\s*"([^"]+)"\s
|
|
3312
|
+
const depRegex = /"([^"]+)"\s*%%?\s*"([^"]+)"\s*%(?!%)\s*(?:"([^"]+)")?/g;
|
|
3110
3313
|
let match;
|
|
3111
3314
|
while ((match = depRegex.exec(content)) !== null) {
|
|
3112
3315
|
deps.push({ name: match[2], version: match[3] });
|
|
3113
3316
|
}
|
|
3114
3317
|
return deps;
|
|
3115
3318
|
}
|
|
3319
|
+
function parseSbtBuildDefinition(content) {
|
|
3320
|
+
const deps = parseBuildSbt(content);
|
|
3321
|
+
const pluginRegex = /addSbtPlugin\s*\(\s*"([^"]+)"\s*%%?\s*"([^"]+)"/g;
|
|
3322
|
+
let match;
|
|
3323
|
+
while ((match = pluginRegex.exec(content)) !== null) {
|
|
3324
|
+
deps.push({ name: `${match[1]}:${match[2]}` });
|
|
3325
|
+
}
|
|
3326
|
+
return deps;
|
|
3327
|
+
}
|
|
3116
3328
|
function parseCargoToml(content) {
|
|
3117
3329
|
const deps = [];
|
|
3118
3330
|
let inDepsSection = false;
|
|
@@ -3262,6 +3474,8 @@ var MANIFEST_PARSERS = /* @__PURE__ */ new Map([
|
|
|
3262
3474
|
["pom.xml", parsePomXml],
|
|
3263
3475
|
["build.gradle", parseBuildGradle],
|
|
3264
3476
|
["build.gradle.kts", parseBuildGradle],
|
|
3477
|
+
["libs.versions.toml", parseVersionCatalogToml],
|
|
3478
|
+
["setup.py", parseSetupPy],
|
|
3265
3479
|
["build.sbt", parseBuildSbt],
|
|
3266
3480
|
["Cargo.toml", parseCargoToml],
|
|
3267
3481
|
["composer.json", parseComposerJson],
|
|
@@ -3278,10 +3492,32 @@ var MANIFEST_SUFFIX_PARSERS = /* @__PURE__ */ new Map([
|
|
|
3278
3492
|
[".vbproj", parseCsproj],
|
|
3279
3493
|
[".fsproj", parseCsproj]
|
|
3280
3494
|
]);
|
|
3495
|
+
var MANIFEST_PATH_PARSERS = [
|
|
3496
|
+
{
|
|
3497
|
+
matches: (filePath, basename) => /^requirements[a-zA-Z0-9_.-]*\.(txt|in)$/i.test(basename) || // Any .txt/.in directly inside a `requirements/` directory: base.txt,
|
|
3498
|
+
// dev.txt, prod.in — the other common split-layout convention.
|
|
3499
|
+
/(^|\/)requirements\/[^/]+\.(txt|in)$/i.test(filePath),
|
|
3500
|
+
parser: parseRequirementsTxt
|
|
3501
|
+
},
|
|
3502
|
+
{
|
|
3503
|
+
// sbt build definition: `project/Dependencies.scala`, `project/plugins.sbt`
|
|
3504
|
+
// and the meta-build's own `project/project/*`. Matching by directory rather
|
|
3505
|
+
// than basename is what this rule exists for — the Scala file can be called
|
|
3506
|
+
// anything, and `*.scala` on its own is source code, not a manifest.
|
|
3507
|
+
//
|
|
3508
|
+
// Case-sensitive deliberately: sbt's directory is always lowercase `project`,
|
|
3509
|
+
// while `Project` is an ordinary domain-package name, and reading
|
|
3510
|
+
// `…/domain/Project/Project.scala` as a manifest would be wrong.
|
|
3511
|
+
matches: (filePath) => /(^|\/)project\/[^/]+\.(scala|sbt)$/.test(filePath),
|
|
3512
|
+
parser: parseSbtBuildDefinition
|
|
3513
|
+
}
|
|
3514
|
+
];
|
|
3281
3515
|
|
|
3282
3516
|
// src/discover.ts
|
|
3283
3517
|
async function discover(repositoryRoot, options) {
|
|
3284
|
-
const
|
|
3518
|
+
const unfiltered = listRepoFiles(repositoryRoot);
|
|
3519
|
+
const files = applyExclusions(unfiltered, [...options?.exclude ?? []], []);
|
|
3520
|
+
const excludedFiles = unfiltered.length - files.length;
|
|
3285
3521
|
const filePatterns = extractFilePatterns(files);
|
|
3286
3522
|
const notableFiles = findNotableFiles(files);
|
|
3287
3523
|
const languages = await detectLanguages(repositoryRoot, files);
|
|
@@ -3304,7 +3540,10 @@ async function discover(repositoryRoot, options) {
|
|
|
3304
3540
|
metadata: {
|
|
3305
3541
|
repositoryRoot: import_path6.default.resolve(repositoryRoot),
|
|
3306
3542
|
totalFiles: files.length,
|
|
3307
|
-
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3543
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3544
|
+
// Surfaced so a scan narrowed by excludes is never mistaken for a repo
|
|
3545
|
+
// that genuinely lacks those languages.
|
|
3546
|
+
...excludedFiles > 0 ? { excludedFiles } : {}
|
|
3308
3547
|
}
|
|
3309
3548
|
};
|
|
3310
3549
|
}
|
|
@@ -3335,6 +3574,9 @@ function resolveManifestParser(filePath) {
|
|
|
3335
3574
|
for (const [suffix, parser] of MANIFEST_SUFFIX_PARSERS) {
|
|
3336
3575
|
if (lowerBasename.endsWith(suffix)) return parser;
|
|
3337
3576
|
}
|
|
3577
|
+
for (const rule of MANIFEST_PATH_PARSERS) {
|
|
3578
|
+
if (rule.matches(filePath, basename)) return rule.parser;
|
|
3579
|
+
}
|
|
3338
3580
|
return void 0;
|
|
3339
3581
|
}
|
|
3340
3582
|
async function extractDependencies(repositoryRoot, files) {
|
|
@@ -3468,6 +3710,28 @@ function applyCodacyYamlExcludes(config, yamlConfig, adapters, logger) {
|
|
|
3468
3710
|
function deduplicateStrings(arr) {
|
|
3469
3711
|
return [...new Set(arr)];
|
|
3470
3712
|
}
|
|
3713
|
+
async function resolveDiscoveryExcludes(repositoryRoot, adapters, existingConfig) {
|
|
3714
|
+
const yamlConfig = await readCodacyYaml(repositoryRoot);
|
|
3715
|
+
const global = deduplicateStrings([
|
|
3716
|
+
...existingConfig?.exclude ?? [],
|
|
3717
|
+
...yamlConfig?.excludePaths ?? []
|
|
3718
|
+
]);
|
|
3719
|
+
const perTool = /* @__PURE__ */ new Map();
|
|
3720
|
+
const addToolExcludes = (toolId, patterns) => {
|
|
3721
|
+
if (patterns.length === 0) return;
|
|
3722
|
+
perTool.set(toolId, deduplicateStrings([...perTool.get(toolId) ?? [], ...patterns]));
|
|
3723
|
+
};
|
|
3724
|
+
for (const tool of existingConfig?.tools ?? []) {
|
|
3725
|
+
addToolExcludes(tool.toolId, tool.exclude ?? []);
|
|
3726
|
+
}
|
|
3727
|
+
for (const [engineName, engineConfig] of yamlConfig?.engines ?? []) {
|
|
3728
|
+
if (engineConfig.excludePaths.length === 0) continue;
|
|
3729
|
+
const adapterId = resolveEngineToAdapter(engineName, adapters);
|
|
3730
|
+
if (!adapterId) continue;
|
|
3731
|
+
addToolExcludes(adapterId, engineConfig.excludePaths);
|
|
3732
|
+
}
|
|
3733
|
+
return { yamlConfig, global, perTool };
|
|
3734
|
+
}
|
|
3471
3735
|
|
|
3472
3736
|
// src/api/client/core/OpenAPI.ts
|
|
3473
3737
|
var OpenAPI = {
|
|
@@ -7032,7 +7296,7 @@ function buildAdapterByUuid(adapters, apiTools) {
|
|
|
7032
7296
|
}
|
|
7033
7297
|
return result;
|
|
7034
7298
|
}
|
|
7035
|
-
async function detectLanguages(repositoryRoot, precomputedFiles) {
|
|
7299
|
+
async function detectLanguages(repositoryRoot, precomputedFiles, exclude) {
|
|
7036
7300
|
let files;
|
|
7037
7301
|
if (precomputedFiles) {
|
|
7038
7302
|
files = precomputedFiles;
|
|
@@ -7048,6 +7312,9 @@ async function detectLanguages(repositoryRoot, precomputedFiles) {
|
|
|
7048
7312
|
return [];
|
|
7049
7313
|
}
|
|
7050
7314
|
}
|
|
7315
|
+
if (exclude && exclude.length > 0) {
|
|
7316
|
+
files = applyExclusions(files, [...exclude], []);
|
|
7317
|
+
}
|
|
7051
7318
|
const detected = /* @__PURE__ */ new Set();
|
|
7052
7319
|
for (const file of files) {
|
|
7053
7320
|
const basename = import_path8.default.basename(file);
|
|
@@ -7086,16 +7353,18 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
|
|
|
7086
7353
|
}
|
|
7087
7354
|
return found;
|
|
7088
7355
|
}
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
if (!yamlConfig) return { config, codacyYamlApplied: false };
|
|
7356
|
+
function enrichWithCodacyYaml(config, adapters, excludes) {
|
|
7357
|
+
if (!excludes.yamlConfig) return { config, codacyYamlApplied: false };
|
|
7092
7358
|
return {
|
|
7093
|
-
config: applyCodacyYamlExcludes(config, yamlConfig, adapters),
|
|
7359
|
+
config: applyCodacyYamlExcludes(config, excludes.yamlConfig, adapters),
|
|
7094
7360
|
codacyYamlApplied: true
|
|
7095
7361
|
};
|
|
7096
7362
|
}
|
|
7097
|
-
async function initLocalConfigBase(repositoryRoot, adapters) {
|
|
7098
|
-
const languages = await detectLanguages(
|
|
7363
|
+
async function initLocalConfigBase(repositoryRoot, adapters, excludes) {
|
|
7364
|
+
const languages = await detectLanguages(
|
|
7365
|
+
repositoryRoot,
|
|
7366
|
+
listRepoFiles(repositoryRoot, excludes.global)
|
|
7367
|
+
);
|
|
7099
7368
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
7100
7369
|
const tools = [];
|
|
7101
7370
|
for (const [toolId, configPath] of localConfigs.entries()) {
|
|
@@ -7108,17 +7377,23 @@ async function initLocalConfigBase(repositoryRoot, adapters) {
|
|
|
7108
7377
|
}
|
|
7109
7378
|
return createCodacyConfig({ source: "local", languages, tools });
|
|
7110
7379
|
}
|
|
7111
|
-
async function initLocalConfig(repositoryRoot, adapters) {
|
|
7112
|
-
const
|
|
7113
|
-
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
7114
|
-
baseConfig,
|
|
7380
|
+
async function initLocalConfig(repositoryRoot, adapters, options) {
|
|
7381
|
+
const excludes = await resolveDiscoveryExcludes(
|
|
7115
7382
|
repositoryRoot,
|
|
7116
|
-
adapters
|
|
7383
|
+
adapters,
|
|
7384
|
+
options?.existingConfig
|
|
7117
7385
|
);
|
|
7386
|
+
const baseConfig = await initLocalConfigBase(repositoryRoot, adapters, excludes);
|
|
7387
|
+
const { config, codacyYamlApplied } = enrichWithCodacyYaml(baseConfig, adapters, excludes);
|
|
7118
7388
|
return { config, stats: { codacyYamlApplied } };
|
|
7119
7389
|
}
|
|
7120
|
-
async function initDefaultConfig(repositoryRoot, adapters) {
|
|
7121
|
-
const
|
|
7390
|
+
async function initDefaultConfig(repositoryRoot, adapters, options) {
|
|
7391
|
+
const excludes = await resolveDiscoveryExcludes(
|
|
7392
|
+
repositoryRoot,
|
|
7393
|
+
adapters,
|
|
7394
|
+
options?.existingConfig
|
|
7395
|
+
);
|
|
7396
|
+
const base = await initLocalConfigBase(repositoryRoot, adapters, excludes);
|
|
7122
7397
|
const toolsWithLocalConfig = new Set(
|
|
7123
7398
|
base.tools.filter((t) => t.useLocalConfigurationFile).map((t) => t.toolId)
|
|
7124
7399
|
);
|
|
@@ -7156,11 +7431,7 @@ async function initDefaultConfig(repositoryRoot, adapters) {
|
|
|
7156
7431
|
languages: base.metadata.languages,
|
|
7157
7432
|
tools: updatedTools
|
|
7158
7433
|
});
|
|
7159
|
-
const { config, codacyYamlApplied } =
|
|
7160
|
-
baseConfig,
|
|
7161
|
-
repositoryRoot,
|
|
7162
|
-
adapters
|
|
7163
|
-
);
|
|
7434
|
+
const { config, codacyYamlApplied } = enrichWithCodacyYaml(baseConfig, adapters, excludes);
|
|
7164
7435
|
return {
|
|
7165
7436
|
config,
|
|
7166
7437
|
stats: { totalApiTools: apiTools.length, addedTools, addedUnsupported, codacyYamlApplied }
|
|
@@ -7246,10 +7517,10 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7246
7517
|
if (ignoredFilesResult.files.length > 0) {
|
|
7247
7518
|
baseConfig.exclude = [.../* @__PURE__ */ new Set([...baseConfig.exclude ?? [], ...ignoredFilesResult.files])];
|
|
7248
7519
|
}
|
|
7249
|
-
const { config, codacyYamlApplied } =
|
|
7520
|
+
const { config, codacyYamlApplied } = enrichWithCodacyYaml(
|
|
7250
7521
|
baseConfig,
|
|
7251
|
-
|
|
7252
|
-
adapters
|
|
7522
|
+
adapters,
|
|
7523
|
+
await resolveDiscoveryExcludes(repositoryRoot, adapters)
|
|
7253
7524
|
);
|
|
7254
7525
|
return {
|
|
7255
7526
|
config,
|
|
@@ -7323,8 +7594,13 @@ function hasMatchingFiles(files, filePatterns) {
|
|
|
7323
7594
|
if (matcher.matchAll) return files.length > 0;
|
|
7324
7595
|
return files.some((file) => matcher.matches(file, file.split("/").pop() ?? ""));
|
|
7325
7596
|
}
|
|
7326
|
-
async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString, loadUnsupportedPatterns) {
|
|
7327
|
-
const
|
|
7597
|
+
async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString, loadUnsupportedPatterns, options) {
|
|
7598
|
+
const excludes = await resolveDiscoveryExcludes(
|
|
7599
|
+
repositoryRoot,
|
|
7600
|
+
adapters,
|
|
7601
|
+
options?.existingConfig
|
|
7602
|
+
);
|
|
7603
|
+
const discoveryResult = await discover(repositoryRoot, { exclude: excludes.global });
|
|
7328
7604
|
onDiscoveryComplete?.(discoveryResult);
|
|
7329
7605
|
const detectedKeys = /* @__PURE__ */ new Set();
|
|
7330
7606
|
for (const lang of discoveryResult.languages) detectedKeys.add(lang);
|
|
@@ -7332,15 +7608,26 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7332
7608
|
for (const lib of discoveryResult.libraries) detectedKeys.add(lib.displayName);
|
|
7333
7609
|
const repoLanguages = new Set(discoveryResult.languages);
|
|
7334
7610
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
7335
|
-
const repoFiles = listRepoFiles(repositoryRoot);
|
|
7611
|
+
const repoFiles = listRepoFiles(repositoryRoot, excludes.global);
|
|
7336
7612
|
const descriptorMap = new Map(descriptors.map((d) => [d.id, d]));
|
|
7613
|
+
const perToolFiles = /* @__PURE__ */ new Map();
|
|
7614
|
+
const filesVisibleTo = (toolId) => {
|
|
7615
|
+
const toolExclude = excludes.perTool.get(toolId);
|
|
7616
|
+
if (!toolExclude || toolExclude.length === 0) return repoFiles;
|
|
7617
|
+
let files = perToolFiles.get(toolId);
|
|
7618
|
+
if (!files) {
|
|
7619
|
+
files = applyExclusions(repoFiles, [], [...toolExclude]);
|
|
7620
|
+
perToolFiles.set(toolId, files);
|
|
7621
|
+
}
|
|
7622
|
+
return files;
|
|
7623
|
+
};
|
|
7337
7624
|
const candidates = [];
|
|
7338
7625
|
for (const adapter of adapters) {
|
|
7339
7626
|
if (!adapter.languages.includes("*") && adapter.languages.length > 0 && repoLanguages.size > 0) {
|
|
7340
7627
|
const hasOverlap = adapter.languages.some((lang) => repoLanguages.has(lang));
|
|
7341
7628
|
if (!hasOverlap) continue;
|
|
7342
7629
|
}
|
|
7343
|
-
if (!hasMatchingFiles(
|
|
7630
|
+
if (!hasMatchingFiles(filesVisibleTo(adapter.id), adapter.filePatterns)) continue;
|
|
7344
7631
|
candidates.push(adapter);
|
|
7345
7632
|
}
|
|
7346
7633
|
const deduped = deduplicateToolFamilies(candidates, localConfigs);
|
|
@@ -7368,23 +7655,32 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7368
7655
|
filters
|
|
7369
7656
|
);
|
|
7370
7657
|
if (selectedPatterns.length > 0) {
|
|
7371
|
-
|
|
7658
|
+
const toolEntry = {
|
|
7372
7659
|
toolId: adapter.id,
|
|
7373
7660
|
patterns: selectedPatterns.map((p) => ({ patternId: p.id }))
|
|
7374
|
-
}
|
|
7661
|
+
};
|
|
7662
|
+
tools.push(toolEntry);
|
|
7663
|
+
await applyAdapterParameterSuggestions(
|
|
7664
|
+
adapter,
|
|
7665
|
+
toolEntry,
|
|
7666
|
+
repositoryRoot,
|
|
7667
|
+
filesVisibleTo(adapter.id)
|
|
7668
|
+
);
|
|
7375
7669
|
}
|
|
7376
7670
|
}
|
|
7377
7671
|
let selectedUnsupported = 0;
|
|
7378
|
-
const selectedFamilies = new Set(
|
|
7672
|
+
const selectedFamilies = new Set(
|
|
7673
|
+
tools.map((t) => toolGroupKey(t.toolId, descriptorMap.get(t.toolId)?.alternativeGroup))
|
|
7674
|
+
);
|
|
7379
7675
|
for (const desc of descriptors) {
|
|
7380
7676
|
if (!desc.unsupported) continue;
|
|
7381
7677
|
if (!loadUnsupportedPatterns) break;
|
|
7382
7678
|
if (!desc.languages.includes("*") && desc.languages.length > 0 && repoLanguages.size > 0 && !desc.languages.some((lang) => repoLanguages.has(lang))) {
|
|
7383
7679
|
continue;
|
|
7384
7680
|
}
|
|
7385
|
-
const family =
|
|
7681
|
+
const family = toolGroupKey(desc.id, desc.alternativeGroup);
|
|
7386
7682
|
if (selectedFamilies.has(family)) continue;
|
|
7387
|
-
if (!hasMatchingFiles(
|
|
7683
|
+
if (!hasMatchingFiles(filesVisibleTo(desc.id), desc.filePatterns)) continue;
|
|
7388
7684
|
const patternsMetadata = loadUnsupportedPatterns(desc.id);
|
|
7389
7685
|
if (patternsMetadata.length === 0) continue;
|
|
7390
7686
|
const selectedPatterns = filterAutoPatterns(
|
|
@@ -7409,11 +7705,7 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7409
7705
|
tools,
|
|
7410
7706
|
autoFilters: rawFilterString || void 0
|
|
7411
7707
|
});
|
|
7412
|
-
const { config, codacyYamlApplied } =
|
|
7413
|
-
baseConfig,
|
|
7414
|
-
repositoryRoot,
|
|
7415
|
-
adapters
|
|
7416
|
-
);
|
|
7708
|
+
const { config, codacyYamlApplied } = enrichWithCodacyYaml(baseConfig, adapters, excludes);
|
|
7417
7709
|
return {
|
|
7418
7710
|
config,
|
|
7419
7711
|
stats: {
|
|
@@ -7426,16 +7718,58 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7426
7718
|
}
|
|
7427
7719
|
};
|
|
7428
7720
|
}
|
|
7721
|
+
async function applyAdapterParameterSuggestions(adapter, toolEntry, repositoryRoot, repoFiles) {
|
|
7722
|
+
if (!adapter.suggestParameters) return;
|
|
7723
|
+
const logger = {
|
|
7724
|
+
debug() {
|
|
7725
|
+
},
|
|
7726
|
+
info() {
|
|
7727
|
+
},
|
|
7728
|
+
warn(message, fields) {
|
|
7729
|
+
console.error(`[init --auto] ${adapter.id}: ${message}`, fields ?? "");
|
|
7730
|
+
},
|
|
7731
|
+
error(message, fields) {
|
|
7732
|
+
console.error(`[init --auto] ${adapter.id}: ${message}`, fields ?? "");
|
|
7733
|
+
}
|
|
7734
|
+
};
|
|
7735
|
+
try {
|
|
7736
|
+
const globalDir = import_path8.default.join(import_os3.default.homedir(), ".codacy");
|
|
7737
|
+
const ctx = {
|
|
7738
|
+
repositoryRoot,
|
|
7739
|
+
generatedConfigDir: import_path8.default.join(repositoryRoot, ".codacy", "generated", adapter.id),
|
|
7740
|
+
globalDir,
|
|
7741
|
+
toolInstallDir: import_path8.default.join(globalDir, "tools", adapter.id),
|
|
7742
|
+
targetFiles: routeFilesToTool(repoFiles, adapter, toolEntry),
|
|
7743
|
+
platform: { os: process.platform, arch: process.arch },
|
|
7744
|
+
logger,
|
|
7745
|
+
runner: "local"
|
|
7746
|
+
};
|
|
7747
|
+
const suggestions = await adapter.suggestParameters(ctx);
|
|
7748
|
+
if (!suggestions) return;
|
|
7749
|
+
for (const pattern of toolEntry.patterns) {
|
|
7750
|
+
const params = suggestions[pattern.patternId];
|
|
7751
|
+
if (!params) continue;
|
|
7752
|
+
pattern.parameters = { ...pattern.parameters, ...params };
|
|
7753
|
+
}
|
|
7754
|
+
} catch (err) {
|
|
7755
|
+
logger.error("suggestParameters failed", {
|
|
7756
|
+
error: err instanceof Error ? err.message : String(err)
|
|
7757
|
+
});
|
|
7758
|
+
}
|
|
7759
|
+
}
|
|
7429
7760
|
function toolFamily(id) {
|
|
7430
7761
|
return id.replace(/\d+$/, "");
|
|
7431
7762
|
}
|
|
7763
|
+
function toolGroupKey(id, alternativeGroup) {
|
|
7764
|
+
return alternativeGroup ?? toolFamily(id);
|
|
7765
|
+
}
|
|
7432
7766
|
function deduplicateToolFamilies(candidates, localConfigs) {
|
|
7433
7767
|
const families = /* @__PURE__ */ new Map();
|
|
7434
7768
|
for (const adapter of candidates) {
|
|
7435
|
-
const
|
|
7436
|
-
const group = families.get(
|
|
7769
|
+
const key = toolGroupKey(adapter.id, adapter.alternativeGroup);
|
|
7770
|
+
const group = families.get(key) ?? [];
|
|
7437
7771
|
group.push(adapter);
|
|
7438
|
-
families.set(
|
|
7772
|
+
families.set(key, group);
|
|
7439
7773
|
}
|
|
7440
7774
|
const result = [];
|
|
7441
7775
|
for (const group of families.values()) {
|
|
@@ -7444,16 +7778,28 @@ function deduplicateToolFamilies(candidates, localConfigs) {
|
|
|
7444
7778
|
continue;
|
|
7445
7779
|
}
|
|
7446
7780
|
const withConfig = group.filter((a) => localConfigs.has(a.id));
|
|
7447
|
-
|
|
7448
|
-
|
|
7781
|
+
const pool = withConfig.length > 0 ? withConfig : group;
|
|
7782
|
+
if (pool.length === 1) {
|
|
7783
|
+
result.push(pool[0]);
|
|
7449
7784
|
continue;
|
|
7450
7785
|
}
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
}
|
|
7456
|
-
|
|
7786
|
+
const preferred = pool.filter((a) => a.alternativeGroupDefault);
|
|
7787
|
+
if (preferred.length === 1) {
|
|
7788
|
+
result.push(preferred[0]);
|
|
7789
|
+
continue;
|
|
7790
|
+
}
|
|
7791
|
+
const defaultFamilies = new Set(
|
|
7792
|
+
group.filter((a) => a.alternativeGroupDefault).map((a) => toolFamily(a.id))
|
|
7793
|
+
);
|
|
7794
|
+
const defaultFamilyMembers = defaultFamilies.size === 1 ? pool.filter((a) => defaultFamilies.has(toolFamily(a.id))) : [];
|
|
7795
|
+
const ranked = [...defaultFamilyMembers.length > 0 ? defaultFamilyMembers : pool].sort(
|
|
7796
|
+
(a, b) => {
|
|
7797
|
+
const majorA = parseInt(a.preferredVersion.split(".")[0], 10) || 0;
|
|
7798
|
+
const majorB = parseInt(b.preferredVersion.split(".")[0], 10) || 0;
|
|
7799
|
+
return majorB - majorA;
|
|
7800
|
+
}
|
|
7801
|
+
);
|
|
7802
|
+
result.push(ranked[0]);
|
|
7457
7803
|
}
|
|
7458
7804
|
return result;
|
|
7459
7805
|
}
|
|
@@ -7609,14 +7955,14 @@ var import_tooling6 = require("@codacy/tooling");
|
|
|
7609
7955
|
// src/credentials.ts
|
|
7610
7956
|
var import_crypto = __toESM(require("crypto"));
|
|
7611
7957
|
var import_promises5 = __toESM(require("fs/promises"));
|
|
7612
|
-
var
|
|
7958
|
+
var import_os4 = __toESM(require("os"));
|
|
7613
7959
|
var import_path9 = __toESM(require("path"));
|
|
7614
7960
|
function credentialsPath() {
|
|
7615
|
-
return import_path9.default.join(
|
|
7961
|
+
return import_path9.default.join(import_os4.default.homedir(), ".codacy", "credentials");
|
|
7616
7962
|
}
|
|
7617
7963
|
function deriveKey(salt) {
|
|
7618
|
-
const info =
|
|
7619
|
-
const machineKey = [
|
|
7964
|
+
const info = import_os4.default.userInfo();
|
|
7965
|
+
const machineKey = [import_os4.default.hostname(), info.username, import_os4.default.homedir(), import_os4.default.platform()].join("|");
|
|
7620
7966
|
return import_crypto.default.pbkdf2Sync(machineKey, salt, 1e5, 32, "sha512");
|
|
7621
7967
|
}
|
|
7622
7968
|
async function saveCredentials(token) {
|
|
@@ -7750,11 +8096,12 @@ function parseSarif(runs) {
|
|
|
7750
8096
|
if (!patternId) continue;
|
|
7751
8097
|
const level = mapSarifLevelToCodacy(result.level);
|
|
7752
8098
|
const message = result.message?.text ?? "";
|
|
8099
|
+
const sourceId = result.properties?.sourceId;
|
|
7753
8100
|
for (const loc of result.locations ?? []) {
|
|
7754
8101
|
const filePath = loc.physicalLocation?.artifactLocation?.uri;
|
|
7755
8102
|
const line = loc.physicalLocation?.region?.startLine;
|
|
7756
8103
|
if (!filePath || !line) continue;
|
|
7757
|
-
issues.push({ toolId, patternId, filePath, line, message, level });
|
|
8104
|
+
issues.push({ toolId, patternId, filePath, line, message, level, sourceId });
|
|
7758
8105
|
}
|
|
7759
8106
|
}
|
|
7760
8107
|
}
|
|
@@ -7768,10 +8115,11 @@ function parseAnalysisResult(result) {
|
|
|
7768
8115
|
line: issue.line,
|
|
7769
8116
|
message: issue.message,
|
|
7770
8117
|
// JSON carries the authoritative Codacy severity; pass it through unchanged.
|
|
7771
|
-
level: issue.severity
|
|
8118
|
+
level: issue.severity,
|
|
8119
|
+
sourceId: issue.sourceId
|
|
7772
8120
|
}));
|
|
7773
8121
|
}
|
|
7774
|
-
function buildPayloads(issues, resolveShortName) {
|
|
8122
|
+
function buildPayloads(issues, resolveShortName, needsSourceId = () => false) {
|
|
7775
8123
|
const byTool = /* @__PURE__ */ new Map();
|
|
7776
8124
|
for (const issue of issues) {
|
|
7777
8125
|
const list = byTool.get(issue.toolId);
|
|
@@ -7780,6 +8128,7 @@ function buildPayloads(issues, resolveShortName) {
|
|
|
7780
8128
|
}
|
|
7781
8129
|
const payloads = [];
|
|
7782
8130
|
for (const [toolId, toolIssues] of byTool) {
|
|
8131
|
+
const withSourceId = needsSourceId(toolId);
|
|
7783
8132
|
const byFile = /* @__PURE__ */ new Map();
|
|
7784
8133
|
for (const issue of toolIssues) {
|
|
7785
8134
|
const entry = {
|
|
@@ -7788,7 +8137,8 @@ function buildPayloads(issues, resolveShortName) {
|
|
|
7788
8137
|
filename: issue.filePath,
|
|
7789
8138
|
message: { text: issue.message },
|
|
7790
8139
|
level: issue.level,
|
|
7791
|
-
location: { LineLocation: { line: issue.line } }
|
|
8140
|
+
location: { LineLocation: { line: issue.line } },
|
|
8141
|
+
...withSourceId && issue.sourceId ? { sourceId: issue.sourceId } : {}
|
|
7792
8142
|
}
|
|
7793
8143
|
};
|
|
7794
8144
|
const list = byFile.get(issue.filePath);
|
|
@@ -7820,6 +8170,13 @@ async function buildShortNameResolver(onProgress) {
|
|
|
7820
8170
|
}
|
|
7821
8171
|
return (toolId) => byName.get(toolId.toLowerCase()) ?? toolId.toLowerCase();
|
|
7822
8172
|
}
|
|
8173
|
+
function buildSourceIdResolver() {
|
|
8174
|
+
const needsIt = /* @__PURE__ */ new Set();
|
|
8175
|
+
for (const descriptor of getRegisteredDescriptors()) {
|
|
8176
|
+
if (descriptor.needsSourceIdUpload) needsIt.add(descriptor.id.toLowerCase());
|
|
8177
|
+
}
|
|
8178
|
+
return (toolId) => needsIt.has(toolId.toLowerCase());
|
|
8179
|
+
}
|
|
7823
8180
|
var COORDINATE_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
7824
8181
|
function assertValidCoordinate(kind, value) {
|
|
7825
8182
|
if (!COORDINATE_PATTERN.test(value)) {
|
|
@@ -7859,7 +8216,7 @@ async function uploadReport(options) {
|
|
|
7859
8216
|
onProgress?.("No issues found in the report; nothing to upload.");
|
|
7860
8217
|
}
|
|
7861
8218
|
const resolveShortName = await buildShortNameResolver(onProgress);
|
|
7862
|
-
const payloads = buildPayloads(issues, resolveShortName);
|
|
8219
|
+
const payloads = buildPayloads(issues, resolveShortName, buildSourceIdResolver());
|
|
7863
8220
|
const base = resolveResultsApiBaseUrl();
|
|
7864
8221
|
const urls = buildUrls(base, auth, commitUuid);
|
|
7865
8222
|
const headers = { "content-type": "application/json", ...authHeader(auth) };
|
|
@@ -7960,6 +8317,7 @@ async function safeReadBody(res) {
|
|
|
7960
8317
|
resolveApiToken,
|
|
7961
8318
|
resolveBaselinePath,
|
|
7962
8319
|
resolveConfigPath,
|
|
8320
|
+
resolveDiscoveryExcludes,
|
|
7963
8321
|
resolveEngineToAdapter,
|
|
7964
8322
|
resolveHeadCommit,
|
|
7965
8323
|
resolveProxyConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codacy/analysis-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Core orchestration engine for Codacy analysis",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
"cli-table3": "^0.6",
|
|
13
13
|
"minimatch": "10.2.4",
|
|
14
14
|
"yaml": "^2.6.0",
|
|
15
|
-
"@codacy/tooling": "0.
|
|
15
|
+
"@codacy/tooling": "0.18.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@codacy/openapi-typescript-codegen": "0.0.9",
|
|
19
|
-
"@codacy/tools-dummy-1": "0.
|
|
19
|
+
"@codacy/tools-dummy-1": "0.18.0"
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"author": "Codacy <support@codacy.com> (https://www.codacy.com)",
|