@codacy/analysis-runner 0.12.0 → 0.13.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 +96 -21
- package/dist/index.d.ts +80 -1
- package/dist/index.js +491 -162
- package/package.json +3 -3
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
|
|
19
|
-
|
|
20
|
-
| `analyze(options)`
|
|
21
|
-
| `registerAdapter(adapter)` | Register a tool adapter |
|
|
22
|
-
| `clearAdapters()`
|
|
23
|
-
| `getRegisteredAdapters()` | List registered adapters |
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
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
|
|
42
|
-
|
|
43
|
-
| `json`
|
|
44
|
-
| `sarif`
|
|
45
|
-
| `container` | One JSON object per line (Codacy Docker runners)
|
|
46
|
-
| `text`
|
|
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
|
|
51
|
-
|
|
52
|
-
| `standard`
|
|
53
|
-
| `strict`
|
|
54
|
-
| `auto-install` | Attempt to install missing tools before analysis
|
|
55
|
-
| `inspect`
|
|
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
|
@@ -51,6 +51,15 @@ 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. */
|
|
@@ -659,6 +668,15 @@ interface LoggerOptions {
|
|
|
659
668
|
*/
|
|
660
669
|
onStderrMessage?: (text: string, level: LogLevel) => void;
|
|
661
670
|
}
|
|
671
|
+
/**
|
|
672
|
+
* Creates a no-op Logger that discards all messages.
|
|
673
|
+
*
|
|
674
|
+
* Intended for embedders (VSCode extension, MCP server, other library
|
|
675
|
+
* consumers) that call `analyze()` programmatically and do not want the
|
|
676
|
+
* pipeline writing to a log file or stderr. Passing this — or simply omitting
|
|
677
|
+
* `options.logger`, which defaults to it — keeps analysis completely silent.
|
|
678
|
+
*/
|
|
679
|
+
declare function createNullLogger(): Logger;
|
|
662
680
|
/**
|
|
663
681
|
* Removes old log files beyond the retention limit.
|
|
664
682
|
*
|
|
@@ -677,6 +695,50 @@ declare function rotateLogFiles(logsDir: string, maxFiles: number): void;
|
|
|
677
695
|
*/
|
|
678
696
|
declare function createLogger(options?: LoggerOptions): Logger;
|
|
679
697
|
|
|
698
|
+
/**
|
|
699
|
+
* Credential storage — encrypts and persists API tokens to ~/.codacy/credentials,
|
|
700
|
+
* and resolves an API token from the environment or stored credentials.
|
|
701
|
+
*
|
|
702
|
+
* Lives in the runner so that any embedder (CLI, MCP server, IDE integration) can
|
|
703
|
+
* reuse credential storage. The interactive prompt stays in the CLI.
|
|
704
|
+
*
|
|
705
|
+
* Replicates the encryption scheme from codacy-cloud-cli — the byte format,
|
|
706
|
+
* algorithm, key derivation, and file location are SHARED, so credentials written
|
|
707
|
+
* by one tool are readable by the other. DO NOT change any of the following:
|
|
708
|
+
* - File location: ~/.codacy/credentials
|
|
709
|
+
* - Machine key: [hostname, username, homedir, platform].join("|")
|
|
710
|
+
* - Key derivation: PBKDF2 (100k iterations, SHA-512, 32-byte key)
|
|
711
|
+
* - Encryption: AES-256-GCM with random salt (32B) and IV (16B)
|
|
712
|
+
* - Storage: JSON with hex-encoded { salt, iv, authTag, encrypted }
|
|
713
|
+
* - File permissions: 0o600 (owner read/write only)
|
|
714
|
+
*
|
|
715
|
+
* All crypto uses Node.js built-in `crypto` — no external dependencies.
|
|
716
|
+
*/
|
|
717
|
+
/**
|
|
718
|
+
* Encrypts and saves an API token to ~/.codacy/credentials.
|
|
719
|
+
* Creates the ~/.codacy/ directory if it doesn't exist.
|
|
720
|
+
*/
|
|
721
|
+
declare function saveCredentials(token: string): Promise<void>;
|
|
722
|
+
/**
|
|
723
|
+
* Reads and decrypts the stored API token from ~/.codacy/credentials.
|
|
724
|
+
* Returns null if the file is missing, corrupt, or cannot be decrypted.
|
|
725
|
+
*/
|
|
726
|
+
declare function loadCredentials(): Promise<string | null>;
|
|
727
|
+
/**
|
|
728
|
+
* Deletes the stored credentials file.
|
|
729
|
+
* Returns true if the file existed and was deleted, false otherwise.
|
|
730
|
+
*/
|
|
731
|
+
declare function deleteCredentials(): Promise<boolean>;
|
|
732
|
+
/**
|
|
733
|
+
* Resolves a Codacy API token from the environment or stored credentials.
|
|
734
|
+
*
|
|
735
|
+
* Resolution order:
|
|
736
|
+
* 1. CODACY_API_TOKEN environment variable
|
|
737
|
+
* 2. Encrypted credentials saved via `codacy-analysis login`
|
|
738
|
+
* 3. Throws with instructions if neither source has a token.
|
|
739
|
+
*/
|
|
740
|
+
declare function resolveApiToken(): Promise<string>;
|
|
741
|
+
|
|
680
742
|
/**
|
|
681
743
|
* Repository discovery — scans a codebase and identifies languages, file patterns,
|
|
682
744
|
* notable files, frameworks, and libraries.
|
|
@@ -699,4 +761,21 @@ interface DiscoverOptions {
|
|
|
699
761
|
*/
|
|
700
762
|
declare function discover(repositoryRoot: string, options?: DiscoverOptions): Promise<DiscoveryResult>;
|
|
701
763
|
|
|
702
|
-
|
|
764
|
+
/**
|
|
765
|
+
* Discovery catalog — static knowledge base for the discover command.
|
|
766
|
+
*
|
|
767
|
+
* Contains manifest file parsers, a curated dependency classification catalog,
|
|
768
|
+
* and notable file detection rules. All parsers are pure functions that accept
|
|
769
|
+
* raw file content and return dependency name arrays. They never throw.
|
|
770
|
+
*/
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Every display name discovery can emit from a dependency/file/package.json signal.
|
|
774
|
+
* This is the authoritative set a framework-mapping key must belong to (together
|
|
775
|
+
* with detected language names) for its patterns to ever gate on. The drift-guard
|
|
776
|
+
* test uses it to catch mapping keys that no signal can produce (e.g. a rename
|
|
777
|
+
* like react-intl → formatjs that updates the mapping but not the catalog).
|
|
778
|
+
*/
|
|
779
|
+
declare function getKnownDisplayNames(): Set<string>;
|
|
780
|
+
|
|
781
|
+
export { type AdapterFactory, type AnalyzeOptions, type AutoFilters, type CategoryFilter, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ConfigOperation, type DiscoverOptions, type ExecutionMode, type IgnoredFilesResult, type InitAutoStats, type InitDefaultStats, type InitLocalStats, type InitRemoteStats, type LoggerOptions, type PatternMetadataLookup, type SeverityFilter, analyze, applyCodacyYamlExcludes, applyConfigOperation, buildToolConfig, clearAdapters, computePatternSummary, configureApiToken, createCodacyConfig, createLogger, createNullLogger, deleteCredentials, detectLanguages, diffConfigs, discover, formatOutput, formatTextSummary, getAdapter, getKnownDisplayNames, getRegisteredAdapters, getRegisteredDescriptors, initAutoConfig, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, intersectConfigs, listDiffFiles, listIgnoredFiles, listPrFiles, listStagedFiles, loadCredentials, mergeConfigs, parseAutoFilters, readCodacyConfig, readCodacyYaml, registerAdapter, registerLazyAdapter, registerUnsupportedTool, resolveApiToken, resolveConfigPath, resolveEngineToAdapter, rotateLogFiles, saveCredentials, validateApiToken, writeCodacyConfig, writeCodacyConfigFile };
|
package/dist/index.js
CHANGED
|
@@ -39,12 +39,15 @@ __export(index_exports, {
|
|
|
39
39
|
configureApiToken: () => configureApiToken,
|
|
40
40
|
createCodacyConfig: () => createCodacyConfig,
|
|
41
41
|
createLogger: () => createLogger,
|
|
42
|
+
createNullLogger: () => createNullLogger,
|
|
43
|
+
deleteCredentials: () => deleteCredentials,
|
|
42
44
|
detectLanguages: () => detectLanguages,
|
|
43
45
|
diffConfigs: () => diffConfigs,
|
|
44
46
|
discover: () => discover,
|
|
45
47
|
formatOutput: () => formatOutput,
|
|
46
48
|
formatTextSummary: () => formatTextSummary,
|
|
47
49
|
getAdapter: () => getAdapter,
|
|
50
|
+
getKnownDisplayNames: () => getKnownDisplayNames,
|
|
48
51
|
getRegisteredAdapters: () => getRegisteredAdapters,
|
|
49
52
|
getRegisteredDescriptors: () => getRegisteredDescriptors,
|
|
50
53
|
initAutoConfig: () => initAutoConfig,
|
|
@@ -57,6 +60,7 @@ __export(index_exports, {
|
|
|
57
60
|
listIgnoredFiles: () => listIgnoredFiles,
|
|
58
61
|
listPrFiles: () => listPrFiles,
|
|
59
62
|
listStagedFiles: () => listStagedFiles,
|
|
63
|
+
loadCredentials: () => loadCredentials,
|
|
60
64
|
mergeConfigs: () => mergeConfigs,
|
|
61
65
|
parseAutoFilters: () => parseAutoFilters,
|
|
62
66
|
readCodacyConfig: () => readCodacyConfig,
|
|
@@ -64,9 +68,11 @@ __export(index_exports, {
|
|
|
64
68
|
registerAdapter: () => registerAdapter,
|
|
65
69
|
registerLazyAdapter: () => registerLazyAdapter,
|
|
66
70
|
registerUnsupportedTool: () => registerUnsupportedTool,
|
|
71
|
+
resolveApiToken: () => resolveApiToken,
|
|
67
72
|
resolveConfigPath: () => resolveConfigPath,
|
|
68
73
|
resolveEngineToAdapter: () => resolveEngineToAdapter,
|
|
69
74
|
rotateLogFiles: () => rotateLogFiles,
|
|
75
|
+
saveCredentials: () => saveCredentials,
|
|
70
76
|
validateApiToken: () => validateApiToken,
|
|
71
77
|
writeCodacyConfig: () => writeCodacyConfig,
|
|
72
78
|
writeCodacyConfigFile: () => writeCodacyConfigFile
|
|
@@ -74,8 +80,8 @@ __export(index_exports, {
|
|
|
74
80
|
module.exports = __toCommonJS(index_exports);
|
|
75
81
|
|
|
76
82
|
// src/analyze.ts
|
|
77
|
-
var
|
|
78
|
-
var
|
|
83
|
+
var import_os2 = __toESM(require("os"));
|
|
84
|
+
var import_path4 = __toESM(require("path"));
|
|
79
85
|
var import_child_process3 = require("child_process");
|
|
80
86
|
var import_util = require("util");
|
|
81
87
|
var import_tooling3 = require("@codacy/tooling");
|
|
@@ -418,6 +424,61 @@ function detectDefaultBranch(repositoryRoot) {
|
|
|
418
424
|
|
|
419
425
|
// src/files.ts
|
|
420
426
|
var MAX_FILE_SIZE_BYTES = 150 * 1024;
|
|
427
|
+
var DEPENDENCY_MANIFEST_FILES = /* @__PURE__ */ new Set([
|
|
428
|
+
// JavaScript / TypeScript
|
|
429
|
+
"package.json",
|
|
430
|
+
"package-lock.json",
|
|
431
|
+
"npm-shrinkwrap.json",
|
|
432
|
+
"yarn.lock",
|
|
433
|
+
"pnpm-lock.yaml",
|
|
434
|
+
"bun.lock",
|
|
435
|
+
// Python
|
|
436
|
+
"requirements.txt",
|
|
437
|
+
"Pipfile",
|
|
438
|
+
"Pipfile.lock",
|
|
439
|
+
"poetry.lock",
|
|
440
|
+
"uv.lock",
|
|
441
|
+
// PHP
|
|
442
|
+
"composer.json",
|
|
443
|
+
"composer.lock",
|
|
444
|
+
// Ruby
|
|
445
|
+
"Gemfile.lock",
|
|
446
|
+
// Rust
|
|
447
|
+
"Cargo.toml",
|
|
448
|
+
"Cargo.lock",
|
|
449
|
+
// Go
|
|
450
|
+
"go.mod",
|
|
451
|
+
"go.sum",
|
|
452
|
+
// .NET
|
|
453
|
+
"packages.config",
|
|
454
|
+
"packages.lock.json",
|
|
455
|
+
"Directory.Packages.props",
|
|
456
|
+
"Packages.props",
|
|
457
|
+
// Java / Scala
|
|
458
|
+
"pom.xml",
|
|
459
|
+
"gradle.lockfile",
|
|
460
|
+
"build.gradle",
|
|
461
|
+
"build.gradle.kts",
|
|
462
|
+
"build.sbt.lock",
|
|
463
|
+
// Dart / Flutter
|
|
464
|
+
"pubspec.yaml",
|
|
465
|
+
"pubspec.lock",
|
|
466
|
+
// Elixir
|
|
467
|
+
"mix.lock",
|
|
468
|
+
// Swift / CocoaPods
|
|
469
|
+
"Package.resolved",
|
|
470
|
+
"Podfile.lock",
|
|
471
|
+
// C / C++ (Conan)
|
|
472
|
+
"conan.lock",
|
|
473
|
+
// Erlang
|
|
474
|
+
"rebar.lock"
|
|
475
|
+
]);
|
|
476
|
+
var DEPENDENCY_MANIFEST_SUFFIXES = [".deps.json"];
|
|
477
|
+
function isDependencyManifest(relativePath) {
|
|
478
|
+
const base = relativePath.split("/").pop() ?? "";
|
|
479
|
+
if (DEPENDENCY_MANIFEST_FILES.has(base)) return true;
|
|
480
|
+
return DEPENDENCY_MANIFEST_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
481
|
+
}
|
|
421
482
|
function listRepoFiles(repositoryRoot) {
|
|
422
483
|
try {
|
|
423
484
|
const tracked = (0, import_child_process2.execSync)("git ls-files", {
|
|
@@ -449,6 +510,15 @@ function filterOversizedFiles(files, repositoryRoot, logger) {
|
|
|
449
510
|
try {
|
|
450
511
|
const stat = import_fs.default.statSync(import_path2.default.join(repositoryRoot, f));
|
|
451
512
|
if (stat.size > MAX_FILE_SIZE_BYTES) {
|
|
513
|
+
if (isDependencyManifest(f)) {
|
|
514
|
+
logger?.debug("Keeping oversized dependency manifest", {
|
|
515
|
+
file: f,
|
|
516
|
+
sizeKB: Math.round(stat.size / 1024),
|
|
517
|
+
limitKB: MAX_FILE_SIZE_BYTES / 1024
|
|
518
|
+
});
|
|
519
|
+
result.push(f);
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
452
522
|
skipped.push({ filePath: f, sizeBytes: stat.size });
|
|
453
523
|
logger?.debug("Skipping oversized file", {
|
|
454
524
|
file: f,
|
|
@@ -504,7 +574,43 @@ function filterByTargetFiles(allFiles, targetPatterns) {
|
|
|
504
574
|
// src/executor.ts
|
|
505
575
|
var import_tooling2 = require("@codacy/tooling");
|
|
506
576
|
var HARD_TIMEOUT_GRACE_MS = 2e3;
|
|
507
|
-
|
|
577
|
+
function anySignal(a, b) {
|
|
578
|
+
if (typeof AbortSignal.any === "function") return AbortSignal.any([a, b]);
|
|
579
|
+
const composite = new AbortController();
|
|
580
|
+
if (a.aborted || b.aborted) {
|
|
581
|
+
composite.abort();
|
|
582
|
+
} else {
|
|
583
|
+
const onAbort = () => composite.abort();
|
|
584
|
+
a.addEventListener("abort", onAbort, { once: true });
|
|
585
|
+
b.addEventListener("abort", onAbort, { once: true });
|
|
586
|
+
}
|
|
587
|
+
return composite.signal;
|
|
588
|
+
}
|
|
589
|
+
function cancelledResult(adapter) {
|
|
590
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
591
|
+
return {
|
|
592
|
+
metadata: { startedAt: now, completedAt: now, durationMs: 0 },
|
|
593
|
+
issues: [],
|
|
594
|
+
errors: [
|
|
595
|
+
{
|
|
596
|
+
toolId: adapter.id,
|
|
597
|
+
phase: "toolInvoke",
|
|
598
|
+
kind: "Cancelled",
|
|
599
|
+
message: `Analysis cancelled by caller; ${adapter.id} was not run`,
|
|
600
|
+
level: "error"
|
|
601
|
+
}
|
|
602
|
+
],
|
|
603
|
+
summary: {
|
|
604
|
+
toolId: adapter.id,
|
|
605
|
+
status: "skipped",
|
|
606
|
+
issueCount: 0,
|
|
607
|
+
errorCount: 1,
|
|
608
|
+
durationMs: 0,
|
|
609
|
+
filesAnalyzed: 0
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
async function runTool(adapter, ctx, codacyToolConfig, timeoutMs, callerSignal) {
|
|
508
614
|
const startTime = Date.now();
|
|
509
615
|
ctx.logger.debug("runTool started", {
|
|
510
616
|
toolId: adapter.id,
|
|
@@ -512,7 +618,7 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
|
|
|
512
618
|
timeoutMs
|
|
513
619
|
});
|
|
514
620
|
const abortController = new AbortController();
|
|
515
|
-
ctx.signal = abortController.signal;
|
|
621
|
+
ctx.signal = callerSignal ? anySignal(callerSignal, abortController.signal) : abortController.signal;
|
|
516
622
|
const abortTimer = setTimeout(() => abortController.abort(), timeoutMs);
|
|
517
623
|
abortTimer.unref?.();
|
|
518
624
|
let hardTimer;
|
|
@@ -533,8 +639,14 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
|
|
|
533
639
|
return result;
|
|
534
640
|
} catch (err) {
|
|
535
641
|
const durationMs = Date.now() - startTime;
|
|
536
|
-
const
|
|
537
|
-
|
|
642
|
+
const isCancelled = callerSignal?.aborted === true;
|
|
643
|
+
const isTimeout = !isCancelled && err instanceof Error && err.message.includes("TIMEOUT");
|
|
644
|
+
if (isCancelled) {
|
|
645
|
+
ctx.logger.error("Tool cancelled by caller", {
|
|
646
|
+
toolId: adapter.id,
|
|
647
|
+
elapsedMs: durationMs
|
|
648
|
+
});
|
|
649
|
+
} else if (isTimeout) {
|
|
538
650
|
ctx.logger.error("Tool timed out", { toolId: adapter.id, timeoutMs, elapsedMs: durationMs });
|
|
539
651
|
} else {
|
|
540
652
|
ctx.logger.error("Tool execution failed", {
|
|
@@ -544,6 +656,8 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
|
|
|
544
656
|
});
|
|
545
657
|
}
|
|
546
658
|
const now = /* @__PURE__ */ new Date();
|
|
659
|
+
const kind = isCancelled ? "Cancelled" : isTimeout ? "Timeout" : "InvocationError";
|
|
660
|
+
const message = isCancelled ? `Tool ${adapter.id} cancelled by caller` : isTimeout ? `Tool ${adapter.id} exceeded timeout of ${(0, import_tooling2.formatDuration)(timeoutMs)}` : `Tool ${adapter.id} failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
547
661
|
return {
|
|
548
662
|
metadata: {
|
|
549
663
|
startedAt: new Date(now.getTime() - durationMs).toISOString(),
|
|
@@ -555,8 +669,8 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
|
|
|
555
669
|
{
|
|
556
670
|
toolId: adapter.id,
|
|
557
671
|
phase: "toolInvoke",
|
|
558
|
-
kind
|
|
559
|
-
message
|
|
672
|
+
kind,
|
|
673
|
+
message,
|
|
560
674
|
level: "error"
|
|
561
675
|
}
|
|
562
676
|
],
|
|
@@ -574,15 +688,19 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
|
|
|
574
688
|
if (hardTimer) clearTimeout(hardTimer);
|
|
575
689
|
}
|
|
576
690
|
}
|
|
577
|
-
async function runToolsSequential(tools, timeoutMs, callbacks) {
|
|
691
|
+
async function runToolsSequential(tools, timeoutMs, callbacks, callerSignal) {
|
|
578
692
|
const results = [];
|
|
579
693
|
for (const { adapter, ctx, codacyToolConfig } of tools) {
|
|
694
|
+
if (callerSignal?.aborted) {
|
|
695
|
+
results.push(cancelledResult(adapter));
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
580
698
|
if (callbacks?.onToolProgress) {
|
|
581
699
|
const cb = callbacks.onToolProgress;
|
|
582
700
|
ctx.onProgress = (message) => cb(adapter.id, adapter.displayName, message);
|
|
583
701
|
}
|
|
584
702
|
callbacks?.onToolStart?.(adapter.id, adapter.displayName);
|
|
585
|
-
const result = await runTool(adapter, ctx, codacyToolConfig, timeoutMs);
|
|
703
|
+
const result = await runTool(adapter, ctx, codacyToolConfig, timeoutMs, callerSignal);
|
|
586
704
|
callbacks?.onToolComplete?.(
|
|
587
705
|
adapter.id,
|
|
588
706
|
adapter.displayName,
|
|
@@ -593,19 +711,23 @@ async function runToolsSequential(tools, timeoutMs, callbacks) {
|
|
|
593
711
|
}
|
|
594
712
|
return results;
|
|
595
713
|
}
|
|
596
|
-
async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
|
|
714
|
+
async function runToolsParallel(tools, timeoutMs, concurrency, callbacks, callerSignal) {
|
|
597
715
|
const results = new Array(tools.length);
|
|
598
716
|
let nextIndex = 0;
|
|
599
717
|
async function worker() {
|
|
600
718
|
while (nextIndex < tools.length) {
|
|
601
719
|
const index = nextIndex++;
|
|
602
720
|
const { adapter, ctx, codacyToolConfig } = tools[index];
|
|
721
|
+
if (callerSignal?.aborted) {
|
|
722
|
+
results[index] = cancelledResult(adapter);
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
603
725
|
if (callbacks?.onToolProgress) {
|
|
604
726
|
const cb = callbacks.onToolProgress;
|
|
605
727
|
ctx.onProgress = (message) => cb(adapter.id, adapter.displayName, message);
|
|
606
728
|
}
|
|
607
729
|
callbacks?.onToolStart?.(adapter.id, adapter.displayName);
|
|
608
|
-
results[index] = await runTool(adapter, ctx, codacyToolConfig, timeoutMs);
|
|
730
|
+
results[index] = await runTool(adapter, ctx, codacyToolConfig, timeoutMs, callerSignal);
|
|
609
731
|
callbacks?.onToolComplete?.(
|
|
610
732
|
adapter.id,
|
|
611
733
|
adapter.displayName,
|
|
@@ -619,6 +741,124 @@ async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
|
|
|
619
741
|
return results;
|
|
620
742
|
}
|
|
621
743
|
|
|
744
|
+
// src/logger.ts
|
|
745
|
+
var import_fs2 = __toESM(require("fs"));
|
|
746
|
+
var import_path3 = __toESM(require("path"));
|
|
747
|
+
var import_os = __toESM(require("os"));
|
|
748
|
+
var LEVEL_ORDER = {
|
|
749
|
+
debug: 0,
|
|
750
|
+
info: 1,
|
|
751
|
+
warning: 2,
|
|
752
|
+
error: 3
|
|
753
|
+
};
|
|
754
|
+
function createNullLogger() {
|
|
755
|
+
return {
|
|
756
|
+
debug: () => {
|
|
757
|
+
},
|
|
758
|
+
info: () => {
|
|
759
|
+
},
|
|
760
|
+
warn: () => {
|
|
761
|
+
},
|
|
762
|
+
error: () => {
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function rotateLogFiles(logsDir, maxFiles) {
|
|
767
|
+
let entries2;
|
|
768
|
+
try {
|
|
769
|
+
entries2 = import_fs2.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
|
|
770
|
+
} catch {
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
if (entries2.length <= maxFiles) return;
|
|
774
|
+
entries2.sort();
|
|
775
|
+
const toRemove = entries2.slice(0, entries2.length - maxFiles);
|
|
776
|
+
for (const f of toRemove) {
|
|
777
|
+
try {
|
|
778
|
+
import_fs2.default.unlinkSync(import_path3.default.join(logsDir, f));
|
|
779
|
+
} catch {
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
var fileSeq = 0;
|
|
784
|
+
function fileTimestamp() {
|
|
785
|
+
return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${fileSeq++}`;
|
|
786
|
+
}
|
|
787
|
+
function enrichMessage(message, fields) {
|
|
788
|
+
if (!fields) return message;
|
|
789
|
+
const parts = [];
|
|
790
|
+
if (fields.toolId) parts.push(String(fields.toolId));
|
|
791
|
+
if (fields.version) parts.push(`v${fields.version}`);
|
|
792
|
+
if (fields.installation) parts.push(String(fields.installation));
|
|
793
|
+
if (fields.configSource) parts.push(String(fields.configSource));
|
|
794
|
+
if (typeof fields.fileCount === "number") parts.push(`${fields.fileCount} files`);
|
|
795
|
+
if (typeof fields.issueCount === "number") parts.push(`${fields.issueCount} issues`);
|
|
796
|
+
if (typeof fields.errorCount === "number" && fields.errorCount > 0)
|
|
797
|
+
parts.push(`${fields.errorCount} errors`);
|
|
798
|
+
if (typeof fields.totalIssues === "number") parts.push(`${fields.totalIssues} issues`);
|
|
799
|
+
if (typeof fields.totalErrors === "number" && fields.totalErrors > 0)
|
|
800
|
+
parts.push(`${fields.totalErrors} errors`);
|
|
801
|
+
if (typeof fields.durationMs === "number")
|
|
802
|
+
parts.push(`${(fields.durationMs / 1e3).toFixed(1)}s`);
|
|
803
|
+
if (fields.reason) parts.push(String(fields.reason));
|
|
804
|
+
if (fields.error) parts.push(String(fields.error));
|
|
805
|
+
if (typeof fields.found === "boolean") parts.push(fields.found ? "found" : "not found");
|
|
806
|
+
if (fields.path && !fields.version) parts.push(String(fields.path));
|
|
807
|
+
if (parts.length === 0) return message;
|
|
808
|
+
return `${message} (${parts.join(", ")})`;
|
|
809
|
+
}
|
|
810
|
+
function createLogger(options = {}) {
|
|
811
|
+
const minLevel = LEVEL_ORDER[options.level ?? "info"];
|
|
812
|
+
const fileEnabled = options.fileEnabled ?? true;
|
|
813
|
+
const logsDir = options.logsDir ?? import_path3.default.join(import_os.default.homedir(), ".codacy", "logs");
|
|
814
|
+
const maxFileSize = options.maxFileSize ?? 10 * 1024 * 1024;
|
|
815
|
+
const maxFiles = options.maxFiles ?? 5;
|
|
816
|
+
const onStderrMessage = options.onStderrMessage;
|
|
817
|
+
let logFd = null;
|
|
818
|
+
let bytesWritten = 0;
|
|
819
|
+
if (fileEnabled) {
|
|
820
|
+
import_fs2.default.mkdirSync(logsDir, { recursive: true });
|
|
821
|
+
rotateLogFiles(logsDir, maxFiles);
|
|
822
|
+
const logFilePath = import_path3.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
|
|
823
|
+
logFd = import_fs2.default.openSync(logFilePath, "a");
|
|
824
|
+
}
|
|
825
|
+
function log(level, message, fields) {
|
|
826
|
+
const numLevel = LEVEL_ORDER[level];
|
|
827
|
+
if (numLevel < minLevel) return;
|
|
828
|
+
if (fileEnabled && logFd !== null) {
|
|
829
|
+
const entry = JSON.stringify({
|
|
830
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
831
|
+
level,
|
|
832
|
+
message,
|
|
833
|
+
...fields
|
|
834
|
+
});
|
|
835
|
+
const buf = Buffer.from(entry + "\n", "utf-8");
|
|
836
|
+
import_fs2.default.writeSync(logFd, buf);
|
|
837
|
+
bytesWritten += buf.length;
|
|
838
|
+
if (bytesWritten >= maxFileSize) {
|
|
839
|
+
import_fs2.default.closeSync(logFd);
|
|
840
|
+
rotateLogFiles(logsDir, maxFiles);
|
|
841
|
+
const newPath = import_path3.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
|
|
842
|
+
logFd = import_fs2.default.openSync(newPath, "a");
|
|
843
|
+
bytesWritten = 0;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
const enriched = enrichMessage(message, fields);
|
|
847
|
+
if (onStderrMessage) {
|
|
848
|
+
onStderrMessage(enriched, level);
|
|
849
|
+
} else {
|
|
850
|
+
process.stderr.write(` ${enriched}
|
|
851
|
+
`);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return {
|
|
855
|
+
debug: (msg, fields) => log("debug", msg, fields),
|
|
856
|
+
info: (msg, fields) => log("info", msg, fields),
|
|
857
|
+
warn: (msg, fields) => log("warning", msg, fields),
|
|
858
|
+
error: (msg, fields) => log("error", msg, fields)
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
|
|
622
862
|
// src/summary.ts
|
|
623
863
|
var NOISY_THRESHOLD = 500;
|
|
624
864
|
var SEVERITY_ORDER = ["Error", "High", "Warning", "Info"];
|
|
@@ -731,16 +971,6 @@ function computePatternSummary(issues, lookup) {
|
|
|
731
971
|
|
|
732
972
|
// src/analyze.ts
|
|
733
973
|
var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
|
|
734
|
-
var nullLogger = {
|
|
735
|
-
debug: () => {
|
|
736
|
-
},
|
|
737
|
-
info: () => {
|
|
738
|
-
},
|
|
739
|
-
warn: () => {
|
|
740
|
-
},
|
|
741
|
-
error: () => {
|
|
742
|
-
}
|
|
743
|
-
};
|
|
744
974
|
var CHECK_CONCURRENCY = 12;
|
|
745
975
|
async function mapWithConcurrency(items, concurrency, task) {
|
|
746
976
|
const results = new Array(items.length);
|
|
@@ -760,7 +990,7 @@ async function analyze(options) {
|
|
|
760
990
|
const mode = options.mode || "standard";
|
|
761
991
|
const toolTimeout = options.toolTimeout || 9e5;
|
|
762
992
|
const parallelTools = options.parallelTools || 1;
|
|
763
|
-
const logger = options.logger ??
|
|
993
|
+
const logger = options.logger ?? createNullLogger();
|
|
764
994
|
const phaseTimings = {
|
|
765
995
|
availabilityMs: 0,
|
|
766
996
|
fileDiscoveryMs: 0,
|
|
@@ -773,6 +1003,26 @@ async function analyze(options) {
|
|
|
773
1003
|
unavailable: [],
|
|
774
1004
|
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
775
1005
|
};
|
|
1006
|
+
if (options.signal?.aborted) {
|
|
1007
|
+
logger.info("Analysis cancelled by caller before it started");
|
|
1008
|
+
return buildResult(
|
|
1009
|
+
startedAt,
|
|
1010
|
+
options.repositoryRoot,
|
|
1011
|
+
mode,
|
|
1012
|
+
emptyCapability,
|
|
1013
|
+
[],
|
|
1014
|
+
[
|
|
1015
|
+
{
|
|
1016
|
+
toolId: "",
|
|
1017
|
+
phase: "toolInvoke",
|
|
1018
|
+
kind: "Cancelled",
|
|
1019
|
+
message: "Analysis cancelled by caller before it started",
|
|
1020
|
+
level: "error"
|
|
1021
|
+
}
|
|
1022
|
+
],
|
|
1023
|
+
[]
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
776
1026
|
try {
|
|
777
1027
|
await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
778
1028
|
cwd: options.repositoryRoot
|
|
@@ -836,11 +1086,11 @@ async function analyze(options) {
|
|
|
836
1086
|
const requestedTools = options.tools ?? codacyConfig.tools.map((t) => t.toolId);
|
|
837
1087
|
const adapters = await getRegisteredAdapters(requestedTools);
|
|
838
1088
|
logger.info("Pipeline started", { mode, toolCount: adapters.length, parallelTools, toolTimeout });
|
|
839
|
-
const globalDir =
|
|
1089
|
+
const globalDir = import_path4.default.join(import_os2.default.homedir(), ".codacy");
|
|
840
1090
|
const baseCtx = {
|
|
841
1091
|
repositoryRoot: options.repositoryRoot,
|
|
842
1092
|
globalDir,
|
|
843
|
-
platform: { os:
|
|
1093
|
+
platform: { os: import_os2.default.platform(), arch: import_os2.default.arch() },
|
|
844
1094
|
logger,
|
|
845
1095
|
runner: "local"
|
|
846
1096
|
};
|
|
@@ -1202,9 +1452,15 @@ async function analyze(options) {
|
|
|
1202
1452
|
};
|
|
1203
1453
|
let results;
|
|
1204
1454
|
if (parallelTools > 1 && toolRuns.length > 1) {
|
|
1205
|
-
results = await runToolsParallel(
|
|
1455
|
+
results = await runToolsParallel(
|
|
1456
|
+
toolRuns,
|
|
1457
|
+
toolTimeout,
|
|
1458
|
+
parallelTools,
|
|
1459
|
+
callbacks,
|
|
1460
|
+
options.signal
|
|
1461
|
+
);
|
|
1206
1462
|
} else {
|
|
1207
|
-
results = await runToolsSequential(toolRuns, toolTimeout, callbacks);
|
|
1463
|
+
results = await runToolsSequential(toolRuns, toolTimeout, callbacks, options.signal);
|
|
1208
1464
|
}
|
|
1209
1465
|
phaseTimings.toolExecutionMs = Date.now() - toolExecStartMs;
|
|
1210
1466
|
const allIssues = normalizeIssues(results.flatMap((r) => r.issues));
|
|
@@ -1245,8 +1501,8 @@ function makeToolContext(base, adapter, targetFiles) {
|
|
|
1245
1501
|
return {
|
|
1246
1502
|
...base,
|
|
1247
1503
|
targetFiles,
|
|
1248
|
-
toolInstallDir:
|
|
1249
|
-
generatedConfigDir:
|
|
1504
|
+
toolInstallDir: import_path4.default.join(base.globalDir, "tools", adapter.id),
|
|
1505
|
+
generatedConfigDir: import_path4.default.join(base.repositoryRoot, ".codacy", "generated", adapter.id)
|
|
1250
1506
|
};
|
|
1251
1507
|
}
|
|
1252
1508
|
function normalizeIssues(issues) {
|
|
@@ -1285,7 +1541,7 @@ function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors
|
|
|
1285
1541
|
}
|
|
1286
1542
|
|
|
1287
1543
|
// src/format.ts
|
|
1288
|
-
var
|
|
1544
|
+
var import_path5 = __toESM(require("path"));
|
|
1289
1545
|
var import_tooling4 = require("@codacy/tooling");
|
|
1290
1546
|
var import_ansis = __toESM(require("ansis"));
|
|
1291
1547
|
var import_cli_table3 = __toESM(require("cli-table3"));
|
|
@@ -1711,7 +1967,7 @@ function formatFilesCell(filesRouted) {
|
|
|
1711
1967
|
}
|
|
1712
1968
|
function formatConfigCell(tool, repositoryRoot) {
|
|
1713
1969
|
if (tool.configSource === "local" && tool.configPath) {
|
|
1714
|
-
const rawPath =
|
|
1970
|
+
const rawPath = import_path5.default.isAbsolute(tool.configPath) ? import_path5.default.relative(repositoryRoot, tool.configPath) : tool.configPath;
|
|
1715
1971
|
return rawPath.startsWith("./") ? rawPath : `./${rawPath}`;
|
|
1716
1972
|
}
|
|
1717
1973
|
if (tool.configSource === "codacy") return "Codacy";
|
|
@@ -1973,13 +2229,13 @@ function applyConfigOperation(op, source, dest) {
|
|
|
1973
2229
|
|
|
1974
2230
|
// src/init.ts
|
|
1975
2231
|
var import_promises4 = __toESM(require("fs/promises"));
|
|
1976
|
-
var
|
|
2232
|
+
var import_path8 = __toESM(require("path"));
|
|
1977
2233
|
var import_child_process4 = require("child_process");
|
|
1978
2234
|
var import_tooling5 = require("@codacy/tooling");
|
|
1979
2235
|
|
|
1980
2236
|
// src/discover.ts
|
|
1981
2237
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
1982
|
-
var
|
|
2238
|
+
var import_path6 = __toESM(require("path"));
|
|
1983
2239
|
|
|
1984
2240
|
// src/discover-catalog.ts
|
|
1985
2241
|
var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
@@ -2009,6 +2265,10 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2009
2265
|
["@hapi/hapi", { displayName: "Hapi", kind: "framework" }],
|
|
2010
2266
|
["hono", { displayName: "Hono", kind: "framework" }],
|
|
2011
2267
|
["electron", { displayName: "Electron", kind: "framework" }],
|
|
2268
|
+
// AWS CDK — the v2 umbrella package `aws-cdk-lib`, the CLI package `aws-cdk`,
|
|
2269
|
+
// and the v1 `@aws-cdk/*` construct packages (prefix rule below).
|
|
2270
|
+
["aws-cdk-lib", { displayName: "AWS CDK", kind: "framework" }],
|
|
2271
|
+
["aws-cdk", { displayName: "AWS CDK", kind: "framework" }],
|
|
2012
2272
|
// --- JavaScript / TypeScript — Libraries ---
|
|
2013
2273
|
["lodash", { displayName: "Lodash", kind: "library" }],
|
|
2014
2274
|
["underscore", { displayName: "Underscore.js", kind: "library" }],
|
|
@@ -2061,9 +2321,20 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2061
2321
|
["pino", { displayName: "Pino", kind: "library" }],
|
|
2062
2322
|
["winston", { displayName: "Winston", kind: "library" }],
|
|
2063
2323
|
["i18next", { displayName: "i18next", kind: "library" }],
|
|
2064
|
-
|
|
2324
|
+
// react-intl is the React binding of the FormatJS ecosystem; eslint-plugin-formatjs
|
|
2325
|
+
// lints both, so we report the umbrella name "formatjs" (matches the mapping key).
|
|
2326
|
+
["react-intl", { displayName: "formatjs", kind: "library" }],
|
|
2065
2327
|
["vue-i18n", { displayName: "vue-i18n", kind: "library" }],
|
|
2066
2328
|
["next-intl", { displayName: "next-intl", kind: "library" }],
|
|
2329
|
+
// Type checker (eslint-plugin-flowtype). Also detected via a `.flowconfig` file.
|
|
2330
|
+
["flow-bin", { displayName: "Flow", kind: "library" }],
|
|
2331
|
+
// Legacy JS libraries with dedicated ESLint plugins.
|
|
2332
|
+
["backbone", { displayName: "Backbone.js", kind: "library" }],
|
|
2333
|
+
["chai", { displayName: "Chai", kind: "library" }],
|
|
2334
|
+
["codeceptjs", { displayName: "CodeceptJS", kind: "library" }],
|
|
2335
|
+
["webdriverio", { displayName: "WebdriverIO", kind: "library" }],
|
|
2336
|
+
["react-relay", { displayName: "Relay", kind: "library" }],
|
|
2337
|
+
["relay-runtime", { displayName: "Relay", kind: "library" }],
|
|
2067
2338
|
// --- JavaScript / TypeScript — Tools ---
|
|
2068
2339
|
["typescript", { displayName: "TypeScript", kind: "tool" }],
|
|
2069
2340
|
["eslint", { displayName: "ESLint", kind: "tool" }],
|
|
@@ -2275,7 +2546,9 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2275
2546
|
["cats-core", { displayName: "Cats", kind: "library" }],
|
|
2276
2547
|
["circe-core", { displayName: "Circe", kind: "library" }],
|
|
2277
2548
|
// --- Dart — Frameworks ---
|
|
2278
|
-
// `flutter` and `angular` appear as plain keys in pubspec.yaml.
|
|
2549
|
+
// `flutter` and `angular` appear as plain keys in pubspec.yaml. NOTE: `angular`
|
|
2550
|
+
// here is Dart's AngularDart. The npm package `angular` is AngularJS (1.x) — a
|
|
2551
|
+
// different framework — so package.json overrides this via MANIFEST_SCOPED_CATALOG.
|
|
2279
2552
|
["flutter", { displayName: "Flutter", kind: "framework" }],
|
|
2280
2553
|
["flutter_test", { displayName: "Flutter", kind: "framework" }],
|
|
2281
2554
|
["angular", { displayName: "Angular", kind: "framework" }],
|
|
@@ -2312,10 +2585,13 @@ var DEPENDENCY_PREFIX_RULES = [
|
|
|
2312
2585
|
classification: { displayName: "Testing Library", kind: "library" }
|
|
2313
2586
|
},
|
|
2314
2587
|
{ prefix: "@tanstack/", classification: { displayName: "TanStack", kind: "library" } },
|
|
2588
|
+
{ prefix: "@wdio/", classification: { displayName: "WebdriverIO", kind: "library" } },
|
|
2589
|
+
// AWS CDK v1 construct libraries are published under the @aws-cdk/ scope.
|
|
2590
|
+
{ prefix: "@aws-cdk/", classification: { displayName: "AWS CDK", kind: "framework" } },
|
|
2315
2591
|
{ prefix: "@reduxjs/", classification: { displayName: "Redux", kind: "library" } },
|
|
2316
2592
|
{ prefix: "@prisma/", classification: { displayName: "Prisma", kind: "library" } },
|
|
2317
2593
|
{ prefix: "@apollo/", classification: { displayName: "Apollo", kind: "library" } },
|
|
2318
|
-
{ prefix: "@formatjs/", classification: { displayName: "
|
|
2594
|
+
{ prefix: "@formatjs/", classification: { displayName: "formatjs", kind: "library" } },
|
|
2319
2595
|
{ prefix: "@lingui/", classification: { displayName: "Lingui", kind: "library" } },
|
|
2320
2596
|
{ prefix: "@emotion/", classification: { displayName: "Emotion", kind: "library" } },
|
|
2321
2597
|
{ prefix: "@mui/", classification: { displayName: "Material UI", kind: "library" } },
|
|
@@ -2358,7 +2634,14 @@ var DEPENDENCY_PREFIX_RULES = [
|
|
|
2358
2634
|
{ prefix: "MSTest.", classification: { displayName: "MSTest", kind: "library" } },
|
|
2359
2635
|
{ prefix: "Serilog", classification: { displayName: "Serilog", kind: "library" } }
|
|
2360
2636
|
];
|
|
2361
|
-
|
|
2637
|
+
var MANIFEST_SCOPED_CATALOG = /* @__PURE__ */ new Map([
|
|
2638
|
+
["package.json", /* @__PURE__ */ new Map([["angular", { displayName: "AngularJS", kind: "framework" }]])]
|
|
2639
|
+
]);
|
|
2640
|
+
function classifyDependency(name, manifestBasename) {
|
|
2641
|
+
if (manifestBasename) {
|
|
2642
|
+
const scoped = MANIFEST_SCOPED_CATALOG.get(manifestBasename)?.get(name);
|
|
2643
|
+
if (scoped) return scoped;
|
|
2644
|
+
}
|
|
2362
2645
|
const exact = DEPENDENCY_CATALOG.get(name);
|
|
2363
2646
|
if (exact) return exact;
|
|
2364
2647
|
for (const rule of DEPENDENCY_PREFIX_RULES) {
|
|
@@ -2366,6 +2649,91 @@ function classifyDependency(name) {
|
|
|
2366
2649
|
}
|
|
2367
2650
|
return void 0;
|
|
2368
2651
|
}
|
|
2652
|
+
function pathBasename(filePath) {
|
|
2653
|
+
return filePath.split("/").pop() ?? filePath;
|
|
2654
|
+
}
|
|
2655
|
+
var FILE_SIGNAL_RULES = [
|
|
2656
|
+
{
|
|
2657
|
+
name: ".flowconfig",
|
|
2658
|
+
matches: (f) => pathBasename(f) === ".flowconfig",
|
|
2659
|
+
classification: { displayName: "Flow", kind: "library" }
|
|
2660
|
+
},
|
|
2661
|
+
{
|
|
2662
|
+
name: "cdk.json",
|
|
2663
|
+
matches: (f) => pathBasename(f) === "cdk.json",
|
|
2664
|
+
classification: { displayName: "AWS CDK", kind: "framework" }
|
|
2665
|
+
},
|
|
2666
|
+
{
|
|
2667
|
+
name: ".meteor/release",
|
|
2668
|
+
matches: (f) => f === ".meteor/release" || f.endsWith("/.meteor/release"),
|
|
2669
|
+
classification: { displayName: "Meteor", kind: "framework" }
|
|
2670
|
+
},
|
|
2671
|
+
{
|
|
2672
|
+
name: "sfdx-project.json",
|
|
2673
|
+
matches: (f) => pathBasename(f) === "sfdx-project.json",
|
|
2674
|
+
classification: { displayName: "Salesforce", kind: "platform" }
|
|
2675
|
+
},
|
|
2676
|
+
{
|
|
2677
|
+
name: "lwc.config.json",
|
|
2678
|
+
matches: (f) => pathBasename(f) === "lwc.config.json",
|
|
2679
|
+
classification: { displayName: "Lightning Web Components", kind: "platform" }
|
|
2680
|
+
},
|
|
2681
|
+
{
|
|
2682
|
+
name: "suitecloud.config.js",
|
|
2683
|
+
matches: (f) => pathBasename(f) === "suitecloud.config.js",
|
|
2684
|
+
classification: { displayName: "SuiteScript", kind: "platform" }
|
|
2685
|
+
}
|
|
2686
|
+
];
|
|
2687
|
+
function detectFileSignals(files) {
|
|
2688
|
+
const result = [];
|
|
2689
|
+
for (const rule of FILE_SIGNAL_RULES) {
|
|
2690
|
+
const hit = files.find((f) => rule.matches(f));
|
|
2691
|
+
if (!hit) continue;
|
|
2692
|
+
result.push({
|
|
2693
|
+
name: rule.name,
|
|
2694
|
+
displayName: rule.classification.displayName,
|
|
2695
|
+
kind: rule.classification.kind,
|
|
2696
|
+
source: "file",
|
|
2697
|
+
manifestFile: hit
|
|
2698
|
+
});
|
|
2699
|
+
}
|
|
2700
|
+
return result;
|
|
2701
|
+
}
|
|
2702
|
+
var PACKAGE_JSON_SIGNAL_DISPLAY_NAMES = ["ESLint Plugin"];
|
|
2703
|
+
function isEslintPluginName(name) {
|
|
2704
|
+
return /^eslint-plugin-/.test(name) || /^@[^/]+\/eslint-plugin(-.*)?$/.test(name);
|
|
2705
|
+
}
|
|
2706
|
+
function detectPackageJsonSignals(content) {
|
|
2707
|
+
let pkg;
|
|
2708
|
+
try {
|
|
2709
|
+
pkg = JSON.parse(content);
|
|
2710
|
+
} catch {
|
|
2711
|
+
return [];
|
|
2712
|
+
}
|
|
2713
|
+
const name = pkg && typeof pkg.name === "string" ? pkg.name : "";
|
|
2714
|
+
if (name && isEslintPluginName(name)) {
|
|
2715
|
+
return [
|
|
2716
|
+
{
|
|
2717
|
+
name,
|
|
2718
|
+
displayName: "ESLint Plugin",
|
|
2719
|
+
kind: "tool",
|
|
2720
|
+
source: "manifest",
|
|
2721
|
+
manifestFile: "package.json"
|
|
2722
|
+
}
|
|
2723
|
+
];
|
|
2724
|
+
}
|
|
2725
|
+
return [];
|
|
2726
|
+
}
|
|
2727
|
+
function getKnownDisplayNames() {
|
|
2728
|
+
const names = /* @__PURE__ */ new Set();
|
|
2729
|
+
for (const { displayName } of DEPENDENCY_CATALOG.values()) names.add(displayName);
|
|
2730
|
+
for (const scoped of MANIFEST_SCOPED_CATALOG.values())
|
|
2731
|
+
for (const { displayName } of scoped.values()) names.add(displayName);
|
|
2732
|
+
for (const rule of DEPENDENCY_PREFIX_RULES) names.add(rule.classification.displayName);
|
|
2733
|
+
for (const rule of FILE_SIGNAL_RULES) names.add(rule.classification.displayName);
|
|
2734
|
+
for (const name of PACKAGE_JSON_SIGNAL_DISPLAY_NAMES) names.add(name);
|
|
2735
|
+
return names;
|
|
2736
|
+
}
|
|
2369
2737
|
var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
|
|
2370
2738
|
"Dockerfile",
|
|
2371
2739
|
"docker-compose.yml",
|
|
@@ -2735,7 +3103,8 @@ async function discover(repositoryRoot, options) {
|
|
|
2735
3103
|
let frameworks = [];
|
|
2736
3104
|
let libraries = [];
|
|
2737
3105
|
if (!options?.skipDependencies) {
|
|
2738
|
-
const
|
|
3106
|
+
const manifestDeps = await extractDependencies(repositoryRoot, files);
|
|
3107
|
+
const allDeps = [...manifestDeps, ...detectFileSignals(files)];
|
|
2739
3108
|
frameworks = deduplicateDeps(allDeps.filter((d) => d.kind === "framework"));
|
|
2740
3109
|
libraries = deduplicateDeps(
|
|
2741
3110
|
allDeps.filter((d) => d.kind === "library" || d.kind === "platform" || d.kind === "tool")
|
|
@@ -2748,7 +3117,7 @@ async function discover(repositoryRoot, options) {
|
|
|
2748
3117
|
frameworks,
|
|
2749
3118
|
libraries,
|
|
2750
3119
|
metadata: {
|
|
2751
|
-
repositoryRoot:
|
|
3120
|
+
repositoryRoot: import_path6.default.resolve(repositoryRoot),
|
|
2752
3121
|
totalFiles: files.length,
|
|
2753
3122
|
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2754
3123
|
}
|
|
@@ -2757,7 +3126,7 @@ async function discover(repositoryRoot, options) {
|
|
|
2757
3126
|
function extractFilePatterns(files) {
|
|
2758
3127
|
const extensions = /* @__PURE__ */ new Set();
|
|
2759
3128
|
for (const file of files) {
|
|
2760
|
-
const ext =
|
|
3129
|
+
const ext = import_path6.default.extname(file).toLowerCase();
|
|
2761
3130
|
if (ext) {
|
|
2762
3131
|
extensions.add(`*${ext}`);
|
|
2763
3132
|
}
|
|
@@ -2790,13 +3159,14 @@ async function extractDependencies(repositoryRoot, files) {
|
|
|
2790
3159
|
if (!parser) continue;
|
|
2791
3160
|
let content;
|
|
2792
3161
|
try {
|
|
2793
|
-
content = await import_promises2.default.readFile(
|
|
3162
|
+
content = await import_promises2.default.readFile(import_path6.default.join(repositoryRoot, filePath), "utf-8");
|
|
2794
3163
|
} catch {
|
|
2795
3164
|
continue;
|
|
2796
3165
|
}
|
|
3166
|
+
const basename = filePath.split("/").pop() ?? filePath;
|
|
2797
3167
|
const rawDeps = parser(content);
|
|
2798
3168
|
for (const raw of rawDeps) {
|
|
2799
|
-
const classification = classifyDependency(raw.name);
|
|
3169
|
+
const classification = classifyDependency(raw.name, basename);
|
|
2800
3170
|
if (classification) {
|
|
2801
3171
|
allDeps.push({
|
|
2802
3172
|
name: raw.name,
|
|
@@ -2808,6 +3178,11 @@ async function extractDependencies(repositoryRoot, files) {
|
|
|
2808
3178
|
});
|
|
2809
3179
|
}
|
|
2810
3180
|
}
|
|
3181
|
+
if (basename === "package.json") {
|
|
3182
|
+
for (const signal of detectPackageJsonSignals(content)) {
|
|
3183
|
+
allDeps.push({ ...signal, manifestFile: filePath });
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
2811
3186
|
}
|
|
2812
3187
|
return allDeps;
|
|
2813
3188
|
}
|
|
@@ -2825,14 +3200,14 @@ function deduplicateDeps(deps) {
|
|
|
2825
3200
|
|
|
2826
3201
|
// src/codacy-yaml.ts
|
|
2827
3202
|
var import_promises3 = __toESM(require("fs/promises"));
|
|
2828
|
-
var
|
|
3203
|
+
var import_path7 = __toESM(require("path"));
|
|
2829
3204
|
var import_yaml = require("yaml");
|
|
2830
3205
|
async function readCodacyYaml(repositoryRoot) {
|
|
2831
3206
|
const candidates = [".codacy.yaml", ".codacy.yml"];
|
|
2832
3207
|
let raw = null;
|
|
2833
3208
|
for (const filename of candidates) {
|
|
2834
3209
|
try {
|
|
2835
|
-
raw = await import_promises3.default.readFile(
|
|
3210
|
+
raw = await import_promises3.default.readFile(import_path7.default.join(repositoryRoot, filename), "utf-8");
|
|
2836
3211
|
break;
|
|
2837
3212
|
} catch {
|
|
2838
3213
|
}
|
|
@@ -3090,13 +3465,13 @@ var getQueryString = (params) => {
|
|
|
3090
3465
|
};
|
|
3091
3466
|
var getUrl = (config, options) => {
|
|
3092
3467
|
const encoder = config.ENCODE_PATH || encodeURI;
|
|
3093
|
-
const
|
|
3468
|
+
const path10 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
|
|
3094
3469
|
if (options.path?.hasOwnProperty(group)) {
|
|
3095
3470
|
return encoder(String(options.path[group]));
|
|
3096
3471
|
}
|
|
3097
3472
|
return substring;
|
|
3098
3473
|
});
|
|
3099
|
-
const url = `${config.BASE}${
|
|
3474
|
+
const url = `${config.BASE}${path10}`;
|
|
3100
3475
|
if (options.query) {
|
|
3101
3476
|
return `${url}${getQueryString(options.query)}`;
|
|
3102
3477
|
}
|
|
@@ -6490,8 +6865,8 @@ async function detectLanguages(repositoryRoot, precomputedFiles) {
|
|
|
6490
6865
|
}
|
|
6491
6866
|
const detected = /* @__PURE__ */ new Set();
|
|
6492
6867
|
for (const file of files) {
|
|
6493
|
-
const basename =
|
|
6494
|
-
const ext =
|
|
6868
|
+
const basename = import_path8.default.basename(file);
|
|
6869
|
+
const ext = import_path8.default.extname(file).toLowerCase();
|
|
6495
6870
|
for (const [lang, config] of Object.entries(import_tooling5.LANGUAGE_CONFIG)) {
|
|
6496
6871
|
if (config.extensions.includes(ext)) {
|
|
6497
6872
|
detected.add(lang);
|
|
@@ -6508,12 +6883,12 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
|
|
|
6508
6883
|
adapters.map(async (adapter) => {
|
|
6509
6884
|
const result = await adapter.checkLocalConfigurationFile({
|
|
6510
6885
|
repositoryRoot,
|
|
6511
|
-
generatedConfigDir:
|
|
6512
|
-
globalDir:
|
|
6513
|
-
toolInstallDir:
|
|
6886
|
+
generatedConfigDir: import_path8.default.join(repositoryRoot, ".codacy", "generated"),
|
|
6887
|
+
globalDir: import_path8.default.join(process.env.HOME ?? "~", ".codacy"),
|
|
6888
|
+
toolInstallDir: import_path8.default.join(process.env.HOME ?? "~", ".codacy", "tools", adapter.id),
|
|
6514
6889
|
targetFiles: [],
|
|
6515
6890
|
platform: { os: process.platform, arch: process.arch },
|
|
6516
|
-
logger:
|
|
6891
|
+
logger: createNullLogger(),
|
|
6517
6892
|
runner: "local"
|
|
6518
6893
|
});
|
|
6519
6894
|
return { toolId: adapter.id, result };
|
|
@@ -6526,16 +6901,6 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
|
|
|
6526
6901
|
}
|
|
6527
6902
|
return found;
|
|
6528
6903
|
}
|
|
6529
|
-
var silentLogger = {
|
|
6530
|
-
debug: () => {
|
|
6531
|
-
},
|
|
6532
|
-
info: () => {
|
|
6533
|
-
},
|
|
6534
|
-
warn: () => {
|
|
6535
|
-
},
|
|
6536
|
-
error: () => {
|
|
6537
|
-
}
|
|
6538
|
-
};
|
|
6539
6904
|
async function enrichWithCodacyYaml(config, repositoryRoot, adapters) {
|
|
6540
6905
|
const yamlConfig = await readCodacyYaml(repositoryRoot);
|
|
6541
6906
|
if (!yamlConfig) return { config, codacyYamlApplied: false };
|
|
@@ -6714,7 +7079,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
6714
7079
|
};
|
|
6715
7080
|
}
|
|
6716
7081
|
async function initContainerConfig(repositoryRoot, adapters) {
|
|
6717
|
-
const codacyrcPath =
|
|
7082
|
+
const codacyrcPath = import_path8.default.join(repositoryRoot, ".codacyrc");
|
|
6718
7083
|
let raw;
|
|
6719
7084
|
try {
|
|
6720
7085
|
raw = await import_promises4.default.readFile(codacyrcPath, "utf-8");
|
|
@@ -7045,110 +7410,68 @@ function parseAutoFilters(raw) {
|
|
|
7045
7410
|
return { filters: { severities, categories }, unknown };
|
|
7046
7411
|
}
|
|
7047
7412
|
|
|
7048
|
-
// src/
|
|
7049
|
-
var
|
|
7050
|
-
var
|
|
7051
|
-
var
|
|
7052
|
-
var
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7413
|
+
// src/credentials.ts
|
|
7414
|
+
var import_crypto = __toESM(require("crypto"));
|
|
7415
|
+
var import_promises5 = __toESM(require("fs/promises"));
|
|
7416
|
+
var import_os3 = __toESM(require("os"));
|
|
7417
|
+
var import_path9 = __toESM(require("path"));
|
|
7418
|
+
function credentialsPath() {
|
|
7419
|
+
return import_path9.default.join(import_os3.default.homedir(), ".codacy", "credentials");
|
|
7420
|
+
}
|
|
7421
|
+
function deriveKey(salt) {
|
|
7422
|
+
const info = import_os3.default.userInfo();
|
|
7423
|
+
const machineKey = [import_os3.default.hostname(), info.username, import_os3.default.homedir(), import_os3.default.platform()].join("|");
|
|
7424
|
+
return import_crypto.default.pbkdf2Sync(machineKey, salt, 1e5, 32, "sha512");
|
|
7425
|
+
}
|
|
7426
|
+
async function saveCredentials(token) {
|
|
7427
|
+
const credentialsFile = credentialsPath();
|
|
7428
|
+
const salt = import_crypto.default.randomBytes(32);
|
|
7429
|
+
const iv = import_crypto.default.randomBytes(16);
|
|
7430
|
+
const key = deriveKey(salt);
|
|
7431
|
+
const cipher = import_crypto.default.createCipheriv("aes-256-gcm", key, iv);
|
|
7432
|
+
const encrypted = Buffer.concat([cipher.update(token, "utf-8"), cipher.final()]);
|
|
7433
|
+
const authTag = cipher.getAuthTag();
|
|
7434
|
+
const payload = JSON.stringify({
|
|
7435
|
+
salt: salt.toString("hex"),
|
|
7436
|
+
iv: iv.toString("hex"),
|
|
7437
|
+
authTag: authTag.toString("hex"),
|
|
7438
|
+
encrypted: encrypted.toString("hex")
|
|
7439
|
+
});
|
|
7440
|
+
await import_promises5.default.mkdir(import_path9.default.dirname(credentialsFile), { recursive: true });
|
|
7441
|
+
await import_promises5.default.writeFile(credentialsFile, payload, { encoding: "utf-8", mode: 384 });
|
|
7442
|
+
}
|
|
7443
|
+
async function loadCredentials() {
|
|
7060
7444
|
try {
|
|
7061
|
-
|
|
7445
|
+
const raw = await import_promises5.default.readFile(credentialsPath(), "utf-8");
|
|
7446
|
+
const data = JSON.parse(raw);
|
|
7447
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return null;
|
|
7448
|
+
const salt = Buffer.from(data.salt, "hex");
|
|
7449
|
+
const iv = Buffer.from(data.iv, "hex");
|
|
7450
|
+
const authTag = Buffer.from(data.authTag, "hex");
|
|
7451
|
+
const encrypted = Buffer.from(data.encrypted, "hex");
|
|
7452
|
+
const key = deriveKey(salt);
|
|
7453
|
+
const decipher = import_crypto.default.createDecipheriv("aes-256-gcm", key, iv);
|
|
7454
|
+
decipher.setAuthTag(authTag);
|
|
7455
|
+
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
7456
|
+
return decrypted.toString("utf-8");
|
|
7062
7457
|
} catch {
|
|
7063
|
-
return;
|
|
7064
|
-
}
|
|
7065
|
-
if (entries2.length <= maxFiles) return;
|
|
7066
|
-
entries2.sort();
|
|
7067
|
-
const toRemove = entries2.slice(0, entries2.length - maxFiles);
|
|
7068
|
-
for (const f of toRemove) {
|
|
7069
|
-
try {
|
|
7070
|
-
import_fs2.default.unlinkSync(import_path8.default.join(logsDir, f));
|
|
7071
|
-
} catch {
|
|
7072
|
-
}
|
|
7458
|
+
return null;
|
|
7073
7459
|
}
|
|
7074
7460
|
}
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
const parts = [];
|
|
7082
|
-
if (fields.toolId) parts.push(String(fields.toolId));
|
|
7083
|
-
if (fields.version) parts.push(`v${fields.version}`);
|
|
7084
|
-
if (fields.installation) parts.push(String(fields.installation));
|
|
7085
|
-
if (fields.configSource) parts.push(String(fields.configSource));
|
|
7086
|
-
if (typeof fields.fileCount === "number") parts.push(`${fields.fileCount} files`);
|
|
7087
|
-
if (typeof fields.issueCount === "number") parts.push(`${fields.issueCount} issues`);
|
|
7088
|
-
if (typeof fields.errorCount === "number" && fields.errorCount > 0)
|
|
7089
|
-
parts.push(`${fields.errorCount} errors`);
|
|
7090
|
-
if (typeof fields.totalIssues === "number") parts.push(`${fields.totalIssues} issues`);
|
|
7091
|
-
if (typeof fields.totalErrors === "number" && fields.totalErrors > 0)
|
|
7092
|
-
parts.push(`${fields.totalErrors} errors`);
|
|
7093
|
-
if (typeof fields.durationMs === "number")
|
|
7094
|
-
parts.push(`${(fields.durationMs / 1e3).toFixed(1)}s`);
|
|
7095
|
-
if (fields.reason) parts.push(String(fields.reason));
|
|
7096
|
-
if (fields.error) parts.push(String(fields.error));
|
|
7097
|
-
if (typeof fields.found === "boolean") parts.push(fields.found ? "found" : "not found");
|
|
7098
|
-
if (fields.path && !fields.version) parts.push(String(fields.path));
|
|
7099
|
-
if (parts.length === 0) return message;
|
|
7100
|
-
return `${message} (${parts.join(", ")})`;
|
|
7101
|
-
}
|
|
7102
|
-
function createLogger(options = {}) {
|
|
7103
|
-
const minLevel = LEVEL_ORDER[options.level ?? "info"];
|
|
7104
|
-
const fileEnabled = options.fileEnabled ?? true;
|
|
7105
|
-
const logsDir = options.logsDir ?? import_path8.default.join(import_os2.default.homedir(), ".codacy", "logs");
|
|
7106
|
-
const maxFileSize = options.maxFileSize ?? 10 * 1024 * 1024;
|
|
7107
|
-
const maxFiles = options.maxFiles ?? 5;
|
|
7108
|
-
const onStderrMessage = options.onStderrMessage;
|
|
7109
|
-
let logFd = null;
|
|
7110
|
-
let bytesWritten = 0;
|
|
7111
|
-
if (fileEnabled) {
|
|
7112
|
-
import_fs2.default.mkdirSync(logsDir, { recursive: true });
|
|
7113
|
-
rotateLogFiles(logsDir, maxFiles);
|
|
7114
|
-
const logFilePath = import_path8.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
|
|
7115
|
-
logFd = import_fs2.default.openSync(logFilePath, "a");
|
|
7116
|
-
}
|
|
7117
|
-
function log(level, message, fields) {
|
|
7118
|
-
const numLevel = LEVEL_ORDER[level];
|
|
7119
|
-
if (numLevel < minLevel) return;
|
|
7120
|
-
if (fileEnabled && logFd !== null) {
|
|
7121
|
-
const entry = JSON.stringify({
|
|
7122
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7123
|
-
level,
|
|
7124
|
-
message,
|
|
7125
|
-
...fields
|
|
7126
|
-
});
|
|
7127
|
-
const buf = Buffer.from(entry + "\n", "utf-8");
|
|
7128
|
-
import_fs2.default.writeSync(logFd, buf);
|
|
7129
|
-
bytesWritten += buf.length;
|
|
7130
|
-
if (bytesWritten >= maxFileSize) {
|
|
7131
|
-
import_fs2.default.closeSync(logFd);
|
|
7132
|
-
rotateLogFiles(logsDir, maxFiles);
|
|
7133
|
-
const newPath = import_path8.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
|
|
7134
|
-
logFd = import_fs2.default.openSync(newPath, "a");
|
|
7135
|
-
bytesWritten = 0;
|
|
7136
|
-
}
|
|
7137
|
-
}
|
|
7138
|
-
const enriched = enrichMessage(message, fields);
|
|
7139
|
-
if (onStderrMessage) {
|
|
7140
|
-
onStderrMessage(enriched, level);
|
|
7141
|
-
} else {
|
|
7142
|
-
process.stderr.write(` ${enriched}
|
|
7143
|
-
`);
|
|
7144
|
-
}
|
|
7461
|
+
async function deleteCredentials() {
|
|
7462
|
+
try {
|
|
7463
|
+
await import_promises5.default.unlink(credentialsPath());
|
|
7464
|
+
return true;
|
|
7465
|
+
} catch {
|
|
7466
|
+
return false;
|
|
7145
7467
|
}
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7468
|
+
}
|
|
7469
|
+
async function resolveApiToken() {
|
|
7470
|
+
const envToken = process.env.CODACY_API_TOKEN;
|
|
7471
|
+
if (envToken) return envToken;
|
|
7472
|
+
const stored = await loadCredentials();
|
|
7473
|
+
if (stored) return stored;
|
|
7474
|
+
throw new Error("No API token found. Set CODACY_API_TOKEN or run 'codacy-analysis login'.");
|
|
7152
7475
|
}
|
|
7153
7476
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7154
7477
|
0 && (module.exports = {
|
|
@@ -7161,12 +7484,15 @@ function createLogger(options = {}) {
|
|
|
7161
7484
|
configureApiToken,
|
|
7162
7485
|
createCodacyConfig,
|
|
7163
7486
|
createLogger,
|
|
7487
|
+
createNullLogger,
|
|
7488
|
+
deleteCredentials,
|
|
7164
7489
|
detectLanguages,
|
|
7165
7490
|
diffConfigs,
|
|
7166
7491
|
discover,
|
|
7167
7492
|
formatOutput,
|
|
7168
7493
|
formatTextSummary,
|
|
7169
7494
|
getAdapter,
|
|
7495
|
+
getKnownDisplayNames,
|
|
7170
7496
|
getRegisteredAdapters,
|
|
7171
7497
|
getRegisteredDescriptors,
|
|
7172
7498
|
initAutoConfig,
|
|
@@ -7179,6 +7505,7 @@ function createLogger(options = {}) {
|
|
|
7179
7505
|
listIgnoredFiles,
|
|
7180
7506
|
listPrFiles,
|
|
7181
7507
|
listStagedFiles,
|
|
7508
|
+
loadCredentials,
|
|
7182
7509
|
mergeConfigs,
|
|
7183
7510
|
parseAutoFilters,
|
|
7184
7511
|
readCodacyConfig,
|
|
@@ -7186,9 +7513,11 @@ function createLogger(options = {}) {
|
|
|
7186
7513
|
registerAdapter,
|
|
7187
7514
|
registerLazyAdapter,
|
|
7188
7515
|
registerUnsupportedTool,
|
|
7516
|
+
resolveApiToken,
|
|
7189
7517
|
resolveConfigPath,
|
|
7190
7518
|
resolveEngineToAdapter,
|
|
7191
7519
|
rotateLogFiles,
|
|
7520
|
+
saveCredentials,
|
|
7192
7521
|
validateApiToken,
|
|
7193
7522
|
writeCodacyConfig,
|
|
7194
7523
|
writeCodacyConfigFile
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codacy/analysis-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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.13.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.13.0"
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"author": "Codacy <support@codacy.com> (https://www.codacy.com)",
|