@codacy/analysis-runner 0.4.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 +138 -61
- 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,
|
|
@@ -1209,7 +1239,7 @@ function colorSeverity(severity) {
|
|
|
1209
1239
|
var import_promises3 = __toESM(require("fs/promises"));
|
|
1210
1240
|
var import_path6 = __toESM(require("path"));
|
|
1211
1241
|
var import_child_process3 = require("child_process");
|
|
1212
|
-
var
|
|
1242
|
+
var import_tooling2 = require("@codacy/tooling");
|
|
1213
1243
|
|
|
1214
1244
|
// src/codacy-yaml.ts
|
|
1215
1245
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
@@ -1252,16 +1282,16 @@ function extractStringArray(value) {
|
|
|
1252
1282
|
if (!Array.isArray(value)) return [];
|
|
1253
1283
|
return value.filter((v) => typeof v === "string");
|
|
1254
1284
|
}
|
|
1255
|
-
function resolveEngineToAdapter(engineName,
|
|
1256
|
-
const exact =
|
|
1285
|
+
function resolveEngineToAdapter(engineName, adapters) {
|
|
1286
|
+
const exact = adapters.find((a) => a.id === engineName);
|
|
1257
1287
|
if (exact) return exact.id;
|
|
1258
1288
|
const needle = engineName.toLowerCase();
|
|
1259
|
-
const matches =
|
|
1289
|
+
const matches = adapters.filter((a) => {
|
|
1260
1290
|
return a.id.toLowerCase().includes(needle) || a.displayName.toLowerCase().includes(needle);
|
|
1261
1291
|
});
|
|
1262
1292
|
return matches.length === 1 ? matches[0].id : void 0;
|
|
1263
1293
|
}
|
|
1264
|
-
function applyCodacyYamlExcludes(config, yamlConfig,
|
|
1294
|
+
function applyCodacyYamlExcludes(config, yamlConfig, adapters, logger) {
|
|
1265
1295
|
const hasGlobalIncludes = yamlConfig.includePaths.length > 0;
|
|
1266
1296
|
const hasEngineIncludes = Array.from(yamlConfig.engines.values()).some(
|
|
1267
1297
|
(e) => e.includePaths.length > 0
|
|
@@ -1273,7 +1303,7 @@ function applyCodacyYamlExcludes(config, yamlConfig, adapters2, logger) {
|
|
|
1273
1303
|
const tools = config.tools.map((tool) => ({ ...tool }));
|
|
1274
1304
|
for (const [engineName, engineConfig] of yamlConfig.engines) {
|
|
1275
1305
|
if (engineConfig.excludePaths.length === 0) continue;
|
|
1276
|
-
const adapterId = resolveEngineToAdapter(engineName,
|
|
1306
|
+
const adapterId = resolveEngineToAdapter(engineName, adapters);
|
|
1277
1307
|
if (!adapterId) {
|
|
1278
1308
|
logger?.warn(`Could not resolve .codacy.yaml engine "${engineName}" to a registered adapter`);
|
|
1279
1309
|
continue;
|
|
@@ -4798,6 +4828,34 @@ async function listRepositoryToolPatterns(provider, org, repo, toolUuid) {
|
|
|
4798
4828
|
);
|
|
4799
4829
|
return all;
|
|
4800
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
|
+
}
|
|
4801
4859
|
|
|
4802
4860
|
// src/init.ts
|
|
4803
4861
|
async function detectLanguages(repositoryRoot) {
|
|
@@ -4816,7 +4874,7 @@ async function detectLanguages(repositoryRoot) {
|
|
|
4816
4874
|
for (const file of files) {
|
|
4817
4875
|
const basename = import_path6.default.basename(file);
|
|
4818
4876
|
const ext = import_path6.default.extname(file).toLowerCase();
|
|
4819
|
-
for (const [lang, config] of Object.entries(
|
|
4877
|
+
for (const [lang, config] of Object.entries(import_tooling2.LANGUAGE_CONFIG)) {
|
|
4820
4878
|
if (config.extensions.includes(ext)) {
|
|
4821
4879
|
detected.add(lang);
|
|
4822
4880
|
} else if (config.files.includes(basename)) {
|
|
@@ -4826,10 +4884,10 @@ async function detectLanguages(repositoryRoot) {
|
|
|
4826
4884
|
}
|
|
4827
4885
|
return Array.from(detected).sort();
|
|
4828
4886
|
}
|
|
4829
|
-
async function detectLocalConfigFiles(repositoryRoot,
|
|
4887
|
+
async function detectLocalConfigFiles(repositoryRoot, adapters) {
|
|
4830
4888
|
const found = /* @__PURE__ */ new Map();
|
|
4831
4889
|
const checks = await Promise.allSettled(
|
|
4832
|
-
|
|
4890
|
+
adapters.map(async (adapter) => {
|
|
4833
4891
|
const result = await adapter.checkLocalConfigurationFile({
|
|
4834
4892
|
repositoryRoot,
|
|
4835
4893
|
generatedConfigDir: import_path6.default.join(repositoryRoot, ".codacy", "generated"),
|
|
@@ -4860,17 +4918,17 @@ var silentLogger = {
|
|
|
4860
4918
|
error: () => {
|
|
4861
4919
|
}
|
|
4862
4920
|
};
|
|
4863
|
-
async function enrichWithCodacyYaml(config, repositoryRoot,
|
|
4921
|
+
async function enrichWithCodacyYaml(config, repositoryRoot, adapters) {
|
|
4864
4922
|
const yamlConfig = await readCodacyYaml(repositoryRoot);
|
|
4865
4923
|
if (!yamlConfig) return { config, codacyYamlApplied: false };
|
|
4866
4924
|
return {
|
|
4867
|
-
config: applyCodacyYamlExcludes(config, yamlConfig,
|
|
4925
|
+
config: applyCodacyYamlExcludes(config, yamlConfig, adapters),
|
|
4868
4926
|
codacyYamlApplied: true
|
|
4869
4927
|
};
|
|
4870
4928
|
}
|
|
4871
|
-
async function initLocalConfigBase(repositoryRoot,
|
|
4929
|
+
async function initLocalConfigBase(repositoryRoot, adapters) {
|
|
4872
4930
|
const languages = await detectLanguages(repositoryRoot);
|
|
4873
|
-
const localConfigs = await detectLocalConfigFiles(repositoryRoot,
|
|
4931
|
+
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
4874
4932
|
const tools = [];
|
|
4875
4933
|
for (const [toolId, configPath] of localConfigs.entries()) {
|
|
4876
4934
|
tools.push({
|
|
@@ -4882,17 +4940,17 @@ async function initLocalConfigBase(repositoryRoot, adapters2) {
|
|
|
4882
4940
|
}
|
|
4883
4941
|
return createCodacyConfig({ source: "local", languages, tools });
|
|
4884
4942
|
}
|
|
4885
|
-
async function initLocalConfig(repositoryRoot,
|
|
4886
|
-
const baseConfig = await initLocalConfigBase(repositoryRoot,
|
|
4943
|
+
async function initLocalConfig(repositoryRoot, adapters) {
|
|
4944
|
+
const baseConfig = await initLocalConfigBase(repositoryRoot, adapters);
|
|
4887
4945
|
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
4888
4946
|
baseConfig,
|
|
4889
4947
|
repositoryRoot,
|
|
4890
|
-
|
|
4948
|
+
adapters
|
|
4891
4949
|
);
|
|
4892
4950
|
return { config, stats: { codacyYamlApplied } };
|
|
4893
4951
|
}
|
|
4894
|
-
async function initDefaultConfig(repositoryRoot,
|
|
4895
|
-
const base = await initLocalConfigBase(repositoryRoot,
|
|
4952
|
+
async function initDefaultConfig(repositoryRoot, adapters) {
|
|
4953
|
+
const base = await initLocalConfigBase(repositoryRoot, adapters);
|
|
4896
4954
|
const toolsWithLocalConfig = new Set(
|
|
4897
4955
|
base.tools.filter((t) => t.useLocalConfigurationFile).map((t) => t.toolId)
|
|
4898
4956
|
);
|
|
@@ -4901,7 +4959,7 @@ async function initDefaultConfig(repositoryRoot, adapters2) {
|
|
|
4901
4959
|
const updatedTools = [...base.tools];
|
|
4902
4960
|
const coveredToolIds = new Set(updatedTools.map((t) => t.toolId));
|
|
4903
4961
|
let addedTools = 0;
|
|
4904
|
-
for (const adapter of
|
|
4962
|
+
for (const adapter of adapters) {
|
|
4905
4963
|
if (toolsWithLocalConfig.has(adapter.id)) continue;
|
|
4906
4964
|
const apiTool = apiTools.find(
|
|
4907
4965
|
(t) => t.prefix ? t.prefix.replace(/_$/, "") === adapter.id : t.shortName === adapter.id
|
|
@@ -4935,20 +4993,26 @@ async function initDefaultConfig(repositoryRoot, adapters2) {
|
|
|
4935
4993
|
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
4936
4994
|
baseConfig,
|
|
4937
4995
|
repositoryRoot,
|
|
4938
|
-
|
|
4996
|
+
adapters
|
|
4939
4997
|
);
|
|
4940
4998
|
return { config, stats: { totalApiTools: apiTools.length, addedTools, codacyYamlApplied } };
|
|
4941
4999
|
}
|
|
4942
|
-
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName,
|
|
5000
|
+
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters, apiToken, onRemoteToolsFound, onToolProcessed, onIgnoredFilesFetched) {
|
|
4943
5001
|
configureApiToken(apiToken);
|
|
4944
5002
|
const repoInfo = await getRepository(provider, organization, repositoryName);
|
|
4945
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);
|
|
4946
5010
|
const adapterByName = /* @__PURE__ */ new Map();
|
|
4947
|
-
for (const adapter of
|
|
5011
|
+
for (const adapter of adapters) {
|
|
4948
5012
|
adapterByName.set(adapter.id, adapter);
|
|
4949
5013
|
adapterByName.set(adapter.displayName.toLowerCase(), adapter);
|
|
4950
5014
|
}
|
|
4951
|
-
const localConfigs = await detectLocalConfigFiles(repositoryRoot,
|
|
5015
|
+
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
4952
5016
|
const allApiTools = await listTools();
|
|
4953
5017
|
const adapterByUuid = /* @__PURE__ */ new Map();
|
|
4954
5018
|
for (const apiTool of allApiTools) {
|
|
@@ -5005,17 +5069,26 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
5005
5069
|
repositoryId: repoInfo.repositoryId != null ? String(repoInfo.repositoryId) : void 0,
|
|
5006
5070
|
repositoryName
|
|
5007
5071
|
});
|
|
5072
|
+
if (ignoredFilesResult.files.length > 0) {
|
|
5073
|
+
baseConfig.exclude = [.../* @__PURE__ */ new Set([...baseConfig.exclude ?? [], ...ignoredFilesResult.files])];
|
|
5074
|
+
}
|
|
5008
5075
|
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
5009
5076
|
baseConfig,
|
|
5010
5077
|
repositoryRoot,
|
|
5011
|
-
|
|
5078
|
+
adapters
|
|
5012
5079
|
);
|
|
5013
5080
|
return {
|
|
5014
5081
|
config,
|
|
5015
|
-
stats: {
|
|
5082
|
+
stats: {
|
|
5083
|
+
totalRemoteTools: remoteTools.length,
|
|
5084
|
+
matchedAdapters,
|
|
5085
|
+
codacyYamlApplied,
|
|
5086
|
+
ignoredFilesCount: ignoredFilesResult.totalFetched,
|
|
5087
|
+
ignoredFilesCapped: ignoredFilesResult.capped
|
|
5088
|
+
}
|
|
5016
5089
|
};
|
|
5017
5090
|
}
|
|
5018
|
-
async function initContainerConfig(repositoryRoot,
|
|
5091
|
+
async function initContainerConfig(repositoryRoot, adapters) {
|
|
5019
5092
|
const codacyrcPath = import_path6.default.join(repositoryRoot, ".codacyrc");
|
|
5020
5093
|
let raw;
|
|
5021
5094
|
try {
|
|
@@ -5035,7 +5108,7 @@ async function initContainerConfig(repositoryRoot, adapters2) {
|
|
|
5035
5108
|
}
|
|
5036
5109
|
}
|
|
5037
5110
|
const includeFiles = Array.isArray(parsed.files) ? parsed.files.filter((f) => typeof f === "string") : [];
|
|
5038
|
-
const adapterById = new Map(
|
|
5111
|
+
const adapterById = new Map(adapters.map((a) => [a.id, a]));
|
|
5039
5112
|
const tools = [];
|
|
5040
5113
|
if (Array.isArray(parsed.tools)) {
|
|
5041
5114
|
for (const toolEntry of parsed.tools) {
|
|
@@ -5082,15 +5155,15 @@ var LEVEL_ORDER = {
|
|
|
5082
5155
|
error: 3
|
|
5083
5156
|
};
|
|
5084
5157
|
function rotateLogFiles(logsDir, maxFiles) {
|
|
5085
|
-
let
|
|
5158
|
+
let entries2;
|
|
5086
5159
|
try {
|
|
5087
|
-
|
|
5160
|
+
entries2 = import_fs.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
|
|
5088
5161
|
} catch {
|
|
5089
5162
|
return;
|
|
5090
5163
|
}
|
|
5091
|
-
if (
|
|
5092
|
-
|
|
5093
|
-
const toRemove =
|
|
5164
|
+
if (entries2.length <= maxFiles) return;
|
|
5165
|
+
entries2.sort();
|
|
5166
|
+
const toRemove = entries2.slice(0, entries2.length - maxFiles);
|
|
5094
5167
|
for (const f of toRemove) {
|
|
5095
5168
|
try {
|
|
5096
5169
|
import_fs.default.unlinkSync(import_path7.default.join(logsDir, f));
|
|
@@ -5188,17 +5261,21 @@ function createLogger(options = {}) {
|
|
|
5188
5261
|
detectLanguages,
|
|
5189
5262
|
formatOutput,
|
|
5190
5263
|
formatTextSummary,
|
|
5264
|
+
getAdapter,
|
|
5191
5265
|
getRegisteredAdapters,
|
|
5266
|
+
getRegisteredDescriptors,
|
|
5192
5267
|
initContainerConfig,
|
|
5193
5268
|
initDefaultConfig,
|
|
5194
5269
|
initLocalConfig,
|
|
5195
5270
|
initRemoteConfig,
|
|
5196
5271
|
listDiffFiles,
|
|
5272
|
+
listIgnoredFiles,
|
|
5197
5273
|
listPrFiles,
|
|
5198
5274
|
listStagedFiles,
|
|
5199
5275
|
readCodacyConfig,
|
|
5200
5276
|
readCodacyYaml,
|
|
5201
5277
|
registerAdapter,
|
|
5278
|
+
registerLazyAdapter,
|
|
5202
5279
|
resolveEngineToAdapter,
|
|
5203
5280
|
rotateLogFiles,
|
|
5204
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)",
|