@codacy/analysis-runner 0.3.0 → 0.5.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 +70 -9
- package/dist/index.js +142 -62
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OutputFormat, Logger, AnalysisResult, ToolAdapter, CodacyConfig, ConfigurationFileCheckResult, CodacyToolConfig, Language, LogLevel } from '@codacy/tooling';
|
|
1
|
+
import { OutputFormat, Logger, AnalysisResult, ToolAdapter, AdapterDescriptor, CodacyConfig, ConfigurationFileCheckResult, CodacyToolConfig, Language, LogLevel } from '@codacy/tooling';
|
|
2
2
|
export { AnalysisResult, OutputFormat } from '@codacy/tooling';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -60,16 +60,55 @@ interface AnalyzeOptions {
|
|
|
60
60
|
declare function analyze(options: AnalyzeOptions): Promise<AnalysisResult>;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* Tool adapter registry — stores
|
|
63
|
+
* Tool adapter registry — stores adapters for discovery by the runner.
|
|
64
64
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
65
|
+
* Supports two registration modes:
|
|
66
|
+
* - **Eager** (`registerAdapter`): stores a fully resolved adapter. Used by tests
|
|
67
|
+
* and programmatic consumers that already have the adapter object.
|
|
68
|
+
* - **Lazy** (`registerLazyAdapter`): stores lightweight metadata plus a factory
|
|
69
|
+
* function that dynamically imports the full adapter module on first use. Used
|
|
70
|
+
* by the CLI to avoid loading all 30+ adapter modules at startup.
|
|
71
|
+
*
|
|
72
|
+
* `clearAdapters()` is provided for testing.
|
|
67
73
|
*/
|
|
68
74
|
|
|
69
|
-
/**
|
|
75
|
+
/** Async factory that loads and returns a full ToolAdapter. */
|
|
76
|
+
type AdapterFactory = () => Promise<Readonly<ToolAdapter>>;
|
|
77
|
+
/**
|
|
78
|
+
* Register a fully resolved tool adapter (eager mode).
|
|
79
|
+
*
|
|
80
|
+
* The adapter is available immediately — no dynamic import needed. This is the
|
|
81
|
+
* path used by tests and by consumers that import adapters directly.
|
|
82
|
+
*/
|
|
70
83
|
declare function registerAdapter(adapter: Readonly<ToolAdapter>): void;
|
|
71
|
-
/**
|
|
72
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Register an adapter lazily via its descriptor and a factory function.
|
|
86
|
+
*
|
|
87
|
+
* The factory is only called when the adapter is first needed for analysis.
|
|
88
|
+
* The descriptor provides enough information for fuzzy matching, file routing,
|
|
89
|
+
* and inspect-mode metadata display without loading the full module.
|
|
90
|
+
*/
|
|
91
|
+
declare function registerLazyAdapter(descriptor: AdapterDescriptor, factory: AdapterFactory): void;
|
|
92
|
+
/**
|
|
93
|
+
* Returns all registered adapters, optionally filtered by tool ID list.
|
|
94
|
+
*
|
|
95
|
+
* This is async because lazy entries may need to import their module on first
|
|
96
|
+
* access. For callers that only need metadata, use `getRegisteredDescriptors`.
|
|
97
|
+
*/
|
|
98
|
+
declare function getRegisteredAdapters(toolIds?: string[]): Promise<Readonly<ToolAdapter>[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Returns descriptors for all registered adapters (sync, no module loading).
|
|
101
|
+
*
|
|
102
|
+
* Useful when only metadata is needed — e.g. file routing, fuzzy matching,
|
|
103
|
+
* or init-mode tool discovery.
|
|
104
|
+
*/
|
|
105
|
+
declare function getRegisteredDescriptors(toolIds?: string[]): AdapterDescriptor[];
|
|
106
|
+
/**
|
|
107
|
+
* Loads and returns a single adapter by its ID (exact match).
|
|
108
|
+
*
|
|
109
|
+
* Returns undefined if no adapter with that ID is registered.
|
|
110
|
+
*/
|
|
111
|
+
declare function getAdapter(toolId: string): Promise<Readonly<ToolAdapter> | undefined>;
|
|
73
112
|
/** Remove all registered adapters. Used in tests to reset state between runs. */
|
|
74
113
|
declare function clearAdapters(): void;
|
|
75
114
|
|
|
@@ -206,6 +245,10 @@ interface InitRemoteStats {
|
|
|
206
245
|
matchedAdapters: number;
|
|
207
246
|
/** Whether a .codacy.yaml file was found and its exclude paths were applied. */
|
|
208
247
|
codacyYamlApplied: boolean;
|
|
248
|
+
/** Number of cloud-ignored files fetched from the Codacy API. */
|
|
249
|
+
ignoredFilesCount: number;
|
|
250
|
+
/** Whether the fetch was stopped because it hit the 1000-file cap. */
|
|
251
|
+
ignoredFilesCapped: boolean;
|
|
209
252
|
}
|
|
210
253
|
/**
|
|
211
254
|
* `--remote <provider> <org> <repo>` mode.
|
|
@@ -219,8 +262,10 @@ interface InitRemoteStats {
|
|
|
219
262
|
* @param onToolProcessed - Optional callback invoked after each tool's patterns are
|
|
220
263
|
* fetched. Receives the adapter's display name, the number of patterns configured, and
|
|
221
264
|
* whether the tool uses a local configuration file. Useful for real-time CLI progress.
|
|
265
|
+
* @param onIgnoredFilesFetched - Optional callback invoked after cloud-ignored files are
|
|
266
|
+
* fetched. Receives the count and whether the 1000-file cap was reached.
|
|
222
267
|
*/
|
|
223
|
-
declare function initRemoteConfig(repositoryRoot: string, provider: string, organization: string, repositoryName: string, adapters: Readonly<ToolAdapter>[], apiToken: string, onRemoteToolsFound?: (totalEnabledTools: number, matchedAdapters: number) => void, onToolProcessed?: (displayName: string, patternCount: number, usesConfigFile: boolean) => void): Promise<{
|
|
268
|
+
declare function initRemoteConfig(repositoryRoot: string, provider: string, organization: string, repositoryName: string, adapters: Readonly<ToolAdapter>[], apiToken: string, onRemoteToolsFound?: (totalEnabledTools: number, matchedAdapters: number) => void, onToolProcessed?: (displayName: string, patternCount: number, usesConfigFile: boolean) => void, onIgnoredFilesFetched?: (count: number, capped: boolean) => void): Promise<{
|
|
224
269
|
config: CodacyConfig;
|
|
225
270
|
stats: InitRemoteStats;
|
|
226
271
|
}>;
|
|
@@ -334,6 +379,15 @@ declare function listPrFiles(repositoryRoot: string): string[];
|
|
|
334
379
|
* follows cursors until the response contains no next cursor.
|
|
335
380
|
*/
|
|
336
381
|
|
|
382
|
+
/** Result of fetching cloud-ignored files, with pagination cap. */
|
|
383
|
+
interface IgnoredFilesResult {
|
|
384
|
+
/** File paths ignored in Codacy Cloud. */
|
|
385
|
+
files: string[];
|
|
386
|
+
/** Total number of files fetched (same as files.length). */
|
|
387
|
+
totalFetched: number;
|
|
388
|
+
/** Whether the fetch was stopped because it hit the 1000-file cap. */
|
|
389
|
+
capped: boolean;
|
|
390
|
+
}
|
|
337
391
|
/**
|
|
338
392
|
* Configures the API token used for authenticated requests.
|
|
339
393
|
* Must be called before `getRepository`, `listRepositoryTools`, or
|
|
@@ -350,6 +404,13 @@ declare function validateApiToken(token: string): Promise<{
|
|
|
350
404
|
name?: string;
|
|
351
405
|
email?: string;
|
|
352
406
|
}>;
|
|
407
|
+
/**
|
|
408
|
+
* Fetches files individually ignored in Codacy Cloud for a repository.
|
|
409
|
+
* Uses cursor-based pagination capped at 10 pages (1000 files max) to avoid
|
|
410
|
+
* unbounded fetching for repositories with very large ignore lists.
|
|
411
|
+
* Requires `configureApiToken()` to have been called.
|
|
412
|
+
*/
|
|
413
|
+
declare function listIgnoredFiles(provider: string, org: string, repo: string): Promise<IgnoredFilesResult>;
|
|
353
414
|
|
|
354
415
|
/**
|
|
355
416
|
* Structured file + stderr logger for the analysis pipeline.
|
|
@@ -403,4 +464,4 @@ declare function rotateLogFiles(logsDir: string, maxFiles: number): void;
|
|
|
403
464
|
*/
|
|
404
465
|
declare function createLogger(options?: LoggerOptions): Logger;
|
|
405
466
|
|
|
406
|
-
export { type AnalyzeOptions, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ExecutionMode, type InitDefaultStats, type InitLocalStats, type InitRemoteStats, type LoggerOptions, analyze, applyCodacyYamlExcludes, buildToolConfig, clearAdapters, configureApiToken, createCodacyConfig, createLogger, detectLanguages, formatOutput, formatTextSummary, getRegisteredAdapters, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, listDiffFiles, listPrFiles, listStagedFiles, readCodacyConfig, readCodacyYaml, registerAdapter, resolveEngineToAdapter, rotateLogFiles, validateApiToken, writeCodacyConfig };
|
|
467
|
+
export { type AdapterFactory, type AnalyzeOptions, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ExecutionMode, type IgnoredFilesResult, type InitDefaultStats, type InitLocalStats, type InitRemoteStats, type LoggerOptions, analyze, applyCodacyYamlExcludes, buildToolConfig, clearAdapters, configureApiToken, createCodacyConfig, createLogger, detectLanguages, formatOutput, formatTextSummary, getAdapter, getRegisteredAdapters, getRegisteredDescriptors, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, listDiffFiles, listIgnoredFiles, listPrFiles, listStagedFiles, readCodacyConfig, readCodacyYaml, registerAdapter, registerLazyAdapter, resolveEngineToAdapter, rotateLogFiles, validateApiToken, writeCodacyConfig };
|
package/dist/index.js
CHANGED
|
@@ -40,17 +40,21 @@ __export(index_exports, {
|
|
|
40
40
|
detectLanguages: () => detectLanguages,
|
|
41
41
|
formatOutput: () => formatOutput,
|
|
42
42
|
formatTextSummary: () => formatTextSummary,
|
|
43
|
+
getAdapter: () => getAdapter,
|
|
43
44
|
getRegisteredAdapters: () => getRegisteredAdapters,
|
|
45
|
+
getRegisteredDescriptors: () => getRegisteredDescriptors,
|
|
44
46
|
initContainerConfig: () => initContainerConfig,
|
|
45
47
|
initDefaultConfig: () => initDefaultConfig,
|
|
46
48
|
initLocalConfig: () => initLocalConfig,
|
|
47
49
|
initRemoteConfig: () => initRemoteConfig,
|
|
48
50
|
listDiffFiles: () => listDiffFiles,
|
|
51
|
+
listIgnoredFiles: () => listIgnoredFiles,
|
|
49
52
|
listPrFiles: () => listPrFiles,
|
|
50
53
|
listStagedFiles: () => listStagedFiles,
|
|
51
54
|
readCodacyConfig: () => readCodacyConfig,
|
|
52
55
|
readCodacyYaml: () => readCodacyYaml,
|
|
53
56
|
registerAdapter: () => registerAdapter,
|
|
57
|
+
registerLazyAdapter: () => registerLazyAdapter,
|
|
54
58
|
resolveEngineToAdapter: () => resolveEngineToAdapter,
|
|
55
59
|
rotateLogFiles: () => rotateLogFiles,
|
|
56
60
|
validateApiToken: () => validateApiToken,
|
|
@@ -62,29 +66,55 @@ module.exports = __toCommonJS(index_exports);
|
|
|
62
66
|
var import_os = __toESM(require("os"));
|
|
63
67
|
var import_path3 = __toESM(require("path"));
|
|
64
68
|
var import_child_process2 = require("child_process");
|
|
69
|
+
var import_util = require("util");
|
|
65
70
|
|
|
66
71
|
// src/registry.ts
|
|
67
|
-
var
|
|
72
|
+
var import_tooling = require("@codacy/tooling");
|
|
73
|
+
var entries = /* @__PURE__ */ new Map();
|
|
68
74
|
function registerAdapter(adapter) {
|
|
69
|
-
|
|
75
|
+
entries.set(adapter.id, {
|
|
76
|
+
descriptor: (0, import_tooling.extractDescriptor)(adapter),
|
|
77
|
+
factory: async () => adapter,
|
|
78
|
+
resolved: adapter
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function registerLazyAdapter(descriptor, factory) {
|
|
82
|
+
entries.set(descriptor.id, { descriptor, factory });
|
|
70
83
|
}
|
|
71
|
-
function
|
|
72
|
-
const exact =
|
|
84
|
+
function resolveEntry(input) {
|
|
85
|
+
const exact = entries.get(input);
|
|
73
86
|
if (exact) return exact;
|
|
74
87
|
const needle = input.toLowerCase();
|
|
75
|
-
const matches = Array.from(
|
|
76
|
-
return
|
|
88
|
+
const matches = Array.from(entries.values()).filter((e) => {
|
|
89
|
+
return e.descriptor.id.toLowerCase().includes(needle) || e.descriptor.displayName.toLowerCase().includes(needle);
|
|
77
90
|
});
|
|
78
91
|
return matches.length === 1 ? matches[0] : void 0;
|
|
79
92
|
}
|
|
80
|
-
function
|
|
93
|
+
async function loadAdapter(entry) {
|
|
94
|
+
if (entry.resolved) return entry.resolved;
|
|
95
|
+
entry.resolved = await entry.factory();
|
|
96
|
+
return entry.resolved;
|
|
97
|
+
}
|
|
98
|
+
async function getRegisteredAdapters(toolIds) {
|
|
81
99
|
if (toolIds && toolIds.length > 0) {
|
|
82
|
-
|
|
100
|
+
const matched = toolIds.map((id) => resolveEntry(id)).filter((e) => e !== void 0);
|
|
101
|
+
return Promise.all(matched.map(loadAdapter));
|
|
83
102
|
}
|
|
84
|
-
return Array.from(
|
|
103
|
+
return Promise.all(Array.from(entries.values()).map(loadAdapter));
|
|
104
|
+
}
|
|
105
|
+
function getRegisteredDescriptors(toolIds) {
|
|
106
|
+
if (toolIds && toolIds.length > 0) {
|
|
107
|
+
return toolIds.map((id) => resolveEntry(id)).filter((e) => e !== void 0).map((e) => e.descriptor);
|
|
108
|
+
}
|
|
109
|
+
return Array.from(entries.values()).map((e) => e.descriptor);
|
|
110
|
+
}
|
|
111
|
+
async function getAdapter(toolId) {
|
|
112
|
+
const entry = entries.get(toolId);
|
|
113
|
+
if (!entry) return void 0;
|
|
114
|
+
return loadAdapter(entry);
|
|
85
115
|
}
|
|
86
116
|
function clearAdapters() {
|
|
87
|
-
|
|
117
|
+
entries.clear();
|
|
88
118
|
}
|
|
89
119
|
|
|
90
120
|
// src/config.ts
|
|
@@ -429,6 +459,7 @@ async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
|
|
|
429
459
|
}
|
|
430
460
|
|
|
431
461
|
// src/analyze.ts
|
|
462
|
+
var execFileAsync = (0, import_util.promisify)(import_child_process2.execFile);
|
|
432
463
|
var nullLogger = {
|
|
433
464
|
debug: () => {
|
|
434
465
|
},
|
|
@@ -451,26 +482,20 @@ async function analyze(options) {
|
|
|
451
482
|
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
452
483
|
};
|
|
453
484
|
try {
|
|
454
|
-
|
|
455
|
-
cwd: options.repositoryRoot
|
|
456
|
-
stdio: "pipe"
|
|
485
|
+
await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
486
|
+
cwd: options.repositoryRoot
|
|
457
487
|
});
|
|
458
|
-
} catch {
|
|
488
|
+
} catch (err) {
|
|
489
|
+
const isGitMissing = err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
490
|
+
const message = isGitMissing ? "git is not installed. Codacy analysis requires git for file discovery." : `${options.repositoryRoot} is not a git repository. Codacy analysis requires a git repository for file discovery. Run \`git init\` to initialize one.`;
|
|
491
|
+
const kind = isGitMissing ? "GitNotInstalled" : "NotAGitRepository";
|
|
459
492
|
return buildResult(
|
|
460
493
|
startedAt,
|
|
461
494
|
options.repositoryRoot,
|
|
462
495
|
mode,
|
|
463
496
|
emptyCapability,
|
|
464
497
|
[],
|
|
465
|
-
[
|
|
466
|
-
{
|
|
467
|
-
toolId: "runner",
|
|
468
|
-
phase: "requirementCheck",
|
|
469
|
-
kind: "NotAGitRepository",
|
|
470
|
-
message: `${options.repositoryRoot} is not a git repository. Codacy analysis requires a git repository for file discovery. Run \`git init\` to initialize one.`,
|
|
471
|
-
level: "error"
|
|
472
|
-
}
|
|
473
|
-
],
|
|
498
|
+
[{ toolId: "runner", phase: "requirementCheck", kind, message, level: "error" }],
|
|
474
499
|
[]
|
|
475
500
|
);
|
|
476
501
|
}
|
|
@@ -515,8 +540,9 @@ async function analyze(options) {
|
|
|
515
540
|
[]
|
|
516
541
|
);
|
|
517
542
|
}
|
|
518
|
-
const
|
|
519
|
-
|
|
543
|
+
const requestedTools = options.tools ?? codacyConfig.tools.map((t) => t.toolId);
|
|
544
|
+
const adapters = await getRegisteredAdapters(requestedTools);
|
|
545
|
+
logger.info("Pipeline started", { mode, toolCount: adapters.length, parallelTools, toolTimeout });
|
|
520
546
|
const globalDir = import_path3.default.join(import_os.default.homedir(), ".codacy");
|
|
521
547
|
const baseCtx = {
|
|
522
548
|
repositoryRoot: options.repositoryRoot,
|
|
@@ -528,7 +554,7 @@ async function analyze(options) {
|
|
|
528
554
|
const ready = [];
|
|
529
555
|
const unavailable = [];
|
|
530
556
|
const availabilityTimeout = 6e4;
|
|
531
|
-
for (const adapter of
|
|
557
|
+
for (const adapter of adapters) {
|
|
532
558
|
const ctx = makeToolContext(baseCtx, adapter, []);
|
|
533
559
|
const checkStart = Date.now();
|
|
534
560
|
logger.debug("Checking availability", { toolId: adapter.id });
|
|
@@ -600,7 +626,7 @@ async function analyze(options) {
|
|
|
600
626
|
const installErrors = [];
|
|
601
627
|
for (let i = capability.unavailable.length - 1; i >= 0; i--) {
|
|
602
628
|
const status = capability.unavailable[i];
|
|
603
|
-
const adapter =
|
|
629
|
+
const adapter = adapters.find((a) => a.id === status.toolId);
|
|
604
630
|
if (!adapter) continue;
|
|
605
631
|
const ctx = makeToolContext(baseCtx, adapter, []);
|
|
606
632
|
logger.info(`${adapter.displayName} is missing, attempting install...`);
|
|
@@ -674,7 +700,7 @@ async function analyze(options) {
|
|
|
674
700
|
}
|
|
675
701
|
if (mode === "inspect") {
|
|
676
702
|
for (const tool of capability.ready) {
|
|
677
|
-
const adapter =
|
|
703
|
+
const adapter = adapters.find((a) => a.id === tool.toolId);
|
|
678
704
|
if (!adapter) continue;
|
|
679
705
|
const ctx = makeToolContext(baseCtx, adapter, []);
|
|
680
706
|
const availability = await adapter.checkAvailability(ctx).catch(() => null);
|
|
@@ -741,7 +767,7 @@ async function analyze(options) {
|
|
|
741
767
|
allFiles = filterByTargetFiles(allFiles, options.files);
|
|
742
768
|
logger.debug("Filtered by target files", { afterFilter: allFiles.length });
|
|
743
769
|
}
|
|
744
|
-
const readyAdapters =
|
|
770
|
+
const readyAdapters = adapters.filter((a) => capability.ready.some((r) => r.toolId === a.id));
|
|
745
771
|
const toolRuns = [];
|
|
746
772
|
for (const adapter of readyAdapters) {
|
|
747
773
|
const toolConfig = getToolConfig(codacyConfig, adapter.id);
|
|
@@ -769,6 +795,10 @@ async function analyze(options) {
|
|
|
769
795
|
logger.debug("No files routed, skipping", { toolId: adapter.id });
|
|
770
796
|
continue;
|
|
771
797
|
}
|
|
798
|
+
if (!codacyToolConfig.useLocalConfigurationFile && codacyToolConfig.patterns.length === 0) {
|
|
799
|
+
logger.debug("No patterns and no local config, skipping", { toolId: adapter.id });
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
772
802
|
logger.debug("Files routed to tool", { toolId: adapter.id, fileCount: toolFiles.length });
|
|
773
803
|
logger.debug("Tool config resolved", {
|
|
774
804
|
toolId: adapter.id,
|
|
@@ -982,8 +1012,11 @@ function formatText(result) {
|
|
|
982
1012
|
byFile.set(issue.filePath, existing);
|
|
983
1013
|
}
|
|
984
1014
|
for (const [filePath, issues] of byFile) {
|
|
1015
|
+
const count = issues.length;
|
|
985
1016
|
lines.push("");
|
|
986
|
-
lines.push(
|
|
1017
|
+
lines.push(
|
|
1018
|
+
`${import_ansis.default.bold.white(filePath)} ${import_ansis.default.dim(`${count} issue${count !== 1 ? "s" : ""}`)}`
|
|
1019
|
+
);
|
|
987
1020
|
for (const issue of issues) {
|
|
988
1021
|
if (issue.multilineContent && issue.multilineContent.length > 1) {
|
|
989
1022
|
formatMultilineIssue(lines, issue);
|
|
@@ -1206,7 +1239,7 @@ function colorSeverity(severity) {
|
|
|
1206
1239
|
var import_promises3 = __toESM(require("fs/promises"));
|
|
1207
1240
|
var import_path6 = __toESM(require("path"));
|
|
1208
1241
|
var import_child_process3 = require("child_process");
|
|
1209
|
-
var
|
|
1242
|
+
var import_tooling2 = require("@codacy/tooling");
|
|
1210
1243
|
|
|
1211
1244
|
// src/codacy-yaml.ts
|
|
1212
1245
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
@@ -1249,16 +1282,16 @@ function extractStringArray(value) {
|
|
|
1249
1282
|
if (!Array.isArray(value)) return [];
|
|
1250
1283
|
return value.filter((v) => typeof v === "string");
|
|
1251
1284
|
}
|
|
1252
|
-
function resolveEngineToAdapter(engineName,
|
|
1253
|
-
const exact =
|
|
1285
|
+
function resolveEngineToAdapter(engineName, adapters) {
|
|
1286
|
+
const exact = adapters.find((a) => a.id === engineName);
|
|
1254
1287
|
if (exact) return exact.id;
|
|
1255
1288
|
const needle = engineName.toLowerCase();
|
|
1256
|
-
const matches =
|
|
1289
|
+
const matches = adapters.filter((a) => {
|
|
1257
1290
|
return a.id.toLowerCase().includes(needle) || a.displayName.toLowerCase().includes(needle);
|
|
1258
1291
|
});
|
|
1259
1292
|
return matches.length === 1 ? matches[0].id : void 0;
|
|
1260
1293
|
}
|
|
1261
|
-
function applyCodacyYamlExcludes(config, yamlConfig,
|
|
1294
|
+
function applyCodacyYamlExcludes(config, yamlConfig, adapters, logger) {
|
|
1262
1295
|
const hasGlobalIncludes = yamlConfig.includePaths.length > 0;
|
|
1263
1296
|
const hasEngineIncludes = Array.from(yamlConfig.engines.values()).some(
|
|
1264
1297
|
(e) => e.includePaths.length > 0
|
|
@@ -1270,7 +1303,7 @@ function applyCodacyYamlExcludes(config, yamlConfig, adapters2, logger) {
|
|
|
1270
1303
|
const tools = config.tools.map((tool) => ({ ...tool }));
|
|
1271
1304
|
for (const [engineName, engineConfig] of yamlConfig.engines) {
|
|
1272
1305
|
if (engineConfig.excludePaths.length === 0) continue;
|
|
1273
|
-
const adapterId = resolveEngineToAdapter(engineName,
|
|
1306
|
+
const adapterId = resolveEngineToAdapter(engineName, adapters);
|
|
1274
1307
|
if (!adapterId) {
|
|
1275
1308
|
logger?.warn(`Could not resolve .codacy.yaml engine "${engineName}" to a registered adapter`);
|
|
1276
1309
|
continue;
|
|
@@ -4795,6 +4828,34 @@ async function listRepositoryToolPatterns(provider, org, repo, toolUuid) {
|
|
|
4795
4828
|
);
|
|
4796
4829
|
return all;
|
|
4797
4830
|
}
|
|
4831
|
+
async function listIgnoredFiles(provider, org, repo) {
|
|
4832
|
+
const MAX_PAGES = 10;
|
|
4833
|
+
const files = [];
|
|
4834
|
+
let cursor;
|
|
4835
|
+
let pages = 0;
|
|
4836
|
+
while (pages < MAX_PAGES) {
|
|
4837
|
+
const page = await RepositoryService.listIgnoredFiles(
|
|
4838
|
+
provider,
|
|
4839
|
+
org,
|
|
4840
|
+
repo,
|
|
4841
|
+
void 0,
|
|
4842
|
+
// branch — use default branch
|
|
4843
|
+
void 0,
|
|
4844
|
+
// search
|
|
4845
|
+
cursor,
|
|
4846
|
+
100
|
|
4847
|
+
);
|
|
4848
|
+
files.push(...page.data.map((f) => f.filepath));
|
|
4849
|
+
cursor = page.pagination?.cursor;
|
|
4850
|
+
pages++;
|
|
4851
|
+
if (!cursor || page.data.length === 0) break;
|
|
4852
|
+
}
|
|
4853
|
+
return {
|
|
4854
|
+
files,
|
|
4855
|
+
totalFetched: files.length,
|
|
4856
|
+
capped: pages >= MAX_PAGES && cursor !== void 0
|
|
4857
|
+
};
|
|
4858
|
+
}
|
|
4798
4859
|
|
|
4799
4860
|
// src/init.ts
|
|
4800
4861
|
async function detectLanguages(repositoryRoot) {
|
|
@@ -4813,7 +4874,7 @@ async function detectLanguages(repositoryRoot) {
|
|
|
4813
4874
|
for (const file of files) {
|
|
4814
4875
|
const basename = import_path6.default.basename(file);
|
|
4815
4876
|
const ext = import_path6.default.extname(file).toLowerCase();
|
|
4816
|
-
for (const [lang, config] of Object.entries(
|
|
4877
|
+
for (const [lang, config] of Object.entries(import_tooling2.LANGUAGE_CONFIG)) {
|
|
4817
4878
|
if (config.extensions.includes(ext)) {
|
|
4818
4879
|
detected.add(lang);
|
|
4819
4880
|
} else if (config.files.includes(basename)) {
|
|
@@ -4823,10 +4884,10 @@ async function detectLanguages(repositoryRoot) {
|
|
|
4823
4884
|
}
|
|
4824
4885
|
return Array.from(detected).sort();
|
|
4825
4886
|
}
|
|
4826
|
-
async function detectLocalConfigFiles(repositoryRoot,
|
|
4887
|
+
async function detectLocalConfigFiles(repositoryRoot, adapters) {
|
|
4827
4888
|
const found = /* @__PURE__ */ new Map();
|
|
4828
4889
|
const checks = await Promise.allSettled(
|
|
4829
|
-
|
|
4890
|
+
adapters.map(async (adapter) => {
|
|
4830
4891
|
const result = await adapter.checkLocalConfigurationFile({
|
|
4831
4892
|
repositoryRoot,
|
|
4832
4893
|
generatedConfigDir: import_path6.default.join(repositoryRoot, ".codacy", "generated"),
|
|
@@ -4857,17 +4918,17 @@ var silentLogger = {
|
|
|
4857
4918
|
error: () => {
|
|
4858
4919
|
}
|
|
4859
4920
|
};
|
|
4860
|
-
async function enrichWithCodacyYaml(config, repositoryRoot,
|
|
4921
|
+
async function enrichWithCodacyYaml(config, repositoryRoot, adapters) {
|
|
4861
4922
|
const yamlConfig = await readCodacyYaml(repositoryRoot);
|
|
4862
4923
|
if (!yamlConfig) return { config, codacyYamlApplied: false };
|
|
4863
4924
|
return {
|
|
4864
|
-
config: applyCodacyYamlExcludes(config, yamlConfig,
|
|
4925
|
+
config: applyCodacyYamlExcludes(config, yamlConfig, adapters),
|
|
4865
4926
|
codacyYamlApplied: true
|
|
4866
4927
|
};
|
|
4867
4928
|
}
|
|
4868
|
-
async function initLocalConfigBase(repositoryRoot,
|
|
4929
|
+
async function initLocalConfigBase(repositoryRoot, adapters) {
|
|
4869
4930
|
const languages = await detectLanguages(repositoryRoot);
|
|
4870
|
-
const localConfigs = await detectLocalConfigFiles(repositoryRoot,
|
|
4931
|
+
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
4871
4932
|
const tools = [];
|
|
4872
4933
|
for (const [toolId, configPath] of localConfigs.entries()) {
|
|
4873
4934
|
tools.push({
|
|
@@ -4879,17 +4940,17 @@ async function initLocalConfigBase(repositoryRoot, adapters2) {
|
|
|
4879
4940
|
}
|
|
4880
4941
|
return createCodacyConfig({ source: "local", languages, tools });
|
|
4881
4942
|
}
|
|
4882
|
-
async function initLocalConfig(repositoryRoot,
|
|
4883
|
-
const baseConfig = await initLocalConfigBase(repositoryRoot,
|
|
4943
|
+
async function initLocalConfig(repositoryRoot, adapters) {
|
|
4944
|
+
const baseConfig = await initLocalConfigBase(repositoryRoot, adapters);
|
|
4884
4945
|
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
4885
4946
|
baseConfig,
|
|
4886
4947
|
repositoryRoot,
|
|
4887
|
-
|
|
4948
|
+
adapters
|
|
4888
4949
|
);
|
|
4889
4950
|
return { config, stats: { codacyYamlApplied } };
|
|
4890
4951
|
}
|
|
4891
|
-
async function initDefaultConfig(repositoryRoot,
|
|
4892
|
-
const base = await initLocalConfigBase(repositoryRoot,
|
|
4952
|
+
async function initDefaultConfig(repositoryRoot, adapters) {
|
|
4953
|
+
const base = await initLocalConfigBase(repositoryRoot, adapters);
|
|
4893
4954
|
const toolsWithLocalConfig = new Set(
|
|
4894
4955
|
base.tools.filter((t) => t.useLocalConfigurationFile).map((t) => t.toolId)
|
|
4895
4956
|
);
|
|
@@ -4898,7 +4959,7 @@ async function initDefaultConfig(repositoryRoot, adapters2) {
|
|
|
4898
4959
|
const updatedTools = [...base.tools];
|
|
4899
4960
|
const coveredToolIds = new Set(updatedTools.map((t) => t.toolId));
|
|
4900
4961
|
let addedTools = 0;
|
|
4901
|
-
for (const adapter of
|
|
4962
|
+
for (const adapter of adapters) {
|
|
4902
4963
|
if (toolsWithLocalConfig.has(adapter.id)) continue;
|
|
4903
4964
|
const apiTool = apiTools.find(
|
|
4904
4965
|
(t) => t.prefix ? t.prefix.replace(/_$/, "") === adapter.id : t.shortName === adapter.id
|
|
@@ -4932,20 +4993,26 @@ async function initDefaultConfig(repositoryRoot, adapters2) {
|
|
|
4932
4993
|
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
4933
4994
|
baseConfig,
|
|
4934
4995
|
repositoryRoot,
|
|
4935
|
-
|
|
4996
|
+
adapters
|
|
4936
4997
|
);
|
|
4937
4998
|
return { config, stats: { totalApiTools: apiTools.length, addedTools, codacyYamlApplied } };
|
|
4938
4999
|
}
|
|
4939
|
-
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName,
|
|
5000
|
+
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters, apiToken, onRemoteToolsFound, onToolProcessed, onIgnoredFilesFetched) {
|
|
4940
5001
|
configureApiToken(apiToken);
|
|
4941
5002
|
const repoInfo = await getRepository(provider, organization, repositoryName);
|
|
4942
5003
|
const remoteTools = await listRepositoryTools(provider, organization, repositoryName);
|
|
5004
|
+
let ignoredFilesResult = { files: [], totalFetched: 0, capped: false };
|
|
5005
|
+
try {
|
|
5006
|
+
ignoredFilesResult = await listIgnoredFiles(provider, organization, repositoryName);
|
|
5007
|
+
} catch {
|
|
5008
|
+
}
|
|
5009
|
+
onIgnoredFilesFetched?.(ignoredFilesResult.totalFetched, ignoredFilesResult.capped);
|
|
4943
5010
|
const adapterByName = /* @__PURE__ */ new Map();
|
|
4944
|
-
for (const adapter of
|
|
5011
|
+
for (const adapter of adapters) {
|
|
4945
5012
|
adapterByName.set(adapter.id, adapter);
|
|
4946
5013
|
adapterByName.set(adapter.displayName.toLowerCase(), adapter);
|
|
4947
5014
|
}
|
|
4948
|
-
const localConfigs = await detectLocalConfigFiles(repositoryRoot,
|
|
5015
|
+
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
4949
5016
|
const allApiTools = await listTools();
|
|
4950
5017
|
const adapterByUuid = /* @__PURE__ */ new Map();
|
|
4951
5018
|
for (const apiTool of allApiTools) {
|
|
@@ -5002,17 +5069,26 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
5002
5069
|
repositoryId: repoInfo.repositoryId != null ? String(repoInfo.repositoryId) : void 0,
|
|
5003
5070
|
repositoryName
|
|
5004
5071
|
});
|
|
5072
|
+
if (ignoredFilesResult.files.length > 0) {
|
|
5073
|
+
baseConfig.exclude = [.../* @__PURE__ */ new Set([...baseConfig.exclude ?? [], ...ignoredFilesResult.files])];
|
|
5074
|
+
}
|
|
5005
5075
|
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
5006
5076
|
baseConfig,
|
|
5007
5077
|
repositoryRoot,
|
|
5008
|
-
|
|
5078
|
+
adapters
|
|
5009
5079
|
);
|
|
5010
5080
|
return {
|
|
5011
5081
|
config,
|
|
5012
|
-
stats: {
|
|
5082
|
+
stats: {
|
|
5083
|
+
totalRemoteTools: remoteTools.length,
|
|
5084
|
+
matchedAdapters,
|
|
5085
|
+
codacyYamlApplied,
|
|
5086
|
+
ignoredFilesCount: ignoredFilesResult.totalFetched,
|
|
5087
|
+
ignoredFilesCapped: ignoredFilesResult.capped
|
|
5088
|
+
}
|
|
5013
5089
|
};
|
|
5014
5090
|
}
|
|
5015
|
-
async function initContainerConfig(repositoryRoot,
|
|
5091
|
+
async function initContainerConfig(repositoryRoot, adapters) {
|
|
5016
5092
|
const codacyrcPath = import_path6.default.join(repositoryRoot, ".codacyrc");
|
|
5017
5093
|
let raw;
|
|
5018
5094
|
try {
|
|
@@ -5032,7 +5108,7 @@ async function initContainerConfig(repositoryRoot, adapters2) {
|
|
|
5032
5108
|
}
|
|
5033
5109
|
}
|
|
5034
5110
|
const includeFiles = Array.isArray(parsed.files) ? parsed.files.filter((f) => typeof f === "string") : [];
|
|
5035
|
-
const adapterById = new Map(
|
|
5111
|
+
const adapterById = new Map(adapters.map((a) => [a.id, a]));
|
|
5036
5112
|
const tools = [];
|
|
5037
5113
|
if (Array.isArray(parsed.tools)) {
|
|
5038
5114
|
for (const toolEntry of parsed.tools) {
|
|
@@ -5079,15 +5155,15 @@ var LEVEL_ORDER = {
|
|
|
5079
5155
|
error: 3
|
|
5080
5156
|
};
|
|
5081
5157
|
function rotateLogFiles(logsDir, maxFiles) {
|
|
5082
|
-
let
|
|
5158
|
+
let entries2;
|
|
5083
5159
|
try {
|
|
5084
|
-
|
|
5160
|
+
entries2 = import_fs.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
|
|
5085
5161
|
} catch {
|
|
5086
5162
|
return;
|
|
5087
5163
|
}
|
|
5088
|
-
if (
|
|
5089
|
-
|
|
5090
|
-
const toRemove =
|
|
5164
|
+
if (entries2.length <= maxFiles) return;
|
|
5165
|
+
entries2.sort();
|
|
5166
|
+
const toRemove = entries2.slice(0, entries2.length - maxFiles);
|
|
5091
5167
|
for (const f of toRemove) {
|
|
5092
5168
|
try {
|
|
5093
5169
|
import_fs.default.unlinkSync(import_path7.default.join(logsDir, f));
|
|
@@ -5185,17 +5261,21 @@ function createLogger(options = {}) {
|
|
|
5185
5261
|
detectLanguages,
|
|
5186
5262
|
formatOutput,
|
|
5187
5263
|
formatTextSummary,
|
|
5264
|
+
getAdapter,
|
|
5188
5265
|
getRegisteredAdapters,
|
|
5266
|
+
getRegisteredDescriptors,
|
|
5189
5267
|
initContainerConfig,
|
|
5190
5268
|
initDefaultConfig,
|
|
5191
5269
|
initLocalConfig,
|
|
5192
5270
|
initRemoteConfig,
|
|
5193
5271
|
listDiffFiles,
|
|
5272
|
+
listIgnoredFiles,
|
|
5194
5273
|
listPrFiles,
|
|
5195
5274
|
listStagedFiles,
|
|
5196
5275
|
readCodacyConfig,
|
|
5197
5276
|
readCodacyYaml,
|
|
5198
5277
|
registerAdapter,
|
|
5278
|
+
registerLazyAdapter,
|
|
5199
5279
|
resolveEngineToAdapter,
|
|
5200
5280
|
rotateLogFiles,
|
|
5201
5281
|
validateApiToken,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codacy/analysis-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.5.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.5.0"
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"author": "Codacy <support@codacy.com> (https://www.codacy.com)",
|