@codacy/analysis-runner 0.13.0 → 0.15.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 +81 -6
- package/dist/index.js +206 -9
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { OutputFormat, Logger, AnalysisResult, ToolAdapter, AdapterDescriptor, CodacyConfig, ConfigurationFileCheckResult, CodacyToolConfig, IssueSeverity, IssueCategory, Language, DiscoveryResult, PatternEntry, Issue, PatternSummary, LogLevel } from '@codacy/tooling';
|
|
2
|
-
export { AnalysisResult, OutputFormat } from '@codacy/tooling';
|
|
1
|
+
import { OutputFormat, Logger, ProxyConfig, AnalysisResult, ToolAdapter, AdapterDescriptor, CodacyConfig, ConfigurationFileCheckResult, CodacyToolConfig, IssueSeverity, IssueCategory, Language, DiscoveryResult, PatternEntry, Issue, PatternSummary, LogLevel } from '@codacy/tooling';
|
|
2
|
+
export { AnalysisResult, OutputFormat, ProxyConfig, configureProxy, resolveProxyConfig } from '@codacy/tooling';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Main analysis entry point — orchestrates the full analysis pipeline.
|
|
@@ -66,6 +66,15 @@ interface AnalyzeOptions {
|
|
|
66
66
|
onToolComplete?: (toolId: string, displayName: string, issueCount: number, durationMs: number) => void;
|
|
67
67
|
/** Called when a tool reports mid-execution progress (e.g. "Linting 50/200 files"). */
|
|
68
68
|
onToolProgress?: (toolId: string, displayName: string, message: string) => void;
|
|
69
|
+
/**
|
|
70
|
+
* Optional proxy/TLS configuration for outbound HTTP (API calls and tool
|
|
71
|
+
* downloads). When provided, it is applied via `configureProxy` at the start of
|
|
72
|
+
* `analyze()`, installing a global fetch dispatcher. Library consumers that
|
|
73
|
+
* make API calls outside `analyze()` (e.g. init) should instead call
|
|
74
|
+
* `configureProxy` themselves once at startup. Omit to rely on environment
|
|
75
|
+
* variables (HTTP(S)_PROXY, NO_PROXY, SSL_CERT_FILE, CODACY_CLI_INSECURE).
|
|
76
|
+
*/
|
|
77
|
+
proxy?: Partial<ProxyConfig>;
|
|
69
78
|
}
|
|
70
79
|
/**
|
|
71
80
|
* Runs analysis on the given repository and returns the unified result.
|
|
@@ -181,6 +190,15 @@ declare function formatTextSummary(result: AnalysisResult, isInspect?: boolean):
|
|
|
181
190
|
* (e.g. synced from Codacy Cloud) while testing a variant side-by-side.
|
|
182
191
|
*/
|
|
183
192
|
declare function resolveConfigPath(repositoryRoot: string, configFile?: string): string;
|
|
193
|
+
/**
|
|
194
|
+
* Resolves the absolute path to the config's baseline snapshot — the config the
|
|
195
|
+
* generator last produced, used by `update-config` to distinguish user-disabled
|
|
196
|
+
* patterns from default-off ones. It is a sibling of the config file
|
|
197
|
+
* (`<name>.json` → `<name>.baseline.json`), so `.codacy/codacy.config.json` maps
|
|
198
|
+
* to `.codacy/codacy.config.baseline.json`. It lives outside `.codacy/generated/`
|
|
199
|
+
* so it is committed and travels with the config.
|
|
200
|
+
*/
|
|
201
|
+
declare function resolveBaselinePath(repositoryRoot: string, configFile?: string): string;
|
|
184
202
|
/**
|
|
185
203
|
* Reads a CodacyConfig from disk.
|
|
186
204
|
* Returns null if the file doesn't exist. Throws on parse/permission errors
|
|
@@ -189,6 +207,19 @@ declare function resolveConfigPath(repositoryRoot: string, configFile?: string):
|
|
|
189
207
|
* Pass `configFile` to read from a non-default location.
|
|
190
208
|
*/
|
|
191
209
|
declare function readCodacyConfig(repositoryRoot: string, configFile?: string): Promise<CodacyConfig | null>;
|
|
210
|
+
/**
|
|
211
|
+
* Reads the config's baseline snapshot (see {@link resolveBaselinePath}).
|
|
212
|
+
* Returns null when no baseline exists (e.g. a config created before baselines
|
|
213
|
+
* were introduced), so `update-config` can fall back gracefully.
|
|
214
|
+
*/
|
|
215
|
+
declare function readBaselineConfig(repositoryRoot: string, configFile?: string): Promise<CodacyConfig | null>;
|
|
216
|
+
/**
|
|
217
|
+
* Writes the config's baseline snapshot next to the config file (see
|
|
218
|
+
* {@link resolveBaselinePath}). No `.codacy/` side effects. Callers write this
|
|
219
|
+
* after every `init`/`update-config` so the next update diffs against the exact
|
|
220
|
+
* generator output. Returns the path written.
|
|
221
|
+
*/
|
|
222
|
+
declare function writeBaselineConfig(repositoryRoot: string, config: CodacyConfig, configFile?: string): Promise<string>;
|
|
192
223
|
/**
|
|
193
224
|
* Writes a CodacyConfig to disk at the exact resolved path, creating only the
|
|
194
225
|
* parent directory. Unlike {@link writeCodacyConfig}, this has NO `.codacy/`
|
|
@@ -261,13 +292,23 @@ declare function createCodacyConfig(params?: {
|
|
|
261
292
|
|
|
262
293
|
/** The set operation to apply between two configs. */
|
|
263
294
|
type ConfigOperation = "merge" | "intersect" | "diff";
|
|
295
|
+
/** Options for {@link mergeConfigs}. */
|
|
296
|
+
interface MergeOptions {
|
|
297
|
+
/**
|
|
298
|
+
* When a pattern exists on both sides, keep dest's parameters instead of the
|
|
299
|
+
* default source-wins behavior. Used by the incremental update so the user's
|
|
300
|
+
* tuned parameters (dest) survive over freshly-generated defaults (source).
|
|
301
|
+
*/
|
|
302
|
+
preferDestParameters?: boolean;
|
|
303
|
+
}
|
|
264
304
|
/**
|
|
265
305
|
* Union of source and dest at the tool and pattern level.
|
|
266
306
|
* Tools present on only one side are carried over as-is; tools on both sides are
|
|
267
|
-
* combined (config-file rule, then per-tool pattern union
|
|
268
|
-
*
|
|
307
|
+
* combined (config-file rule, then per-tool pattern union). Parameters of a
|
|
308
|
+
* pattern on both sides come from source unless `options.preferDestParameters`.
|
|
309
|
+
* Never drops tools.
|
|
269
310
|
*/
|
|
270
|
-
declare function mergeConfigs(source: CodacyConfig, dest: CodacyConfig): CodacyConfig;
|
|
311
|
+
declare function mergeConfigs(source: CodacyConfig, dest: CodacyConfig, options?: MergeOptions): CodacyConfig;
|
|
271
312
|
/**
|
|
272
313
|
* Intersection of source and dest: only tools present in both, and per-tool only
|
|
273
314
|
* the patterns present in both (config-file rule applies when either side uses a
|
|
@@ -286,6 +327,40 @@ declare function intersectConfigs(source: CodacyConfig, dest: CodacyConfig): Cod
|
|
|
286
327
|
declare function diffConfigs(source: CodacyConfig, dest: CodacyConfig): CodacyConfig;
|
|
287
328
|
/** Dispatches to the {@link mergeConfigs}, {@link intersectConfigs}, or {@link diffConfigs}. */
|
|
288
329
|
declare function applyConfigOperation(op: ConfigOperation, source: CodacyConfig, dest: CodacyConfig): CodacyConfig;
|
|
330
|
+
/**
|
|
331
|
+
* Returns a copy of `config` with tools that would do nothing removed — a tool
|
|
332
|
+
* with no enabled patterns and no native config file. Used after the incremental
|
|
333
|
+
* update so a tool whose every pattern was removed (e.g. its language/framework
|
|
334
|
+
* left the repo) drops out entirely. Config-file tools are always kept (they run
|
|
335
|
+
* from their own file). Does not mutate the input.
|
|
336
|
+
*/
|
|
337
|
+
declare function pruneEmptyTools(config: CodacyConfig): CodacyConfig;
|
|
338
|
+
/**
|
|
339
|
+
* Incrementally updates a Codacy config while preserving user customizations.
|
|
340
|
+
*
|
|
341
|
+
* Three-way merge over the enabled-only config model (a disabled pattern is
|
|
342
|
+
* indistinguishable from a default-off one, so a naive re-generate + merge would
|
|
343
|
+
* resurrect patterns the user deliberately removed):
|
|
344
|
+
*
|
|
345
|
+
* - `base` — the config the generator last produced (pre-user-edit baseline)
|
|
346
|
+
* - `current` — the user's live config (baseline + their edits)
|
|
347
|
+
* - `next` — the config the generator produces now (for the current stack)
|
|
348
|
+
*
|
|
349
|
+
* added = next − base (genuinely new tools/patterns to add)
|
|
350
|
+
* removed = base − next (tools/patterns whose stack element is gone)
|
|
351
|
+
* result = (current − removed) + added
|
|
352
|
+
*
|
|
353
|
+
* User-disabled patterns stay absent (they're in `base`, so never in `added`);
|
|
354
|
+
* user-tuned parameters and manual excludes ride along in `current` (dest wins on
|
|
355
|
+
* parameter conflicts via `preferDestParameters`). Identity/provenance metadata
|
|
356
|
+
* (`createdAt`, `source`, organization, …) come from `current`; `updatedAt` is
|
|
357
|
+
* refreshed and `metadata.languages` is refreshed from `next` (an incremental
|
|
358
|
+
* update is effectively a re-discovery, so the recorded stack must reflect the
|
|
359
|
+
* current repository, not the languages captured when the config was first
|
|
360
|
+
* written). Top-level `exclude` is reconciled with the same three-way logic, and
|
|
361
|
+
* any tool left with no patterns and no config file is pruned. Inputs are not mutated.
|
|
362
|
+
*/
|
|
363
|
+
declare function updateConfigIncremental(base: CodacyConfig, current: CodacyConfig, next: CodacyConfig): CodacyConfig;
|
|
289
364
|
|
|
290
365
|
/**
|
|
291
366
|
* auto-filters.ts — Parses and applies --auto filter syntax.
|
|
@@ -778,4 +853,4 @@ declare function discover(repositoryRoot: string, options?: DiscoverOptions): Pr
|
|
|
778
853
|
*/
|
|
779
854
|
declare function getKnownDisplayNames(): Set<string>;
|
|
780
855
|
|
|
781
|
-
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 PatternMetadataLookup, type SeverityFilter, analyze, applyCodacyYamlExcludes, applyConfigOperation, 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, readCodacyConfig, readCodacyYaml, registerAdapter, registerLazyAdapter, registerUnsupportedTool, resolveApiToken, resolveConfigPath, resolveEngineToAdapter, rotateLogFiles, saveCredentials, validateApiToken, writeCodacyConfig, writeCodacyConfigFile };
|
|
856
|
+
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 PatternMetadataLookup, type SeverityFilter, analyze, applyCodacyYamlExcludes, applyConfigOperation, 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, pruneEmptyTools, readBaselineConfig, readCodacyConfig, readCodacyYaml, registerAdapter, registerLazyAdapter, registerUnsupportedTool, resolveApiToken, resolveBaselinePath, resolveConfigPath, resolveEngineToAdapter, rotateLogFiles, saveCredentials, updateConfigIncremental, validateApiToken, writeBaselineConfig, writeCodacyConfig, writeCodacyConfigFile };
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
clearAdapters: () => clearAdapters,
|
|
38
38
|
computePatternSummary: () => computePatternSummary,
|
|
39
39
|
configureApiToken: () => configureApiToken,
|
|
40
|
+
configureProxy: () => import_tooling6.configureProxy,
|
|
40
41
|
createCodacyConfig: () => createCodacyConfig,
|
|
41
42
|
createLogger: () => createLogger,
|
|
42
43
|
createNullLogger: () => createNullLogger,
|
|
@@ -63,17 +64,23 @@ __export(index_exports, {
|
|
|
63
64
|
loadCredentials: () => loadCredentials,
|
|
64
65
|
mergeConfigs: () => mergeConfigs,
|
|
65
66
|
parseAutoFilters: () => parseAutoFilters,
|
|
67
|
+
pruneEmptyTools: () => pruneEmptyTools,
|
|
68
|
+
readBaselineConfig: () => readBaselineConfig,
|
|
66
69
|
readCodacyConfig: () => readCodacyConfig,
|
|
67
70
|
readCodacyYaml: () => readCodacyYaml,
|
|
68
71
|
registerAdapter: () => registerAdapter,
|
|
69
72
|
registerLazyAdapter: () => registerLazyAdapter,
|
|
70
73
|
registerUnsupportedTool: () => registerUnsupportedTool,
|
|
71
74
|
resolveApiToken: () => resolveApiToken,
|
|
75
|
+
resolveBaselinePath: () => resolveBaselinePath,
|
|
72
76
|
resolveConfigPath: () => resolveConfigPath,
|
|
73
77
|
resolveEngineToAdapter: () => resolveEngineToAdapter,
|
|
78
|
+
resolveProxyConfig: () => import_tooling6.resolveProxyConfig,
|
|
74
79
|
rotateLogFiles: () => rotateLogFiles,
|
|
75
80
|
saveCredentials: () => saveCredentials,
|
|
81
|
+
updateConfigIncremental: () => updateConfigIncremental,
|
|
76
82
|
validateApiToken: () => validateApiToken,
|
|
83
|
+
writeBaselineConfig: () => writeBaselineConfig,
|
|
77
84
|
writeCodacyConfig: () => writeCodacyConfig,
|
|
78
85
|
writeCodacyConfigFile: () => writeCodacyConfigFile
|
|
79
86
|
});
|
|
@@ -82,6 +89,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
82
89
|
// src/analyze.ts
|
|
83
90
|
var import_os2 = __toESM(require("os"));
|
|
84
91
|
var import_path4 = __toESM(require("path"));
|
|
92
|
+
var import_fs3 = __toESM(require("fs"));
|
|
85
93
|
var import_child_process3 = require("child_process");
|
|
86
94
|
var import_util = require("util");
|
|
87
95
|
var import_tooling3 = require("@codacy/tooling");
|
|
@@ -154,8 +162,14 @@ var CONFIG_FILE = "codacy.config.json";
|
|
|
154
162
|
function resolveConfigPath(repositoryRoot, configFile) {
|
|
155
163
|
return configFile ? import_path.default.resolve(repositoryRoot, configFile) : import_path.default.join(repositoryRoot, CODACY_DIR, CONFIG_FILE);
|
|
156
164
|
}
|
|
157
|
-
|
|
165
|
+
function resolveBaselinePath(repositoryRoot, configFile) {
|
|
158
166
|
const configPath = resolveConfigPath(repositoryRoot, configFile);
|
|
167
|
+
const dir = import_path.default.dirname(configPath);
|
|
168
|
+
const ext = import_path.default.extname(configPath);
|
|
169
|
+
const stem = import_path.default.basename(configPath, ext);
|
|
170
|
+
return import_path.default.join(dir, `${stem}.baseline${ext}`);
|
|
171
|
+
}
|
|
172
|
+
async function readConfigAtPath(configPath) {
|
|
159
173
|
let raw;
|
|
160
174
|
try {
|
|
161
175
|
raw = await import_promises.default.readFile(configPath, "utf-8");
|
|
@@ -165,10 +179,26 @@ async function readCodacyConfig(repositoryRoot, configFile) {
|
|
|
165
179
|
}
|
|
166
180
|
throw err;
|
|
167
181
|
}
|
|
168
|
-
const
|
|
182
|
+
const parsed = JSON.parse(raw);
|
|
183
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
184
|
+
throw new Error(`Invalid config at ${configPath}: expected a JSON object`);
|
|
185
|
+
}
|
|
186
|
+
const config = parsed;
|
|
169
187
|
coerceParameterValues(config);
|
|
170
188
|
return config;
|
|
171
189
|
}
|
|
190
|
+
async function readCodacyConfig(repositoryRoot, configFile) {
|
|
191
|
+
return readConfigAtPath(resolveConfigPath(repositoryRoot, configFile));
|
|
192
|
+
}
|
|
193
|
+
async function readBaselineConfig(repositoryRoot, configFile) {
|
|
194
|
+
return readConfigAtPath(resolveBaselinePath(repositoryRoot, configFile));
|
|
195
|
+
}
|
|
196
|
+
async function writeBaselineConfig(repositoryRoot, config, configFile) {
|
|
197
|
+
const baselinePath = resolveBaselinePath(repositoryRoot, configFile);
|
|
198
|
+
await import_promises.default.mkdir(import_path.default.dirname(baselinePath), { recursive: true });
|
|
199
|
+
await import_promises.default.writeFile(baselinePath, JSON.stringify(config, null, 2) + "\n");
|
|
200
|
+
return baselinePath;
|
|
201
|
+
}
|
|
172
202
|
function coerceParameterValues(config) {
|
|
173
203
|
for (const tool of config.tools) {
|
|
174
204
|
for (const pattern of tool.patterns) {
|
|
@@ -557,7 +587,7 @@ function routeFilesToTool(files, adapter, toolConfig) {
|
|
|
557
587
|
}
|
|
558
588
|
function filterByTargetFiles(allFiles, targetPatterns) {
|
|
559
589
|
if (targetPatterns.length === 0) return allFiles;
|
|
560
|
-
const normalized = targetPatterns.map((p) => p.replace(/^\.\//, ""));
|
|
590
|
+
const normalized = targetPatterns.map((p) => p.replace(/\\/g, "/").replace(/^\.\//, ""));
|
|
561
591
|
const exact = new Set(normalized);
|
|
562
592
|
const globs = normalized.map((p) => new import_minimatch2.Minimatch(p, { dot: true, matchBase: false })).filter((m) => m.hasMagic());
|
|
563
593
|
const dirPrefixes = normalized.filter((p) => p.endsWith("/"));
|
|
@@ -987,6 +1017,7 @@ async function mapWithConcurrency(items, concurrency, task) {
|
|
|
987
1017
|
}
|
|
988
1018
|
async function analyze(options) {
|
|
989
1019
|
const startedAt = /* @__PURE__ */ new Date();
|
|
1020
|
+
if (options.proxy) (0, import_tooling3.configureProxy)(options.proxy);
|
|
990
1021
|
const mode = options.mode || "standard";
|
|
991
1022
|
const toolTimeout = options.toolTimeout || 9e5;
|
|
992
1023
|
const parallelTools = options.parallelTools || 1;
|
|
@@ -1023,6 +1054,26 @@ async function analyze(options) {
|
|
|
1023
1054
|
[]
|
|
1024
1055
|
);
|
|
1025
1056
|
}
|
|
1057
|
+
const rootStat = import_fs3.default.statSync(options.repositoryRoot, { throwIfNoEntry: false });
|
|
1058
|
+
if (!rootStat?.isDirectory()) {
|
|
1059
|
+
return buildResult(
|
|
1060
|
+
startedAt,
|
|
1061
|
+
options.repositoryRoot,
|
|
1062
|
+
mode,
|
|
1063
|
+
emptyCapability,
|
|
1064
|
+
[],
|
|
1065
|
+
[
|
|
1066
|
+
{
|
|
1067
|
+
toolId: "runner",
|
|
1068
|
+
phase: "requirementCheck",
|
|
1069
|
+
kind: "RepositoryNotFound",
|
|
1070
|
+
message: `${options.repositoryRoot} does not exist or is not a directory. Pass a path to an existing repository, or a quoted glob (e.g. "src/**/*.ts") to scope files.`,
|
|
1071
|
+
level: "error"
|
|
1072
|
+
}
|
|
1073
|
+
],
|
|
1074
|
+
[]
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1026
1077
|
try {
|
|
1027
1078
|
await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
1028
1079
|
cwd: options.repositoryRoot
|
|
@@ -2110,13 +2161,13 @@ function buildConfigFileTool(toolId, source, dest, listFn) {
|
|
|
2110
2161
|
applyToolLevelLists(tool, source, dest, listFn);
|
|
2111
2162
|
return tool;
|
|
2112
2163
|
}
|
|
2113
|
-
function mergePatterns(source, dest) {
|
|
2164
|
+
function mergePatterns(source, dest, preferDest = false) {
|
|
2114
2165
|
const sourceMap = indexByPatternId(source);
|
|
2115
2166
|
const seen = /* @__PURE__ */ new Set();
|
|
2116
2167
|
const result = [];
|
|
2117
2168
|
for (const dp of dest) {
|
|
2118
2169
|
const sp = sourceMap.get(dp.patternId);
|
|
2119
|
-
result.push(clonePattern(sp ?? dp));
|
|
2170
|
+
result.push(clonePattern(preferDest ? dp : sp ?? dp));
|
|
2120
2171
|
seen.add(dp.patternId);
|
|
2121
2172
|
}
|
|
2122
2173
|
for (const sp of source) {
|
|
@@ -2137,12 +2188,12 @@ function diffPatterns(source, dest) {
|
|
|
2137
2188
|
const sourceIds = new Set(source.map((p) => p.patternId));
|
|
2138
2189
|
return dest.filter((dp) => !sourceIds.has(dp.patternId)).map(clonePattern);
|
|
2139
2190
|
}
|
|
2140
|
-
function combineTool(op, toolId, source, dest) {
|
|
2191
|
+
function combineTool(op, toolId, source, dest, preferDest = false) {
|
|
2141
2192
|
const listFn = op === "merge" ? unionList : intersectList;
|
|
2142
2193
|
if (usesConfigFile(source) || usesConfigFile(dest)) {
|
|
2143
2194
|
return buildConfigFileTool(toolId, source, dest, listFn);
|
|
2144
2195
|
}
|
|
2145
|
-
const patterns = op === "merge" ? mergePatterns(source.patterns, dest.patterns) : intersectPatterns(source.patterns, dest.patterns);
|
|
2196
|
+
const patterns = op === "merge" ? mergePatterns(source.patterns, dest.patterns, preferDest) : intersectPatterns(source.patterns, dest.patterns);
|
|
2146
2197
|
const tool = { toolId, patterns };
|
|
2147
2198
|
if (dest.useLocalConfigurationFile !== void 0) {
|
|
2148
2199
|
tool.useLocalConfigurationFile = dest.useLocalConfigurationFile;
|
|
@@ -2162,13 +2213,14 @@ function buildResultConfig(dest, tools) {
|
|
|
2162
2213
|
if (dest.includeFiles !== void 0) result.includeFiles = structuredClone(dest.includeFiles);
|
|
2163
2214
|
return result;
|
|
2164
2215
|
}
|
|
2165
|
-
function mergeConfigs(source, dest) {
|
|
2216
|
+
function mergeConfigs(source, dest, options = {}) {
|
|
2217
|
+
const preferDest = options.preferDestParameters === true;
|
|
2166
2218
|
const sourceMap = indexByToolId(source.tools);
|
|
2167
2219
|
const seen = /* @__PURE__ */ new Set();
|
|
2168
2220
|
const tools = [];
|
|
2169
2221
|
for (const d of dest.tools) {
|
|
2170
2222
|
const s = sourceMap.get(d.toolId);
|
|
2171
|
-
tools.push(s ? combineTool("merge", d.toolId, s, d) : cloneTool(d));
|
|
2223
|
+
tools.push(s ? combineTool("merge", d.toolId, s, d, preferDest) : cloneTool(d));
|
|
2172
2224
|
seen.add(d.toolId);
|
|
2173
2225
|
}
|
|
2174
2226
|
for (const s of source.tools) {
|
|
@@ -2226,6 +2278,39 @@ function applyConfigOperation(op, source, dest) {
|
|
|
2226
2278
|
}
|
|
2227
2279
|
}
|
|
2228
2280
|
}
|
|
2281
|
+
function pruneEmptyTools(config) {
|
|
2282
|
+
const tools = config.tools.filter((t) => !isEmptyTool(t));
|
|
2283
|
+
if (tools.length === config.tools.length) return config;
|
|
2284
|
+
return { ...config, tools };
|
|
2285
|
+
}
|
|
2286
|
+
function threeWayList(base = [], current = [], next = []) {
|
|
2287
|
+
const baseSet = new Set(base);
|
|
2288
|
+
const nextSet = new Set(next);
|
|
2289
|
+
const removed = new Set(base.filter((x) => !nextSet.has(x)));
|
|
2290
|
+
const added = next.filter((x) => !baseSet.has(x));
|
|
2291
|
+
const kept = current.filter((x) => !removed.has(x));
|
|
2292
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2293
|
+
const result = [];
|
|
2294
|
+
for (const x of [...kept, ...added]) {
|
|
2295
|
+
if (!seen.has(x)) {
|
|
2296
|
+
seen.add(x);
|
|
2297
|
+
result.push(x);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
return result;
|
|
2301
|
+
}
|
|
2302
|
+
function updateConfigIncremental(base, current, next) {
|
|
2303
|
+
const added = diffConfigs(base, next);
|
|
2304
|
+
const removed = diffConfigs(next, base);
|
|
2305
|
+
const afterRemoval = diffConfigs(removed, current);
|
|
2306
|
+
const merged = mergeConfigs(added, afterRemoval, { preferDestParameters: true });
|
|
2307
|
+
const exclude = threeWayList(base.exclude, current.exclude, next.exclude);
|
|
2308
|
+
const result = { ...merged };
|
|
2309
|
+
if (exclude.length > 0) result.exclude = exclude;
|
|
2310
|
+
else delete result.exclude;
|
|
2311
|
+
result.metadata = { ...merged.metadata, languages: [...next.metadata.languages] };
|
|
2312
|
+
return pruneEmptyTools(result);
|
|
2313
|
+
}
|
|
2229
2314
|
|
|
2230
2315
|
// src/init.ts
|
|
2231
2316
|
var import_promises4 = __toESM(require("fs/promises"));
|
|
@@ -2540,11 +2625,24 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2540
2625
|
// --- Scala — Frameworks ---
|
|
2541
2626
|
["play-server", { displayName: "Play Framework", kind: "framework" }],
|
|
2542
2627
|
["akka-actor", { displayName: "Akka", kind: "framework" }],
|
|
2628
|
+
["spray-can", { displayName: "Spray", kind: "framework" }],
|
|
2543
2629
|
// --- Scala — Libraries ---
|
|
2544
2630
|
["scalatest", { displayName: "ScalaTest", kind: "library" }],
|
|
2545
2631
|
["specs2-core", { displayName: "Specs2", kind: "library" }],
|
|
2546
2632
|
["cats-core", { displayName: "Cats", kind: "library" }],
|
|
2547
2633
|
["circe-core", { displayName: "Circe", kind: "library" }],
|
|
2634
|
+
// --- Elixir — Frameworks ---
|
|
2635
|
+
["phoenix", { displayName: "Phoenix", kind: "framework" }],
|
|
2636
|
+
["plug", { displayName: "Plug", kind: "framework" }],
|
|
2637
|
+
["plug_cowboy", { displayName: "Plug", kind: "framework" }],
|
|
2638
|
+
// --- Elixir — Libraries ---
|
|
2639
|
+
["ecto", { displayName: "Ecto", kind: "library" }],
|
|
2640
|
+
["ecto_sql", { displayName: "Ecto", kind: "library" }],
|
|
2641
|
+
["phoenix_ecto", { displayName: "Ecto", kind: "library" }],
|
|
2642
|
+
// --- Crystal — Frameworks ---
|
|
2643
|
+
["amber", { displayName: "Amber", kind: "framework" }],
|
|
2644
|
+
["lucky", { displayName: "Lucky", kind: "framework" }],
|
|
2645
|
+
["kemal", { displayName: "Kemal", kind: "framework" }],
|
|
2548
2646
|
// --- Dart — Frameworks ---
|
|
2549
2647
|
// `flutter` and `angular` appear as plain keys in pubspec.yaml. NOTE: `angular`
|
|
2550
2648
|
// here is Dart's AngularDart. The npm package `angular` is AngularJS (1.x) — a
|
|
@@ -2682,6 +2780,31 @@ var FILE_SIGNAL_RULES = [
|
|
|
2682
2780
|
name: "suitecloud.config.js",
|
|
2683
2781
|
matches: (f) => pathBasename(f) === "suitecloud.config.js",
|
|
2684
2782
|
classification: { displayName: "SuiteScript", kind: "platform" }
|
|
2783
|
+
},
|
|
2784
|
+
{
|
|
2785
|
+
name: ".github/workflows",
|
|
2786
|
+
matches: (f) => f.startsWith(".github/workflows/"),
|
|
2787
|
+
classification: { displayName: "GitHub Actions", kind: "framework" }
|
|
2788
|
+
},
|
|
2789
|
+
{
|
|
2790
|
+
name: ".circleci",
|
|
2791
|
+
matches: (f) => f.startsWith(".circleci/"),
|
|
2792
|
+
classification: { displayName: "CircleCI", kind: "framework" }
|
|
2793
|
+
},
|
|
2794
|
+
{
|
|
2795
|
+
name: ".gitlab-ci.yml",
|
|
2796
|
+
matches: (f) => f === ".gitlab-ci.yml" || f.startsWith(".gitlab/"),
|
|
2797
|
+
classification: { displayName: "GitLab CI", kind: "framework" }
|
|
2798
|
+
},
|
|
2799
|
+
{
|
|
2800
|
+
name: "bitbucket-pipelines.yml",
|
|
2801
|
+
matches: (f) => f === "bitbucket-pipelines.yml",
|
|
2802
|
+
classification: { displayName: "Bitbucket Pipelines", kind: "framework" }
|
|
2803
|
+
},
|
|
2804
|
+
{
|
|
2805
|
+
name: "azure-pipelines.yml",
|
|
2806
|
+
matches: (f) => f === "azure-pipelines.yml" || f === "azure-pipelines.yaml",
|
|
2807
|
+
classification: { displayName: "Azure Pipelines", kind: "framework" }
|
|
2685
2808
|
}
|
|
2686
2809
|
];
|
|
2687
2810
|
function detectFileSignals(files) {
|
|
@@ -2927,6 +3050,15 @@ function parseGemfile(content) {
|
|
|
2927
3050
|
}
|
|
2928
3051
|
return deps;
|
|
2929
3052
|
}
|
|
3053
|
+
function parseMixExs(content) {
|
|
3054
|
+
const deps = [];
|
|
3055
|
+
const depRegex = /\{:([a-zA-Z_][a-zA-Z0-9_]*)\s*,\s*"([^"]*)"/g;
|
|
3056
|
+
let match;
|
|
3057
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
3058
|
+
deps.push({ name: match[1], version: match[2] || void 0 });
|
|
3059
|
+
}
|
|
3060
|
+
return deps;
|
|
3061
|
+
}
|
|
2930
3062
|
function parseGoMod(content) {
|
|
2931
3063
|
const deps = [];
|
|
2932
3064
|
const singleRegex = /^require\s+(\S+)\s+(\S+)/gm;
|
|
@@ -2966,6 +3098,15 @@ function parseBuildGradle(content) {
|
|
|
2966
3098
|
}
|
|
2967
3099
|
return deps;
|
|
2968
3100
|
}
|
|
3101
|
+
function parseBuildSbt(content) {
|
|
3102
|
+
const deps = [];
|
|
3103
|
+
const depRegex = /"([^"]+)"\s*%%?\s*"([^"]+)"\s*%\s*"([^"]+)"/g;
|
|
3104
|
+
let match;
|
|
3105
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
3106
|
+
deps.push({ name: match[2], version: match[3] });
|
|
3107
|
+
}
|
|
3108
|
+
return deps;
|
|
3109
|
+
}
|
|
2969
3110
|
function parseCargoToml(content) {
|
|
2970
3111
|
const deps = [];
|
|
2971
3112
|
let inDepsSection = false;
|
|
@@ -3046,6 +3187,41 @@ function parsePubspecYaml(content) {
|
|
|
3046
3187
|
}
|
|
3047
3188
|
return deps;
|
|
3048
3189
|
}
|
|
3190
|
+
function parseShardYml(content) {
|
|
3191
|
+
const deps = [];
|
|
3192
|
+
let inSection = false;
|
|
3193
|
+
let depIndent = -1;
|
|
3194
|
+
let current;
|
|
3195
|
+
const flush = () => {
|
|
3196
|
+
if (current) deps.push(current);
|
|
3197
|
+
current = void 0;
|
|
3198
|
+
};
|
|
3199
|
+
for (const rawLine of content.split("\n")) {
|
|
3200
|
+
const trimmed = rawLine.trim();
|
|
3201
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
3202
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
3203
|
+
if (indent === 0) {
|
|
3204
|
+
flush();
|
|
3205
|
+
inSection = /^(dependencies|development_dependencies):/.test(trimmed);
|
|
3206
|
+
depIndent = -1;
|
|
3207
|
+
continue;
|
|
3208
|
+
}
|
|
3209
|
+
if (!inSection) continue;
|
|
3210
|
+
if (depIndent === -1) depIndent = indent;
|
|
3211
|
+
if (indent === depIndent) {
|
|
3212
|
+
flush();
|
|
3213
|
+
const m = trimmed.match(/^([a-zA-Z0-9_-]+)\s*:/);
|
|
3214
|
+
current = m ? { name: m[1] } : void 0;
|
|
3215
|
+
continue;
|
|
3216
|
+
}
|
|
3217
|
+
if (indent > depIndent && current) {
|
|
3218
|
+
const v = trimmed.match(/^version\s*:\s*["']?(?:~>|[~^])?\s*([0-9][^"'\s]*)/);
|
|
3219
|
+
if (v) current.version = v[1];
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
flush();
|
|
3223
|
+
return deps;
|
|
3224
|
+
}
|
|
3049
3225
|
function parseXmlDependencyElements(content, elementRegex, nameAttr) {
|
|
3050
3226
|
const deps = [];
|
|
3051
3227
|
const nameRegex = new RegExp(`\\b${nameAttr}\\s*=\\s*["']([^"']+)["']`, "i");
|
|
@@ -3080,11 +3256,14 @@ var MANIFEST_PARSERS = /* @__PURE__ */ new Map([
|
|
|
3080
3256
|
["pom.xml", parsePomXml],
|
|
3081
3257
|
["build.gradle", parseBuildGradle],
|
|
3082
3258
|
["build.gradle.kts", parseBuildGradle],
|
|
3259
|
+
["build.sbt", parseBuildSbt],
|
|
3083
3260
|
["Cargo.toml", parseCargoToml],
|
|
3084
3261
|
["composer.json", parseComposerJson],
|
|
3085
3262
|
["Podfile", parsePodfile],
|
|
3086
3263
|
["Package.swift", parsePackageSwift],
|
|
3087
3264
|
["pubspec.yaml", parsePubspecYaml],
|
|
3265
|
+
["mix.exs", parseMixExs],
|
|
3266
|
+
["shard.yml", parseShardYml],
|
|
3088
3267
|
["packages.config", parsePackagesConfig],
|
|
3089
3268
|
["Directory.Packages.props", parseCsproj]
|
|
3090
3269
|
]);
|
|
@@ -7133,6 +7312,11 @@ async function initContainerConfig(repositoryRoot, adapters) {
|
|
|
7133
7312
|
}
|
|
7134
7313
|
return createCodacyConfig({ source: "container", tools, includeFiles });
|
|
7135
7314
|
}
|
|
7315
|
+
function hasMatchingFiles(files, filePatterns) {
|
|
7316
|
+
const matcher = new CompiledMatcher(filePatterns);
|
|
7317
|
+
if (matcher.matchAll) return files.length > 0;
|
|
7318
|
+
return files.some((file) => matcher.matches(file, file.split("/").pop() ?? ""));
|
|
7319
|
+
}
|
|
7136
7320
|
async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString, loadUnsupportedPatterns) {
|
|
7137
7321
|
const discoveryResult = await discover(repositoryRoot);
|
|
7138
7322
|
onDiscoveryComplete?.(discoveryResult);
|
|
@@ -7142,6 +7326,7 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7142
7326
|
for (const lib of discoveryResult.libraries) detectedKeys.add(lib.displayName);
|
|
7143
7327
|
const repoLanguages = new Set(discoveryResult.languages);
|
|
7144
7328
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
7329
|
+
const repoFiles = listRepoFiles(repositoryRoot);
|
|
7145
7330
|
const descriptorMap = new Map(descriptors.map((d) => [d.id, d]));
|
|
7146
7331
|
const candidates = [];
|
|
7147
7332
|
for (const adapter of adapters) {
|
|
@@ -7149,6 +7334,7 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7149
7334
|
const hasOverlap = adapter.languages.some((lang) => repoLanguages.has(lang));
|
|
7150
7335
|
if (!hasOverlap) continue;
|
|
7151
7336
|
}
|
|
7337
|
+
if (!hasMatchingFiles(repoFiles, adapter.filePatterns)) continue;
|
|
7152
7338
|
candidates.push(adapter);
|
|
7153
7339
|
}
|
|
7154
7340
|
const deduped = deduplicateToolFamilies(candidates, localConfigs);
|
|
@@ -7192,6 +7378,7 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7192
7378
|
}
|
|
7193
7379
|
const family = toolFamily(desc.id);
|
|
7194
7380
|
if (selectedFamilies.has(family)) continue;
|
|
7381
|
+
if (!hasMatchingFiles(repoFiles, desc.filePatterns)) continue;
|
|
7195
7382
|
const patternsMetadata = loadUnsupportedPatterns(desc.id);
|
|
7196
7383
|
if (patternsMetadata.length === 0) continue;
|
|
7197
7384
|
const selectedPatterns = filterAutoPatterns(
|
|
@@ -7410,6 +7597,9 @@ function parseAutoFilters(raw) {
|
|
|
7410
7597
|
return { filters: { severities, categories }, unknown };
|
|
7411
7598
|
}
|
|
7412
7599
|
|
|
7600
|
+
// src/index.ts
|
|
7601
|
+
var import_tooling6 = require("@codacy/tooling");
|
|
7602
|
+
|
|
7413
7603
|
// src/credentials.ts
|
|
7414
7604
|
var import_crypto = __toESM(require("crypto"));
|
|
7415
7605
|
var import_promises5 = __toESM(require("fs/promises"));
|
|
@@ -7482,6 +7672,7 @@ async function resolveApiToken() {
|
|
|
7482
7672
|
clearAdapters,
|
|
7483
7673
|
computePatternSummary,
|
|
7484
7674
|
configureApiToken,
|
|
7675
|
+
configureProxy,
|
|
7485
7676
|
createCodacyConfig,
|
|
7486
7677
|
createLogger,
|
|
7487
7678
|
createNullLogger,
|
|
@@ -7508,17 +7699,23 @@ async function resolveApiToken() {
|
|
|
7508
7699
|
loadCredentials,
|
|
7509
7700
|
mergeConfigs,
|
|
7510
7701
|
parseAutoFilters,
|
|
7702
|
+
pruneEmptyTools,
|
|
7703
|
+
readBaselineConfig,
|
|
7511
7704
|
readCodacyConfig,
|
|
7512
7705
|
readCodacyYaml,
|
|
7513
7706
|
registerAdapter,
|
|
7514
7707
|
registerLazyAdapter,
|
|
7515
7708
|
registerUnsupportedTool,
|
|
7516
7709
|
resolveApiToken,
|
|
7710
|
+
resolveBaselinePath,
|
|
7517
7711
|
resolveConfigPath,
|
|
7518
7712
|
resolveEngineToAdapter,
|
|
7713
|
+
resolveProxyConfig,
|
|
7519
7714
|
rotateLogFiles,
|
|
7520
7715
|
saveCredentials,
|
|
7716
|
+
updateConfigIncremental,
|
|
7521
7717
|
validateApiToken,
|
|
7718
|
+
writeBaselineConfig,
|
|
7522
7719
|
writeCodacyConfig,
|
|
7523
7720
|
writeCodacyConfigFile
|
|
7524
7721
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codacy/analysis-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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.15.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.15.0"
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"author": "Codacy <support@codacy.com> (https://www.codacy.com)",
|