@codacy/analysis-runner 0.12.0 → 0.14.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/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # @codacy/analysis-runner
2
2
 
3
3
  ## Table of Contents
4
+
4
5
  - [Overview](#overview)
6
+ - [Quick start](#quick-start)
5
7
  - [API](#api)
6
8
  - [Pipeline steps](#pipeline-steps)
7
9
  - [Output formats](#output-formats)
@@ -13,17 +15,90 @@
13
15
 
14
16
  Core orchestration engine for the Codacy Analysis CLI. Discovers tools, checks capabilities, calls `analyze()` on each adapter, aggregates results, and transforms output. Usable programmatically by both the CLI and IDE integrations.
15
17
 
18
+ ## Quick start
19
+
20
+ Tool adapters live in a companion preset package, **`@codacy/analysis-adapters`**. A
21
+ consumer installs both and registers the built-in adapters **once** before running
22
+ anything tool-related — the runner's adapter registry starts empty and nothing
23
+ auto-populates it.
24
+
25
+ ```ts
26
+ import { registerBuiltinAdapters } from "@codacy/analysis-adapters";
27
+ import { analyze, createNullLogger } from "@codacy/analysis-runner";
28
+
29
+ // Register all built-in tools + cloud-only descriptors (call once at startup).
30
+ registerBuiltinAdapters();
31
+
32
+ // Run analysis. `repositoryRoot` must be a git work tree.
33
+ const result = await analyze({
34
+ repositoryRoot: "/abs/path/to/repo",
35
+ // files: ["src/index.ts"], // optional: scope to specific files
36
+ logger: createNullLogger(), // or bring your own Logger (see below)
37
+ });
38
+
39
+ // Issue paths are repo-relative; line/column are 1-based.
40
+ for (const issue of result.issues) {
41
+ console.log(
42
+ `${issue.filePath}:${issue.line}:${issue.column} ${issue.patternId} ${issue.message}`,
43
+ );
44
+ }
45
+ ```
46
+
47
+ To register only a subset of tools, import `builtinAdapterEntries` from the preset and
48
+ call `registerLazyAdapter(descriptor, load)` for the ones you want.
49
+
50
+ ### Creating a config
51
+
52
+ `analyze()` reads `.codacy/codacy.config.json`. Generate one programmatically with the
53
+ `init*` family — they build a `CodacyConfig` from the registered adapters; `writeCodacyConfig`
54
+ persists it:
55
+
56
+ ```ts
57
+ import {
58
+ getRegisteredAdapters,
59
+ initLocalConfig, // local-config detection only (offline)
60
+ // initDefaultConfig / initAutoConfig / initRemoteConfig also available
61
+ writeCodacyConfig,
62
+ } from "@codacy/analysis-runner";
63
+
64
+ const adapters = await getRegisteredAdapters();
65
+ const { config } = await initLocalConfig(repositoryRoot, adapters);
66
+ await writeCodacyConfig(repositoryRoot, config);
67
+ ```
68
+
69
+ `initAutoConfig` additionally takes the registered descriptors (`getRegisteredDescriptors()`)
70
+ and the preset's `loadUnsupportedPatterns` to gate stack-specific patterns; `initRemoteConfig`
71
+ pulls the config from Codacy Cloud (pass an API token — see credential helpers below).
72
+
73
+ ### Custom logging & cancellation
74
+
75
+ - Pass your own `Logger` (the `@codacy/tooling` contract: `debug` / `info` / `warn` / `error`)
76
+ to route progress into your UI, or `createNullLogger()` for silence. Do **not** use
77
+ `createLogger()` in an embedder — it writes to files/stderr. Drive progress UI from the
78
+ `onToolStart` / `onToolComplete` / `onToolProgress` callbacks on `AnalyzeOptions`.
79
+ - Pass `signal: AbortSignal` on `AnalyzeOptions` to cancel an in-flight run; signal-honoring
80
+ adapters return partial results and remaining tools are marked cancelled.
81
+
16
82
  ## API
17
83
 
18
- | Export | Purpose |
19
- |--------|---------|
20
- | `analyze(options)` | Main entry point — runs the full analysis pipeline |
21
- | `registerAdapter(adapter)` | Register a tool adapter |
22
- | `clearAdapters()` | Reset adapter registry (for tests) |
23
- | `getRegisteredAdapters()` | List registered adapters |
24
- | `formatOutput(result, format)` | Transform AnalysisResult into a formatted string |
25
- | `buildToolConfig()` | Assemble CodacyToolConfig from CodacyConfig + local config |
26
- | `readCodacyConfig()` / `writeCodacyConfig()` | Read/write `.codacy/codacy.config.json` |
84
+ | Export | Purpose |
85
+ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
86
+ | `analyze(options)` | Main entry point — runs the full analysis pipeline. Options include `repositoryRoot`, `tools`, `files`, `logger`, `signal`, and `onTool*` progress callbacks |
87
+ | `registerAdapter(adapter)` / `registerLazyAdapter(descriptor, load)` | Register a tool adapter (eager / lazy) |
88
+ | `clearAdapters()` | Reset adapter registry (for tests) |
89
+ | `getRegisteredAdapters()` / `getRegisteredDescriptors()` | List registered adapters / descriptors |
90
+ | `initLocalConfig()` / `initDefaultConfig()` / `initAutoConfig()` / `initRemoteConfig()` | Build a `CodacyConfig` (local, default, stack-aware, or Cloud-sourced) |
91
+ | `readCodacyConfig()` / `writeCodacyConfig()` | Read/write `.codacy/codacy.config.json` |
92
+ | `buildToolConfig()` | Assemble CodacyToolConfig from CodacyConfig + local config |
93
+ | `discover(path)` | Scan a repo and report languages, frameworks, libraries, notable files |
94
+ | `formatOutput(result, format)` | Transform AnalysisResult into a formatted string |
95
+ | `createLogger()` / `createNullLogger()` | File/stderr logger (CLI) / no-op logger (embedders) |
96
+ | `saveCredentials()` / `loadCredentials()` / `deleteCredentials()` / `resolveApiToken()` | Machine-stored API-token storage in `~/.codacy/credentials` |
97
+ | `validateApiToken()` / `configureApiToken()` / `listIgnoredFiles()` | Codacy Cloud API helpers |
98
+
99
+ > Adapter **registration** lives in the companion preset `@codacy/analysis-adapters`
100
+ > (`registerBuiltinAdapters`, `builtinAdapterEntries`), not here — the runner stays
101
+ > tool-agnostic.
27
102
 
28
103
  ## Pipeline steps
29
104
 
@@ -38,18 +113,18 @@ Core orchestration engine for the Codacy Analysis CLI. Discovers tools, checks c
38
113
 
39
114
  ## Output formats
40
115
 
41
- | Format | Description |
42
- |--------|-------------|
43
- | `json` | Pretty-printed AnalysisResult (default) |
44
- | `sarif` | SARIF v2.1.0 document |
45
- | `container` | One JSON object per line (Codacy Docker runners) |
46
- | `text` | Human-readable terminal summary with colors and grouping |
116
+ | Format | Description |
117
+ | ----------- | -------------------------------------------------------- |
118
+ | `json` | Pretty-printed AnalysisResult (default) |
119
+ | `sarif` | SARIF v2.1.0 document |
120
+ | `container` | One JSON object per line (Codacy Docker runners) |
121
+ | `text` | Human-readable terminal summary with colors and grouping |
47
122
 
48
123
  ## Execution modes
49
124
 
50
- | Mode | Behavior |
51
- |------|----------|
52
- | `standard` | Skip unavailable tools, report them (default) |
53
- | `strict` | Fail if any configured tool is unavailable |
54
- | `auto-install` | Attempt to install missing tools before analysis |
55
- | `inspect` | Dry-run: report capability without running analysis |
125
+ | Mode | Behavior |
126
+ | -------------- | --------------------------------------------------- |
127
+ | `standard` | Skip unavailable tools, report them (default) |
128
+ | `strict` | Fail if any configured tool is unavailable |
129
+ | `auto-install` | Attempt to install missing tools before analysis |
130
+ | `inspect` | Dry-run: report capability without running analysis |
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { OutputFormat, Logger, AnalysisResult, ToolAdapter, AdapterDescriptor, CodacyConfig, ConfigurationFileCheckResult, CodacyToolConfig, IssueSeverity, IssueCategory, Language, DiscoveryResult, PatternEntry, Issue, PatternSummary, LogLevel } from '@codacy/tooling';
2
- export { AnalysisResult, OutputFormat } from '@codacy/tooling';
1
+ import { OutputFormat, Logger, ProxyConfig, AnalysisResult, ToolAdapter, AdapterDescriptor, CodacyConfig, ConfigurationFileCheckResult, CodacyToolConfig, IssueSeverity, IssueCategory, Language, DiscoveryResult, PatternEntry, Issue, PatternSummary, LogLevel } from '@codacy/tooling';
2
+ export { AnalysisResult, OutputFormat, ProxyConfig, configureProxy, resolveProxyConfig } from '@codacy/tooling';
3
3
 
4
4
  /**
5
5
  * Main analysis entry point — orchestrates the full analysis pipeline.
@@ -51,12 +51,30 @@ interface AnalyzeOptions {
51
51
  configFile?: string;
52
52
  /** Logger for pipeline progress messages (install status, etc.). Defaults to no-op. */
53
53
  logger?: Logger;
54
+ /**
55
+ * Optional caller abort signal for cancelling an in-flight analysis (e.g. an
56
+ * embedder like VSCode or an MCP server). When aborted, `analyze()` stops
57
+ * launching new tools, in-flight signal-honoring adapters return partial
58
+ * results, and remaining tools are marked cancelled. If the signal is already
59
+ * aborted when `analyze()` is called, it returns immediately without doing any
60
+ * work.
61
+ */
62
+ signal?: AbortSignal;
54
63
  /** Called when a tool starts executing. */
55
64
  onToolStart?: (toolId: string, displayName: string) => void;
56
65
  /** Called when a tool finishes executing. */
57
66
  onToolComplete?: (toolId: string, displayName: string, issueCount: number, durationMs: number) => void;
58
67
  /** Called when a tool reports mid-execution progress (e.g. "Linting 50/200 files"). */
59
68
  onToolProgress?: (toolId: string, displayName: string, message: string) => void;
69
+ /**
70
+ * Optional proxy/TLS configuration for outbound HTTP (API calls and tool
71
+ * downloads). When provided, it is applied via `configureProxy` at the start of
72
+ * `analyze()`, installing a global fetch dispatcher. Library consumers that
73
+ * make API calls outside `analyze()` (e.g. init) should instead call
74
+ * `configureProxy` themselves once at startup. Omit to rely on environment
75
+ * variables (HTTP(S)_PROXY, NO_PROXY, SSL_CERT_FILE, CODACY_CLI_INSECURE).
76
+ */
77
+ proxy?: Partial<ProxyConfig>;
60
78
  }
61
79
  /**
62
80
  * Runs analysis on the given repository and returns the unified result.
@@ -172,6 +190,15 @@ declare function formatTextSummary(result: AnalysisResult, isInspect?: boolean):
172
190
  * (e.g. synced from Codacy Cloud) while testing a variant side-by-side.
173
191
  */
174
192
  declare function resolveConfigPath(repositoryRoot: string, configFile?: string): string;
193
+ /**
194
+ * Resolves the absolute path to the config's baseline snapshot — the config the
195
+ * generator last produced, used by `update-config` to distinguish user-disabled
196
+ * patterns from default-off ones. It is a sibling of the config file
197
+ * (`<name>.json` → `<name>.baseline.json`), so `.codacy/codacy.config.json` maps
198
+ * to `.codacy/codacy.config.baseline.json`. It lives outside `.codacy/generated/`
199
+ * so it is committed and travels with the config.
200
+ */
201
+ declare function resolveBaselinePath(repositoryRoot: string, configFile?: string): string;
175
202
  /**
176
203
  * Reads a CodacyConfig from disk.
177
204
  * Returns null if the file doesn't exist. Throws on parse/permission errors
@@ -180,6 +207,19 @@ declare function resolveConfigPath(repositoryRoot: string, configFile?: string):
180
207
  * Pass `configFile` to read from a non-default location.
181
208
  */
182
209
  declare function readCodacyConfig(repositoryRoot: string, configFile?: string): Promise<CodacyConfig | null>;
210
+ /**
211
+ * Reads the config's baseline snapshot (see {@link resolveBaselinePath}).
212
+ * Returns null when no baseline exists (e.g. a config created before baselines
213
+ * were introduced), so `update-config` can fall back gracefully.
214
+ */
215
+ declare function readBaselineConfig(repositoryRoot: string, configFile?: string): Promise<CodacyConfig | null>;
216
+ /**
217
+ * Writes the config's baseline snapshot next to the config file (see
218
+ * {@link resolveBaselinePath}). No `.codacy/` side effects. Callers write this
219
+ * after every `init`/`update-config` so the next update diffs against the exact
220
+ * generator output. Returns the path written.
221
+ */
222
+ declare function writeBaselineConfig(repositoryRoot: string, config: CodacyConfig, configFile?: string): Promise<string>;
183
223
  /**
184
224
  * Writes a CodacyConfig to disk at the exact resolved path, creating only the
185
225
  * parent directory. Unlike {@link writeCodacyConfig}, this has NO `.codacy/`
@@ -252,13 +292,23 @@ declare function createCodacyConfig(params?: {
252
292
 
253
293
  /** The set operation to apply between two configs. */
254
294
  type ConfigOperation = "merge" | "intersect" | "diff";
295
+ /** Options for {@link mergeConfigs}. */
296
+ interface MergeOptions {
297
+ /**
298
+ * When a pattern exists on both sides, keep dest's parameters instead of the
299
+ * default source-wins behavior. Used by the incremental update so the user's
300
+ * tuned parameters (dest) survive over freshly-generated defaults (source).
301
+ */
302
+ preferDestParameters?: boolean;
303
+ }
255
304
  /**
256
305
  * Union of source and dest at the tool and pattern level.
257
306
  * Tools present on only one side are carried over as-is; tools on both sides are
258
- * combined (config-file rule, then per-tool pattern union with source parameters
259
- * winning). Never drops tools.
307
+ * combined (config-file rule, then per-tool pattern union). Parameters of a
308
+ * pattern on both sides come from source unless `options.preferDestParameters`.
309
+ * Never drops tools.
260
310
  */
261
- declare function mergeConfigs(source: CodacyConfig, dest: CodacyConfig): CodacyConfig;
311
+ declare function mergeConfigs(source: CodacyConfig, dest: CodacyConfig, options?: MergeOptions): CodacyConfig;
262
312
  /**
263
313
  * Intersection of source and dest: only tools present in both, and per-tool only
264
314
  * the patterns present in both (config-file rule applies when either side uses a
@@ -277,6 +327,40 @@ declare function intersectConfigs(source: CodacyConfig, dest: CodacyConfig): Cod
277
327
  declare function diffConfigs(source: CodacyConfig, dest: CodacyConfig): CodacyConfig;
278
328
  /** Dispatches to the {@link mergeConfigs}, {@link intersectConfigs}, or {@link diffConfigs}. */
279
329
  declare function applyConfigOperation(op: ConfigOperation, source: CodacyConfig, dest: CodacyConfig): CodacyConfig;
330
+ /**
331
+ * Returns a copy of `config` with tools that would do nothing removed — a tool
332
+ * with no enabled patterns and no native config file. Used after the incremental
333
+ * update so a tool whose every pattern was removed (e.g. its language/framework
334
+ * left the repo) drops out entirely. Config-file tools are always kept (they run
335
+ * from their own file). Does not mutate the input.
336
+ */
337
+ declare function pruneEmptyTools(config: CodacyConfig): CodacyConfig;
338
+ /**
339
+ * Incrementally updates a Codacy config while preserving user customizations.
340
+ *
341
+ * Three-way merge over the enabled-only config model (a disabled pattern is
342
+ * indistinguishable from a default-off one, so a naive re-generate + merge would
343
+ * resurrect patterns the user deliberately removed):
344
+ *
345
+ * - `base` — the config the generator last produced (pre-user-edit baseline)
346
+ * - `current` — the user's live config (baseline + their edits)
347
+ * - `next` — the config the generator produces now (for the current stack)
348
+ *
349
+ * added = next − base (genuinely new tools/patterns to add)
350
+ * removed = base − next (tools/patterns whose stack element is gone)
351
+ * result = (current − removed) + added
352
+ *
353
+ * User-disabled patterns stay absent (they're in `base`, so never in `added`);
354
+ * user-tuned parameters and manual excludes ride along in `current` (dest wins on
355
+ * parameter conflicts via `preferDestParameters`). Identity/provenance metadata
356
+ * (`createdAt`, `source`, organization, …) come from `current`; `updatedAt` is
357
+ * refreshed and `metadata.languages` is refreshed from `next` (an incremental
358
+ * update is effectively a re-discovery, so the recorded stack must reflect the
359
+ * current repository, not the languages captured when the config was first
360
+ * written). Top-level `exclude` is reconciled with the same three-way logic, and
361
+ * any tool left with no patterns and no config file is pruned. Inputs are not mutated.
362
+ */
363
+ declare function updateConfigIncremental(base: CodacyConfig, current: CodacyConfig, next: CodacyConfig): CodacyConfig;
280
364
 
281
365
  /**
282
366
  * auto-filters.ts — Parses and applies --auto filter syntax.
@@ -659,6 +743,15 @@ interface LoggerOptions {
659
743
  */
660
744
  onStderrMessage?: (text: string, level: LogLevel) => void;
661
745
  }
746
+ /**
747
+ * Creates a no-op Logger that discards all messages.
748
+ *
749
+ * Intended for embedders (VSCode extension, MCP server, other library
750
+ * consumers) that call `analyze()` programmatically and do not want the
751
+ * pipeline writing to a log file or stderr. Passing this — or simply omitting
752
+ * `options.logger`, which defaults to it — keeps analysis completely silent.
753
+ */
754
+ declare function createNullLogger(): Logger;
662
755
  /**
663
756
  * Removes old log files beyond the retention limit.
664
757
  *
@@ -677,6 +770,50 @@ declare function rotateLogFiles(logsDir: string, maxFiles: number): void;
677
770
  */
678
771
  declare function createLogger(options?: LoggerOptions): Logger;
679
772
 
773
+ /**
774
+ * Credential storage — encrypts and persists API tokens to ~/.codacy/credentials,
775
+ * and resolves an API token from the environment or stored credentials.
776
+ *
777
+ * Lives in the runner so that any embedder (CLI, MCP server, IDE integration) can
778
+ * reuse credential storage. The interactive prompt stays in the CLI.
779
+ *
780
+ * Replicates the encryption scheme from codacy-cloud-cli — the byte format,
781
+ * algorithm, key derivation, and file location are SHARED, so credentials written
782
+ * by one tool are readable by the other. DO NOT change any of the following:
783
+ * - File location: ~/.codacy/credentials
784
+ * - Machine key: [hostname, username, homedir, platform].join("|")
785
+ * - Key derivation: PBKDF2 (100k iterations, SHA-512, 32-byte key)
786
+ * - Encryption: AES-256-GCM with random salt (32B) and IV (16B)
787
+ * - Storage: JSON with hex-encoded { salt, iv, authTag, encrypted }
788
+ * - File permissions: 0o600 (owner read/write only)
789
+ *
790
+ * All crypto uses Node.js built-in `crypto` — no external dependencies.
791
+ */
792
+ /**
793
+ * Encrypts and saves an API token to ~/.codacy/credentials.
794
+ * Creates the ~/.codacy/ directory if it doesn't exist.
795
+ */
796
+ declare function saveCredentials(token: string): Promise<void>;
797
+ /**
798
+ * Reads and decrypts the stored API token from ~/.codacy/credentials.
799
+ * Returns null if the file is missing, corrupt, or cannot be decrypted.
800
+ */
801
+ declare function loadCredentials(): Promise<string | null>;
802
+ /**
803
+ * Deletes the stored credentials file.
804
+ * Returns true if the file existed and was deleted, false otherwise.
805
+ */
806
+ declare function deleteCredentials(): Promise<boolean>;
807
+ /**
808
+ * Resolves a Codacy API token from the environment or stored credentials.
809
+ *
810
+ * Resolution order:
811
+ * 1. CODACY_API_TOKEN environment variable
812
+ * 2. Encrypted credentials saved via `codacy-analysis login`
813
+ * 3. Throws with instructions if neither source has a token.
814
+ */
815
+ declare function resolveApiToken(): Promise<string>;
816
+
680
817
  /**
681
818
  * Repository discovery — scans a codebase and identifies languages, file patterns,
682
819
  * notable files, frameworks, and libraries.
@@ -699,4 +836,21 @@ interface DiscoverOptions {
699
836
  */
700
837
  declare function discover(repositoryRoot: string, options?: DiscoverOptions): Promise<DiscoveryResult>;
701
838
 
702
- export { type AdapterFactory, type AnalyzeOptions, type AutoFilters, type CategoryFilter, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ConfigOperation, type DiscoverOptions, type ExecutionMode, type IgnoredFilesResult, type InitAutoStats, type InitDefaultStats, type InitLocalStats, type InitRemoteStats, type LoggerOptions, type PatternMetadataLookup, type SeverityFilter, analyze, applyCodacyYamlExcludes, applyConfigOperation, buildToolConfig, clearAdapters, computePatternSummary, configureApiToken, createCodacyConfig, createLogger, detectLanguages, diffConfigs, discover, formatOutput, formatTextSummary, getAdapter, getRegisteredAdapters, getRegisteredDescriptors, initAutoConfig, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, intersectConfigs, listDiffFiles, listIgnoredFiles, listPrFiles, listStagedFiles, mergeConfigs, parseAutoFilters, readCodacyConfig, readCodacyYaml, registerAdapter, registerLazyAdapter, registerUnsupportedTool, resolveConfigPath, resolveEngineToAdapter, rotateLogFiles, validateApiToken, writeCodacyConfig, writeCodacyConfigFile };
839
+ /**
840
+ * Discovery catalog — static knowledge base for the discover command.
841
+ *
842
+ * Contains manifest file parsers, a curated dependency classification catalog,
843
+ * and notable file detection rules. All parsers are pure functions that accept
844
+ * raw file content and return dependency name arrays. They never throw.
845
+ */
846
+
847
+ /**
848
+ * Every display name discovery can emit from a dependency/file/package.json signal.
849
+ * This is the authoritative set a framework-mapping key must belong to (together
850
+ * with detected language names) for its patterns to ever gate on. The drift-guard
851
+ * test uses it to catch mapping keys that no signal can produce (e.g. a rename
852
+ * like react-intl → formatjs that updates the mapping but not the catalog).
853
+ */
854
+ declare function getKnownDisplayNames(): Set<string>;
855
+
856
+ export { type AdapterFactory, type AnalyzeOptions, type AutoFilters, type CategoryFilter, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ConfigOperation, type DiscoverOptions, type ExecutionMode, type IgnoredFilesResult, type InitAutoStats, type InitDefaultStats, type InitLocalStats, type InitRemoteStats, type LoggerOptions, type MergeOptions, type PatternMetadataLookup, type SeverityFilter, analyze, applyCodacyYamlExcludes, applyConfigOperation, buildToolConfig, clearAdapters, computePatternSummary, configureApiToken, createCodacyConfig, createLogger, createNullLogger, deleteCredentials, detectLanguages, diffConfigs, discover, formatOutput, formatTextSummary, getAdapter, getKnownDisplayNames, getRegisteredAdapters, getRegisteredDescriptors, initAutoConfig, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, intersectConfigs, listDiffFiles, listIgnoredFiles, listPrFiles, listStagedFiles, loadCredentials, mergeConfigs, parseAutoFilters, pruneEmptyTools, readBaselineConfig, readCodacyConfig, readCodacyYaml, registerAdapter, registerLazyAdapter, registerUnsupportedTool, resolveApiToken, resolveBaselinePath, resolveConfigPath, resolveEngineToAdapter, rotateLogFiles, saveCredentials, updateConfigIncremental, validateApiToken, writeBaselineConfig, writeCodacyConfig, writeCodacyConfigFile };