@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 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 registered adapters for discovery by the runner.
63
+ * Tool adapter registry — stores adapters for discovery by the runner.
64
64
  *
65
- * Adapters must be registered before calling analyze(). The registry is a simple
66
- * in-memory map keyed by adapter ID. clearAdapters() is provided for testing.
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
- /** Register a tool adapter so the runner can discover and invoke it. */
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
- /** Returns all registered adapters, optionally filtered by tool ID list. */
72
- declare function getRegisteredAdapters(toolIds?: string[]): Readonly<ToolAdapter>[];
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 adapters = /* @__PURE__ */ new Map();
72
+ var import_tooling = require("@codacy/tooling");
73
+ var entries = /* @__PURE__ */ new Map();
68
74
  function registerAdapter(adapter) {
69
- adapters.set(adapter.id, adapter);
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 resolveAdapter(input) {
72
- const exact = adapters.get(input);
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(adapters.values()).filter((a) => {
76
- return a.id.toLowerCase().includes(needle) || a.displayName.toLowerCase().includes(needle);
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 getRegisteredAdapters(toolIds) {
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
- return toolIds.map((id) => resolveAdapter(id)).filter((a) => a !== void 0);
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(adapters.values());
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
- adapters.clear();
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
- (0, import_child_process2.execSync)("git rev-parse --is-inside-work-tree", {
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 adapters2 = getRegisteredAdapters(options.tools);
519
- logger.info("Pipeline started", { mode, toolCount: adapters2.length, parallelTools, toolTimeout });
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 adapters2) {
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 = adapters2.find((a) => a.id === status.toolId);
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 = adapters2.find((a) => a.id === tool.toolId);
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 = adapters2.filter((a) => capability.ready.some((r) => r.toolId === a.id));
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(import_ansis.default.bold.white(filePath));
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 import_tooling = require("@codacy/tooling");
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, adapters2) {
1253
- const exact = adapters2.find((a) => a.id === engineName);
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 = adapters2.filter((a) => {
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, adapters2, logger) {
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, adapters2);
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(import_tooling.LANGUAGE_CONFIG)) {
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, adapters2) {
4887
+ async function detectLocalConfigFiles(repositoryRoot, adapters) {
4827
4888
  const found = /* @__PURE__ */ new Map();
4828
4889
  const checks = await Promise.allSettled(
4829
- adapters2.map(async (adapter) => {
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, adapters2) {
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, adapters2),
4925
+ config: applyCodacyYamlExcludes(config, yamlConfig, adapters),
4865
4926
  codacyYamlApplied: true
4866
4927
  };
4867
4928
  }
4868
- async function initLocalConfigBase(repositoryRoot, adapters2) {
4929
+ async function initLocalConfigBase(repositoryRoot, adapters) {
4869
4930
  const languages = await detectLanguages(repositoryRoot);
4870
- const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters2);
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, adapters2) {
4883
- const baseConfig = await initLocalConfigBase(repositoryRoot, adapters2);
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
- adapters2
4948
+ adapters
4888
4949
  );
4889
4950
  return { config, stats: { codacyYamlApplied } };
4890
4951
  }
4891
- async function initDefaultConfig(repositoryRoot, adapters2) {
4892
- const base = await initLocalConfigBase(repositoryRoot, adapters2);
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 adapters2) {
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
- adapters2
4996
+ adapters
4936
4997
  );
4937
4998
  return { config, stats: { totalApiTools: apiTools.length, addedTools, codacyYamlApplied } };
4938
4999
  }
4939
- async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters2, apiToken, onRemoteToolsFound, onToolProcessed) {
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 adapters2) {
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, adapters2);
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
- adapters2
5078
+ adapters
5009
5079
  );
5010
5080
  return {
5011
5081
  config,
5012
- stats: { totalRemoteTools: remoteTools.length, matchedAdapters, codacyYamlApplied }
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, adapters2) {
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(adapters2.map((a) => [a.id, a]));
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 entries;
5158
+ let entries2;
5083
5159
  try {
5084
- entries = import_fs.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
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 (entries.length <= maxFiles) return;
5089
- entries.sort();
5090
- const toRemove = entries.slice(0, entries.length - maxFiles);
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.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.3.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.3.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)",