@codacy/analysis-runner 0.10.1 → 0.11.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 +81 -54
- package/dist/index.js +532 -162
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OutputFormat, Logger, AnalysisResult, ToolAdapter, AdapterDescriptor, CodacyConfig, ConfigurationFileCheckResult, CodacyToolConfig, IssueSeverity, IssueCategory, Language, DiscoveryResult, Issue, PatternSummary, LogLevel } from '@codacy/tooling';
|
|
1
|
+
import { OutputFormat, Logger, AnalysisResult, ToolAdapter, AdapterDescriptor, CodacyConfig, ConfigurationFileCheckResult, CodacyToolConfig, IssueSeverity, IssueCategory, Language, DiscoveryResult, PatternEntry, Issue, PatternSummary, LogLevel } from '@codacy/tooling';
|
|
2
2
|
export { AnalysisResult, OutputFormat } from '@codacy/tooling';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -94,6 +94,17 @@ declare function registerAdapter(adapter: Readonly<ToolAdapter>): void;
|
|
|
94
94
|
* and inspect-mode metadata display without loading the full module.
|
|
95
95
|
*/
|
|
96
96
|
declare function registerLazyAdapter(descriptor: AdapterDescriptor, factory: AdapterFactory): void;
|
|
97
|
+
/**
|
|
98
|
+
* Register an unsupported (cloud-only) tool by descriptor alone.
|
|
99
|
+
*
|
|
100
|
+
* These tools exist on Codacy Cloud but have no local analyzer in this CLI. They are
|
|
101
|
+
* registered so that `getRegisteredDescriptors()` (and therefore `info`, `init --auto`,
|
|
102
|
+
* and config generation) can see them, but they are intentionally excluded from
|
|
103
|
+
* `getRegisteredAdapters()`/`getAdapter()` since there is nothing to execute.
|
|
104
|
+
*
|
|
105
|
+
* The descriptor should have `unsupported: true`.
|
|
106
|
+
*/
|
|
107
|
+
declare function registerUnsupportedTool(descriptor: AdapterDescriptor): void;
|
|
97
108
|
/**
|
|
98
109
|
* Returns all registered adapters, optionally filtered by tool ID list.
|
|
99
110
|
*
|
|
@@ -309,6 +320,53 @@ declare function parseAutoFilters(raw: string): {
|
|
|
309
320
|
unknown: string[];
|
|
310
321
|
};
|
|
311
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Codacy API client — thin wrappers around the generated OpenAPI client.
|
|
325
|
+
*
|
|
326
|
+
* The generated client (src/api/client/) is produced from the Codacy OpenAPI spec
|
|
327
|
+
* and should never be edited by hand. All API interactions go through this file.
|
|
328
|
+
*
|
|
329
|
+
* Authentication: Codacy uses an `api-token` header (not Bearer). We set it via
|
|
330
|
+
* `OpenAPI.HEADERS` before each call group. `configureApiToken()` must be called
|
|
331
|
+
* before any authenticated endpoint is used.
|
|
332
|
+
*
|
|
333
|
+
* Pagination: all list endpoints use cursor-based pagination. `fetchAllPages()`
|
|
334
|
+
* follows cursors until the response contains no next cursor.
|
|
335
|
+
*/
|
|
336
|
+
|
|
337
|
+
/** Result of fetching cloud-ignored files, with pagination cap. */
|
|
338
|
+
interface IgnoredFilesResult {
|
|
339
|
+
/** File paths ignored in Codacy Cloud. */
|
|
340
|
+
files: string[];
|
|
341
|
+
/** Total number of files fetched (same as files.length). */
|
|
342
|
+
totalFetched: number;
|
|
343
|
+
/** Whether the fetch was stopped because it hit the 1000-file cap. */
|
|
344
|
+
capped: boolean;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Configures the API token used for authenticated requests.
|
|
348
|
+
* Must be called before `getRepository`, `listRepositoryTools`, or
|
|
349
|
+
* `listRepositoryToolPatterns`. Public endpoints (listTools, listPatterns)
|
|
350
|
+
* do not require a token.
|
|
351
|
+
*/
|
|
352
|
+
declare function configureApiToken(token: string): void;
|
|
353
|
+
/**
|
|
354
|
+
* Validates an API token by calling the authenticated user endpoint.
|
|
355
|
+
* Returns user info on success; caller should catch errors for invalid tokens.
|
|
356
|
+
*/
|
|
357
|
+
declare function validateApiToken(token: string): Promise<{
|
|
358
|
+
valid: boolean;
|
|
359
|
+
name?: string;
|
|
360
|
+
email?: string;
|
|
361
|
+
}>;
|
|
362
|
+
/**
|
|
363
|
+
* Fetches files individually ignored in Codacy Cloud for a repository.
|
|
364
|
+
* Uses cursor-based pagination capped at 10 pages (1000 files max) to avoid
|
|
365
|
+
* unbounded fetching for repositories with very large ignore lists.
|
|
366
|
+
* Requires `configureApiToken()` to have been called.
|
|
367
|
+
*/
|
|
368
|
+
declare function listIgnoredFiles(provider: string, org: string, repo: string): Promise<IgnoredFilesResult>;
|
|
369
|
+
|
|
312
370
|
/**
|
|
313
371
|
* Init mode logic — builds a CodacyConfig from five distinct sources:
|
|
314
372
|
*
|
|
@@ -348,8 +406,10 @@ declare function initLocalConfig(repositoryRoot: string, adapters: Readonly<Tool
|
|
|
348
406
|
interface InitDefaultStats {
|
|
349
407
|
/** Total number of tools listed by the Codacy API. */
|
|
350
408
|
totalApiTools: number;
|
|
351
|
-
/** Number of tool entries added
|
|
409
|
+
/** Number of supported (local-adapter) tool entries added from API defaults. */
|
|
352
410
|
addedTools: number;
|
|
411
|
+
/** Number of unsupported (cloud-only) tool entries added from API defaults. */
|
|
412
|
+
addedUnsupported: number;
|
|
353
413
|
/** Whether a .codacy.yaml file was found and its exclude paths were applied. */
|
|
354
414
|
codacyYamlApplied: boolean;
|
|
355
415
|
}
|
|
@@ -373,6 +433,8 @@ interface InitRemoteStats {
|
|
|
373
433
|
totalRemoteTools: number;
|
|
374
434
|
/** Number of those tools that have a registered local adapter. */
|
|
375
435
|
matchedAdapters: number;
|
|
436
|
+
/** Number of those tools included as unsupported (cloud-only) entries. */
|
|
437
|
+
unsupportedTools: number;
|
|
376
438
|
/** Whether a .codacy.yaml file was found and its exclude paths were applied. */
|
|
377
439
|
codacyYamlApplied: boolean;
|
|
378
440
|
/** Number of cloud-ignored files fetched from the Codacy API. */
|
|
@@ -383,8 +445,10 @@ interface InitRemoteStats {
|
|
|
383
445
|
/**
|
|
384
446
|
* `--remote <provider> <org> <repo>` mode.
|
|
385
447
|
* Fetches the full tool/pattern configuration from the repository's Codacy settings.
|
|
386
|
-
* The remote config is treated as the source of truth
|
|
387
|
-
* adapter are
|
|
448
|
+
* The remote config is treated as the source of truth: tools with a registered adapter
|
|
449
|
+
* are included as supported tools, and enabled tools without a local adapter are tracked
|
|
450
|
+
* as unsupported (cloud-only) entries (subject to the trackability filter — deprecated and
|
|
451
|
+
* client-side/standalone-only tools are skipped).
|
|
388
452
|
*
|
|
389
453
|
* @param onRemoteToolsFound - Optional callback invoked once after the list of enabled
|
|
390
454
|
* remote tools is fetched. Receives the total count of enabled tools and the count
|
|
@@ -418,8 +482,10 @@ interface InitAutoStats {
|
|
|
418
482
|
detectedFrameworks: number;
|
|
419
483
|
/** Number of libraries detected by discover. */
|
|
420
484
|
detectedLibraries: number;
|
|
421
|
-
/** Number of tool entries added to the config. */
|
|
485
|
+
/** Number of tool entries added to the config (supported + unsupported). */
|
|
422
486
|
selectedTools: number;
|
|
487
|
+
/** Number of those entries that are unsupported (cloud-only) tools. */
|
|
488
|
+
selectedUnsupported: number;
|
|
423
489
|
/** Whether a .codacy.yaml file was found and its exclude paths were applied. */
|
|
424
490
|
codacyYamlApplied: boolean;
|
|
425
491
|
}
|
|
@@ -440,8 +506,16 @@ interface InitAutoStats {
|
|
|
440
506
|
* ESLint core rules and their `@typescript-eslint` equivalents may both be
|
|
441
507
|
* enabled, potentially causing duplicate findings. This will be addressed in v2
|
|
442
508
|
* via conflict-resolution metadata in the mapping files.
|
|
509
|
+
*
|
|
510
|
+
* Unsupported (cloud-only) tools — descriptors with `unsupported: true` — are
|
|
511
|
+
* also filled when their languages overlap the detected stack. Their patterns are
|
|
512
|
+
* loaded lazily via `loadUnsupportedPatterns`; when it is not provided they are
|
|
513
|
+
* skipped. Tools that ship framework-specific patterns (e.g. PHPCS, dartanalyzer,
|
|
514
|
+
* SonarCSharp, CodeNarc) carry curated `frameworkMappings` on their descriptor, so
|
|
515
|
+
* those patterns are gated on detected frameworks exactly like supported tools;
|
|
516
|
+
* tools without mappings have every committed pattern treated as universal.
|
|
443
517
|
*/
|
|
444
|
-
declare function initAutoConfig(repositoryRoot: string, adapters: Readonly<ToolAdapter>[], descriptors: AdapterDescriptor[], filters?: AutoFilters, onDiscoveryComplete?: (result: DiscoveryResult) => void, rawFilterString?: string): Promise<{
|
|
518
|
+
declare function initAutoConfig(repositoryRoot: string, adapters: Readonly<ToolAdapter>[], descriptors: AdapterDescriptor[], filters?: AutoFilters, onDiscoveryComplete?: (result: DiscoveryResult) => void, rawFilterString?: string, loadUnsupportedPatterns?: (toolId: string) => readonly PatternEntry[]): Promise<{
|
|
445
519
|
config: CodacyConfig;
|
|
446
520
|
stats: InitAutoStats;
|
|
447
521
|
}>;
|
|
@@ -552,53 +626,6 @@ declare function listDiffFiles(repositoryRoot: string, base?: string): string[];
|
|
|
552
626
|
*/
|
|
553
627
|
declare function listPrFiles(repositoryRoot: string): string[];
|
|
554
628
|
|
|
555
|
-
/**
|
|
556
|
-
* Codacy API client — thin wrappers around the generated OpenAPI client.
|
|
557
|
-
*
|
|
558
|
-
* The generated client (src/api/client/) is produced from the Codacy OpenAPI spec
|
|
559
|
-
* and should never be edited by hand. All API interactions go through this file.
|
|
560
|
-
*
|
|
561
|
-
* Authentication: Codacy uses an `api-token` header (not Bearer). We set it via
|
|
562
|
-
* `OpenAPI.HEADERS` before each call group. `configureApiToken()` must be called
|
|
563
|
-
* before any authenticated endpoint is used.
|
|
564
|
-
*
|
|
565
|
-
* Pagination: all list endpoints use cursor-based pagination. `fetchAllPages()`
|
|
566
|
-
* follows cursors until the response contains no next cursor.
|
|
567
|
-
*/
|
|
568
|
-
|
|
569
|
-
/** Result of fetching cloud-ignored files, with pagination cap. */
|
|
570
|
-
interface IgnoredFilesResult {
|
|
571
|
-
/** File paths ignored in Codacy Cloud. */
|
|
572
|
-
files: string[];
|
|
573
|
-
/** Total number of files fetched (same as files.length). */
|
|
574
|
-
totalFetched: number;
|
|
575
|
-
/** Whether the fetch was stopped because it hit the 1000-file cap. */
|
|
576
|
-
capped: boolean;
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Configures the API token used for authenticated requests.
|
|
580
|
-
* Must be called before `getRepository`, `listRepositoryTools`, or
|
|
581
|
-
* `listRepositoryToolPatterns`. Public endpoints (listTools, listPatterns)
|
|
582
|
-
* do not require a token.
|
|
583
|
-
*/
|
|
584
|
-
declare function configureApiToken(token: string): void;
|
|
585
|
-
/**
|
|
586
|
-
* Validates an API token by calling the authenticated user endpoint.
|
|
587
|
-
* Returns user info on success; caller should catch errors for invalid tokens.
|
|
588
|
-
*/
|
|
589
|
-
declare function validateApiToken(token: string): Promise<{
|
|
590
|
-
valid: boolean;
|
|
591
|
-
name?: string;
|
|
592
|
-
email?: string;
|
|
593
|
-
}>;
|
|
594
|
-
/**
|
|
595
|
-
* Fetches files individually ignored in Codacy Cloud for a repository.
|
|
596
|
-
* Uses cursor-based pagination capped at 10 pages (1000 files max) to avoid
|
|
597
|
-
* unbounded fetching for repositories with very large ignore lists.
|
|
598
|
-
* Requires `configureApiToken()` to have been called.
|
|
599
|
-
*/
|
|
600
|
-
declare function listIgnoredFiles(provider: string, org: string, repo: string): Promise<IgnoredFilesResult>;
|
|
601
|
-
|
|
602
629
|
/**
|
|
603
630
|
* Structured file + stderr logger for the analysis pipeline.
|
|
604
631
|
*
|
|
@@ -673,4 +700,4 @@ interface DiscoverOptions {
|
|
|
673
700
|
*/
|
|
674
701
|
declare function discover(repositoryRoot: string, options?: DiscoverOptions): Promise<DiscoveryResult>;
|
|
675
702
|
|
|
676
|
-
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, resolveConfigPath, resolveEngineToAdapter, rotateLogFiles, validateApiToken, writeCodacyConfig, writeCodacyConfigFile };
|
|
703
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -63,6 +63,7 @@ __export(index_exports, {
|
|
|
63
63
|
readCodacyYaml: () => readCodacyYaml,
|
|
64
64
|
registerAdapter: () => registerAdapter,
|
|
65
65
|
registerLazyAdapter: () => registerLazyAdapter,
|
|
66
|
+
registerUnsupportedTool: () => registerUnsupportedTool,
|
|
66
67
|
resolveConfigPath: () => resolveConfigPath,
|
|
67
68
|
resolveEngineToAdapter: () => resolveEngineToAdapter,
|
|
68
69
|
rotateLogFiles: () => rotateLogFiles,
|
|
@@ -92,6 +93,9 @@ function registerAdapter(adapter) {
|
|
|
92
93
|
function registerLazyAdapter(descriptor, factory) {
|
|
93
94
|
entries.set(descriptor.id, { descriptor, factory });
|
|
94
95
|
}
|
|
96
|
+
function registerUnsupportedTool(descriptor) {
|
|
97
|
+
entries.set(descriptor.id, { descriptor });
|
|
98
|
+
}
|
|
95
99
|
function resolveEntry(input) {
|
|
96
100
|
const exact = entries.get(input);
|
|
97
101
|
if (exact) return exact;
|
|
@@ -103,15 +107,23 @@ function resolveEntry(input) {
|
|
|
103
107
|
}
|
|
104
108
|
async function loadAdapter(entry) {
|
|
105
109
|
if (entry.resolved) return entry.resolved;
|
|
110
|
+
if (!entry.factory) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Tool "${entry.descriptor.id}" is unsupported and has no local adapter to load`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
106
115
|
entry.resolved = await entry.factory();
|
|
107
116
|
return entry.resolved;
|
|
108
117
|
}
|
|
118
|
+
function isLoadable(entry) {
|
|
119
|
+
return entry.factory !== void 0;
|
|
120
|
+
}
|
|
109
121
|
async function getRegisteredAdapters(toolIds) {
|
|
110
122
|
if (toolIds && toolIds.length > 0) {
|
|
111
|
-
const matched = toolIds.map((id) => resolveEntry(id)).filter((e) => e !== void 0);
|
|
123
|
+
const matched = toolIds.map((id) => resolveEntry(id)).filter((e) => e !== void 0).filter(isLoadable);
|
|
112
124
|
return Promise.all(matched.map(loadAdapter));
|
|
113
125
|
}
|
|
114
|
-
return Promise.all(Array.from(entries.values()).map(loadAdapter));
|
|
126
|
+
return Promise.all(Array.from(entries.values()).filter(isLoadable).map(loadAdapter));
|
|
115
127
|
}
|
|
116
128
|
function getRegisteredDescriptors(toolIds) {
|
|
117
129
|
if (toolIds && toolIds.length > 0) {
|
|
@@ -121,7 +133,7 @@ function getRegisteredDescriptors(toolIds) {
|
|
|
121
133
|
}
|
|
122
134
|
async function getAdapter(toolId) {
|
|
123
135
|
const entry = entries.get(toolId);
|
|
124
|
-
if (!entry) return void 0;
|
|
136
|
+
if (!entry || !isLoadable(entry)) return void 0;
|
|
125
137
|
return loadAdapter(entry);
|
|
126
138
|
}
|
|
127
139
|
function clearAdapters() {
|
|
@@ -231,6 +243,88 @@ var import_fs = __toESM(require("fs"));
|
|
|
231
243
|
var import_path2 = __toESM(require("path"));
|
|
232
244
|
var import_minimatch = require("minimatch");
|
|
233
245
|
var MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
|
|
246
|
+
var PURE_EXTENSION_PATTERN = /^\*\*\/\*(\.[^*/.]+)$/;
|
|
247
|
+
function pureExtensionOf(pattern) {
|
|
248
|
+
const m = PURE_EXTENSION_PATTERN.exec(pattern);
|
|
249
|
+
return m ? m[1] : null;
|
|
250
|
+
}
|
|
251
|
+
var CompiledMatcher = class {
|
|
252
|
+
/** True if any pattern is the bare `"*"` wildcard (matches every file). */
|
|
253
|
+
matchAll;
|
|
254
|
+
literalPaths;
|
|
255
|
+
literalNames;
|
|
256
|
+
exts;
|
|
257
|
+
// Residual globs are split by whether they contain a path separator so the
|
|
258
|
+
// hot loop never runs a full-path match for a slash-less pattern (which can
|
|
259
|
+
// only ever match via the basename) — that call is always wasted on nested
|
|
260
|
+
// files. Slash patterns match against the full path; slash-less ones against
|
|
261
|
+
// the basename, which together reproduce the old dual check exactly.
|
|
262
|
+
residualPath;
|
|
263
|
+
residualName;
|
|
264
|
+
constructor(patterns) {
|
|
265
|
+
let matchAll = false;
|
|
266
|
+
const literalPaths = /* @__PURE__ */ new Set();
|
|
267
|
+
const literalNames = /* @__PURE__ */ new Set();
|
|
268
|
+
const exts = /* @__PURE__ */ new Set();
|
|
269
|
+
const residualPath = [];
|
|
270
|
+
const residualName = [];
|
|
271
|
+
for (const pattern of patterns) {
|
|
272
|
+
if (pattern === "*") {
|
|
273
|
+
matchAll = true;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const ext = pureExtensionOf(pattern);
|
|
277
|
+
if (ext !== null) {
|
|
278
|
+
exts.add(ext);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const mm = new import_minimatch.Minimatch(pattern, { dot: true });
|
|
282
|
+
if (mm.hasMagic()) {
|
|
283
|
+
(pattern.includes("/") ? residualPath : residualName).push(mm);
|
|
284
|
+
} else if (pattern.includes("/")) {
|
|
285
|
+
literalPaths.add(pattern);
|
|
286
|
+
} else {
|
|
287
|
+
literalNames.add(pattern);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
this.matchAll = matchAll;
|
|
291
|
+
this.literalPaths = literalPaths;
|
|
292
|
+
this.literalNames = literalNames;
|
|
293
|
+
this.exts = exts;
|
|
294
|
+
this.residualPath = residualPath;
|
|
295
|
+
this.residualName = residualName;
|
|
296
|
+
}
|
|
297
|
+
/** True when this matcher holds no patterns at all (matches nothing). */
|
|
298
|
+
get isEmpty() {
|
|
299
|
+
return !this.matchAll && this.literalPaths.size === 0 && this.literalNames.size === 0 && this.exts.size === 0 && this.residualPath.length === 0 && this.residualName.length === 0;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Returns true if `file` matches any pattern. `basename` must be
|
|
303
|
+
* `path.basename(file)` — passed in so callers compute it once per file.
|
|
304
|
+
*
|
|
305
|
+
* Mirrors the historical dual check: a pattern matches if it matches the full
|
|
306
|
+
* relative path, or (for patterns with no path separator) the basename. For
|
|
307
|
+
* globstar/slash patterns the full-path check already subsumes the basename
|
|
308
|
+
* check, so gating the basename check on `!includes("/")` is equivalent to
|
|
309
|
+
* the old unconditional basename check used by `routeFilesToTool`.
|
|
310
|
+
*/
|
|
311
|
+
matches(file, basename) {
|
|
312
|
+
if (this.matchAll) return true;
|
|
313
|
+
if (this.literalPaths.size > 0 && this.literalPaths.has(file)) return true;
|
|
314
|
+
if (this.literalNames.size > 0 && this.literalNames.has(basename)) return true;
|
|
315
|
+
if (this.exts.size > 0) {
|
|
316
|
+
const dot = basename.lastIndexOf(".");
|
|
317
|
+
if (dot >= 0 && this.exts.has(basename.slice(dot))) return true;
|
|
318
|
+
}
|
|
319
|
+
for (const m of this.residualPath) {
|
|
320
|
+
if (m.match(file)) return true;
|
|
321
|
+
}
|
|
322
|
+
for (const m of this.residualName) {
|
|
323
|
+
if (m.match(basename)) return true;
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
234
328
|
function listRepoFiles(repositoryRoot) {
|
|
235
329
|
try {
|
|
236
330
|
const tracked = (0, import_child_process.execSync)("git ls-files", {
|
|
@@ -280,29 +374,19 @@ function filterOversizedFiles(files, repositoryRoot, logger) {
|
|
|
280
374
|
return result;
|
|
281
375
|
}
|
|
282
376
|
function applyExclusions(files, globalExclude, toolExclude) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return !allExclusions.some((pattern) => {
|
|
288
|
-
if ((0, import_minimatch.minimatch)(file, pattern, { dot: true })) return true;
|
|
289
|
-
if (!pattern.includes("/") && (0, import_minimatch.minimatch)(basename, pattern, { dot: true })) return true;
|
|
290
|
-
return false;
|
|
291
|
-
});
|
|
292
|
-
});
|
|
377
|
+
if (globalExclude.length === 0 && toolExclude.length === 0) return files;
|
|
378
|
+
const matcher = new CompiledMatcher([...globalExclude, ...toolExclude]);
|
|
379
|
+
if (matcher.isEmpty) return files;
|
|
380
|
+
return files.filter((file) => !matcher.matches(file, import_path2.default.basename(file)));
|
|
293
381
|
}
|
|
294
382
|
function routeFilesToTool(files, adapter, toolConfig) {
|
|
295
383
|
const patterns = [...adapter.filePatterns];
|
|
296
384
|
if (toolConfig?.extraFilePatterns) {
|
|
297
385
|
patterns.push(...toolConfig.extraFilePatterns);
|
|
298
386
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return patterns.some(
|
|
303
|
-
(pattern) => (0, import_minimatch.minimatch)(basename, pattern, { dot: true }) || (0, import_minimatch.minimatch)(file, pattern, { dot: true })
|
|
304
|
-
);
|
|
305
|
-
});
|
|
387
|
+
const matcher = new CompiledMatcher(patterns);
|
|
388
|
+
if (matcher.matchAll) return files;
|
|
389
|
+
return files.filter((file) => matcher.matches(file, import_path2.default.basename(file)));
|
|
306
390
|
}
|
|
307
391
|
function listStagedFiles(repositoryRoot) {
|
|
308
392
|
try {
|
|
@@ -392,14 +476,15 @@ function detectDefaultBranch(repositoryRoot) {
|
|
|
392
476
|
function filterByTargetFiles(allFiles, targetPatterns) {
|
|
393
477
|
if (targetPatterns.length === 0) return allFiles;
|
|
394
478
|
const normalized = targetPatterns.map((p) => p.replace(/^\.\//, ""));
|
|
479
|
+
const exact = new Set(normalized);
|
|
480
|
+
const globs = normalized.map((p) => new import_minimatch.Minimatch(p, { dot: true, matchBase: false })).filter((m) => m.hasMagic());
|
|
481
|
+
const dirPrefixes = normalized.filter((p) => p.endsWith("/"));
|
|
395
482
|
return allFiles.filter(
|
|
396
|
-
(f) =>
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
pattern.endsWith("/") && f.startsWith(pattern)
|
|
402
|
-
)
|
|
483
|
+
(f) => (
|
|
484
|
+
// Exact match (fast path)
|
|
485
|
+
exact.has(f) || // Glob match
|
|
486
|
+
globs.some((m) => m.match(f)) || // Directory prefix (e.g. "src/subdir/" matches all files under it)
|
|
487
|
+
dirPrefixes.some((p) => f.startsWith(p))
|
|
403
488
|
)
|
|
404
489
|
);
|
|
405
490
|
}
|
|
@@ -634,6 +719,20 @@ var nullLogger = {
|
|
|
634
719
|
error: () => {
|
|
635
720
|
}
|
|
636
721
|
};
|
|
722
|
+
var CHECK_CONCURRENCY = 12;
|
|
723
|
+
async function mapWithConcurrency(items, concurrency, task) {
|
|
724
|
+
const results = new Array(items.length);
|
|
725
|
+
let next = 0;
|
|
726
|
+
async function worker() {
|
|
727
|
+
while (next < items.length) {
|
|
728
|
+
const index = next++;
|
|
729
|
+
results[index] = await task(items[index], index);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());
|
|
733
|
+
await Promise.all(workers);
|
|
734
|
+
return results;
|
|
735
|
+
}
|
|
637
736
|
async function analyze(options) {
|
|
638
737
|
const startedAt = /* @__PURE__ */ new Date();
|
|
639
738
|
const mode = options.mode || "standard";
|
|
@@ -727,23 +826,25 @@ async function analyze(options) {
|
|
|
727
826
|
const unavailable = [];
|
|
728
827
|
const availabilityTimeout = 6e4;
|
|
729
828
|
const availabilityStartMs = Date.now();
|
|
730
|
-
|
|
829
|
+
const checkAvailabilityOf = async (adapter) => {
|
|
731
830
|
const ctx = makeToolContext(baseCtx, adapter, []);
|
|
732
831
|
const checkStart = Date.now();
|
|
733
832
|
logger.debug("Checking availability", { toolId: adapter.id });
|
|
833
|
+
let timer;
|
|
734
834
|
try {
|
|
735
835
|
const availability = await Promise.race([
|
|
736
836
|
adapter.checkAvailability(ctx),
|
|
737
|
-
new Promise(
|
|
738
|
-
|
|
837
|
+
new Promise((_, reject) => {
|
|
838
|
+
timer = setTimeout(
|
|
739
839
|
() => reject(
|
|
740
840
|
new Error(
|
|
741
841
|
`checkAvailability timed out after ${(0, import_tooling3.formatDuration)(availabilityTimeout)}`
|
|
742
842
|
)
|
|
743
843
|
),
|
|
744
844
|
availabilityTimeout
|
|
745
|
-
)
|
|
746
|
-
|
|
845
|
+
);
|
|
846
|
+
timer.unref?.();
|
|
847
|
+
})
|
|
747
848
|
]);
|
|
748
849
|
if (availability.available) {
|
|
749
850
|
logger.debug("Tool available", {
|
|
@@ -752,38 +853,57 @@ async function analyze(options) {
|
|
|
752
853
|
installation: availability.installation,
|
|
753
854
|
durationMs: Date.now() - checkStart
|
|
754
855
|
});
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
856
|
+
return {
|
|
857
|
+
kind: "ready",
|
|
858
|
+
status: {
|
|
859
|
+
toolId: adapter.id,
|
|
860
|
+
displayName: adapter.displayName,
|
|
861
|
+
version: availability.version ?? adapter.preferredVersion,
|
|
862
|
+
installation: availability.installation ?? "global",
|
|
863
|
+
runner: "local"
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
logger.warn("Tool unavailable", {
|
|
868
|
+
toolId: adapter.id,
|
|
869
|
+
reason: availability.reason,
|
|
870
|
+
durationMs: Date.now() - checkStart
|
|
871
|
+
});
|
|
872
|
+
return {
|
|
873
|
+
kind: "unavailable",
|
|
874
|
+
status: {
|
|
769
875
|
toolId: adapter.id,
|
|
770
876
|
displayName: adapter.displayName,
|
|
771
877
|
reason: availability.reason || "Tool is not available",
|
|
772
878
|
...availability.installHint ? { remediation: availability.installHint } : {}
|
|
773
|
-
}
|
|
774
|
-
}
|
|
879
|
+
}
|
|
880
|
+
};
|
|
775
881
|
} catch (err) {
|
|
776
882
|
logger.warn("Availability check failed", {
|
|
777
883
|
toolId: adapter.id,
|
|
778
884
|
error: err instanceof Error ? err.message : String(err),
|
|
779
885
|
durationMs: Date.now() - checkStart
|
|
780
886
|
});
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
887
|
+
return {
|
|
888
|
+
kind: "unavailable",
|
|
889
|
+
status: {
|
|
890
|
+
toolId: adapter.id,
|
|
891
|
+
displayName: adapter.displayName,
|
|
892
|
+
reason: `Availability check failed: ${err instanceof Error ? err.message : String(err)}`
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
} finally {
|
|
896
|
+
if (timer) clearTimeout(timer);
|
|
786
897
|
}
|
|
898
|
+
};
|
|
899
|
+
const availabilityOutcomes = await mapWithConcurrency(
|
|
900
|
+
adapters,
|
|
901
|
+
CHECK_CONCURRENCY,
|
|
902
|
+
checkAvailabilityOf
|
|
903
|
+
);
|
|
904
|
+
for (const outcome of availabilityOutcomes) {
|
|
905
|
+
if (outcome.kind === "ready") ready.push(outcome.status);
|
|
906
|
+
else unavailable.push(outcome.status);
|
|
787
907
|
}
|
|
788
908
|
phaseTimings.availabilityMs = Date.now() - availabilityStartMs;
|
|
789
909
|
const capability = {
|
|
@@ -800,6 +920,31 @@ async function analyze(options) {
|
|
|
800
920
|
readyAfter: capability.ready.length,
|
|
801
921
|
configuredToolCount: configuredIds.size
|
|
802
922
|
});
|
|
923
|
+
const representedIds = /* @__PURE__ */ new Set([
|
|
924
|
+
...capability.ready.map((t) => t.toolId),
|
|
925
|
+
...capability.unavailable.map((t) => t.toolId)
|
|
926
|
+
]);
|
|
927
|
+
const requestedLower = options.tools?.map((t) => t.toLowerCase());
|
|
928
|
+
const inScope = (toolId) => {
|
|
929
|
+
if (!requestedLower) return true;
|
|
930
|
+
const id = toolId.toLowerCase();
|
|
931
|
+
return requestedLower.some((r) => id.includes(r) || r.includes(id));
|
|
932
|
+
};
|
|
933
|
+
const descriptorsById = new Map(getRegisteredDescriptors().map((d) => [d.id, d]));
|
|
934
|
+
for (const toolConfig of codacyConfig.tools) {
|
|
935
|
+
if (!toolConfig?.toolId) continue;
|
|
936
|
+
if (representedIds.has(toolConfig.toolId)) continue;
|
|
937
|
+
if (!inScope(toolConfig.toolId)) continue;
|
|
938
|
+
const descriptor = descriptorsById.get(toolConfig.toolId);
|
|
939
|
+
if (!descriptor?.unsupported) continue;
|
|
940
|
+
capability.unavailable.push({
|
|
941
|
+
toolId: toolConfig.toolId,
|
|
942
|
+
displayName: descriptor.displayName ?? toolConfig.toolId,
|
|
943
|
+
reason: "No local analyzer (configured on Codacy Cloud)",
|
|
944
|
+
availableVia: "cloud"
|
|
945
|
+
});
|
|
946
|
+
representedIds.add(toolConfig.toolId);
|
|
947
|
+
}
|
|
803
948
|
if (mode === "auto-install" && capability.unavailable.length > 0) {
|
|
804
949
|
const installStartMs = Date.now();
|
|
805
950
|
const installErrors = [];
|
|
@@ -908,8 +1053,9 @@ async function analyze(options) {
|
|
|
908
1053
|
}
|
|
909
1054
|
return buildResult(startedAt, options.repositoryRoot, mode, capability, [], [], []);
|
|
910
1055
|
}
|
|
911
|
-
|
|
912
|
-
|
|
1056
|
+
const strictMissing = capability.unavailable.filter((u) => u.availableVia !== "cloud");
|
|
1057
|
+
if (mode === "strict" && strictMissing.length > 0) {
|
|
1058
|
+
const missingTools = strictMissing.map((u) => u.toolId).join(", ");
|
|
913
1059
|
return buildResult(
|
|
914
1060
|
startedAt,
|
|
915
1061
|
options.repositoryRoot,
|
|
@@ -953,22 +1099,47 @@ async function analyze(options) {
|
|
|
953
1099
|
allFiles = filterOversizedFiles(allFiles, options.repositoryRoot, logger);
|
|
954
1100
|
phaseTimings.fileDiscoveryMs = Date.now() - fileDiscoveryStartMs;
|
|
955
1101
|
const fileRoutingStartMs = Date.now();
|
|
956
|
-
const
|
|
1102
|
+
const readyById = new Map(capability.ready.map((r) => [r.toolId, r]));
|
|
1103
|
+
const readyAdapters = adapters.filter((a) => readyById.has(a.id));
|
|
1104
|
+
const filesAfterGlobalExclude = applyExclusions(allFiles, globalExclude, []);
|
|
957
1105
|
const toolRuns = [];
|
|
1106
|
+
const candidates = [];
|
|
958
1107
|
for (const adapter of readyAdapters) {
|
|
959
1108
|
const toolConfig = getToolConfig(codacyConfig, adapter.id);
|
|
960
1109
|
if (!toolConfig) continue;
|
|
961
|
-
const toolExclude = toolConfig
|
|
962
|
-
const
|
|
963
|
-
const toolFiles = routeFilesToTool(
|
|
964
|
-
const
|
|
965
|
-
if (
|
|
966
|
-
|
|
1110
|
+
const toolExclude = toolConfig.exclude || [];
|
|
1111
|
+
const filesForTool = toolExclude.length > 0 ? applyExclusions(filesAfterGlobalExclude, toolExclude, []) : filesAfterGlobalExclude;
|
|
1112
|
+
const toolFiles = routeFilesToTool(filesForTool, adapter, toolConfig);
|
|
1113
|
+
const readyEntry = readyById.get(adapter.id);
|
|
1114
|
+
if (readyEntry) {
|
|
1115
|
+
readyEntry.filesRouted = toolFiles.length;
|
|
967
1116
|
}
|
|
968
|
-
|
|
969
|
-
|
|
1117
|
+
candidates.push({
|
|
1118
|
+
adapter,
|
|
1119
|
+
toolFiles,
|
|
1120
|
+
configCtx: makeToolContext(baseCtx, adapter, toolFiles),
|
|
1121
|
+
readyEntry
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
const configChecks = await mapWithConcurrency(
|
|
1125
|
+
candidates,
|
|
1126
|
+
CHECK_CONCURRENCY,
|
|
1127
|
+
async (c) => {
|
|
1128
|
+
try {
|
|
1129
|
+
return await c.adapter.checkLocalConfigurationFile(c.configCtx);
|
|
1130
|
+
} catch (err) {
|
|
1131
|
+
logger.debug("Local config check failed, assuming none", {
|
|
1132
|
+
toolId: c.adapter.id,
|
|
1133
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1134
|
+
});
|
|
1135
|
+
return { found: false };
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
);
|
|
1139
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
1140
|
+
const { adapter, toolFiles, configCtx, readyEntry } = candidates[i];
|
|
1141
|
+
const configCheck = configChecks[i];
|
|
970
1142
|
const codacyToolConfig = buildToolConfig(codacyConfig, adapter, configCheck);
|
|
971
|
-
const readyEntry = capability.ready.find((r) => r.toolId === adapter.id);
|
|
972
1143
|
if (readyEntry) {
|
|
973
1144
|
if (configCheck.found && codacyToolConfig.useLocalConfigurationFile) {
|
|
974
1145
|
readyEntry.configSource = "local";
|
|
@@ -1301,13 +1472,14 @@ function formatTextSummary(result, isInspect) {
|
|
|
1301
1472
|
]);
|
|
1302
1473
|
}
|
|
1303
1474
|
for (const tool of unavailableTools) {
|
|
1475
|
+
const isUnsupported = tool.availableVia === "cloud";
|
|
1304
1476
|
table.push([
|
|
1305
|
-
import_ansis.default.dim.red(tool.displayName ?? tool.toolId),
|
|
1306
|
-
"-",
|
|
1477
|
+
isUnsupported ? import_ansis.default.dim(tool.displayName ?? tool.toolId) : import_ansis.default.dim.red(tool.displayName ?? tool.toolId),
|
|
1307
1478
|
"-",
|
|
1479
|
+
isUnsupported ? import_ansis.default.dim("Unsupported") : "-",
|
|
1308
1480
|
"-",
|
|
1309
1481
|
"-",
|
|
1310
|
-
tool.reason
|
|
1482
|
+
isUnsupported ? import_ansis.default.dim("No local analyzer (Codacy Cloud)") : tool.reason
|
|
1311
1483
|
]);
|
|
1312
1484
|
}
|
|
1313
1485
|
lines.push(table.toString());
|
|
@@ -1332,13 +1504,14 @@ function formatTextSummary(result, isInspect) {
|
|
|
1332
1504
|
]);
|
|
1333
1505
|
}
|
|
1334
1506
|
for (const tool of unavailableTools) {
|
|
1507
|
+
const isUnsupported = tool.availableVia === "cloud";
|
|
1335
1508
|
table.push([
|
|
1336
|
-
import_ansis.default.dim.red(tool.displayName ?? tool.toolId),
|
|
1509
|
+
isUnsupported ? import_ansis.default.dim(tool.displayName ?? tool.toolId) : import_ansis.default.dim.red(tool.displayName ?? tool.toolId),
|
|
1337
1510
|
"-",
|
|
1511
|
+
isUnsupported ? import_ansis.default.dim("Unsupported") : "-",
|
|
1338
1512
|
"-",
|
|
1339
1513
|
"-",
|
|
1340
|
-
"-",
|
|
1341
|
-
import_ansis.default.dim("missing binary"),
|
|
1514
|
+
isUnsupported ? import_ansis.default.dim("cloud-only") : import_ansis.default.dim("missing binary"),
|
|
1342
1515
|
"-"
|
|
1343
1516
|
]);
|
|
1344
1517
|
}
|
|
@@ -1954,6 +2127,12 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
1954
2127
|
["gson", { displayName: "Gson", kind: "library" }],
|
|
1955
2128
|
["okhttp", { displayName: "OkHttp", kind: "library" }],
|
|
1956
2129
|
["retrofit", { displayName: "Retrofit", kind: "library" }],
|
|
2130
|
+
["spock-core", { displayName: "Spock", kind: "library" }],
|
|
2131
|
+
// --- Groovy — Frameworks ---
|
|
2132
|
+
// Grails/GORM artifact IDs vary (grails-core, grails-web, gorm-hibernate5, …);
|
|
2133
|
+
// the `grails-`/`gorm-` prefix rules below catch the rest.
|
|
2134
|
+
["grails-core", { displayName: "Grails", kind: "framework" }],
|
|
2135
|
+
["gorm-core", { displayName: "GORM", kind: "library" }],
|
|
1957
2136
|
// --- Go — Frameworks ---
|
|
1958
2137
|
["github.com/gin-gonic/gin", { displayName: "Gin", kind: "framework" }],
|
|
1959
2138
|
["github.com/labstack/echo", { displayName: "Echo", kind: "framework" }],
|
|
@@ -1995,9 +2174,20 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
1995
2174
|
// --- PHP — Frameworks ---
|
|
1996
2175
|
["laravel/framework", { displayName: "Laravel", kind: "framework" }],
|
|
1997
2176
|
["symfony/framework-bundle", { displayName: "Symfony", kind: "framework" }],
|
|
2177
|
+
["symfony/symfony", { displayName: "Symfony", kind: "framework" }],
|
|
1998
2178
|
["codeigniter4/framework", { displayName: "CodeIgniter", kind: "framework" }],
|
|
1999
2179
|
["slim/slim", { displayName: "Slim", kind: "framework" }],
|
|
2000
2180
|
["cakephp/cakephp", { displayName: "CakePHP", kind: "framework" }],
|
|
2181
|
+
// WordPress is rarely a real Composer dependency; these are the common
|
|
2182
|
+
// "WordPress as a dependency" packages used by Bedrock/Composer-based sites.
|
|
2183
|
+
["johnpbloch/wordpress-core", { displayName: "WordPress", kind: "framework" }],
|
|
2184
|
+
["johnpbloch/wordpress", { displayName: "WordPress", kind: "framework" }],
|
|
2185
|
+
["roots/wordpress", { displayName: "WordPress", kind: "framework" }],
|
|
2186
|
+
["drupal/core", { displayName: "Drupal", kind: "framework" }],
|
|
2187
|
+
["drupal/core-recommended", { displayName: "Drupal", kind: "framework" }],
|
|
2188
|
+
["magento/product-community-edition", { displayName: "Magento", kind: "framework" }],
|
|
2189
|
+
["magento/product-enterprise-edition", { displayName: "Magento", kind: "framework" }],
|
|
2190
|
+
["magento/framework", { displayName: "Magento", kind: "framework" }],
|
|
2001
2191
|
// --- PHP — Libraries ---
|
|
2002
2192
|
["phpunit/phpunit", { displayName: "PHPUnit", kind: "library" }],
|
|
2003
2193
|
["guzzlehttp/guzzle", { displayName: "Guzzle", kind: "library" }],
|
|
@@ -2027,7 +2217,32 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
|
2027
2217
|
["scalatest", { displayName: "ScalaTest", kind: "library" }],
|
|
2028
2218
|
["specs2-core", { displayName: "Specs2", kind: "library" }],
|
|
2029
2219
|
["cats-core", { displayName: "Cats", kind: "library" }],
|
|
2030
|
-
["circe-core", { displayName: "Circe", kind: "library" }]
|
|
2220
|
+
["circe-core", { displayName: "Circe", kind: "library" }],
|
|
2221
|
+
// --- Dart — Frameworks ---
|
|
2222
|
+
// `flutter` and `angular` appear as plain keys in pubspec.yaml.
|
|
2223
|
+
["flutter", { displayName: "Flutter", kind: "framework" }],
|
|
2224
|
+
["flutter_test", { displayName: "Flutter", kind: "framework" }],
|
|
2225
|
+
["angular", { displayName: "Angular", kind: "framework" }],
|
|
2226
|
+
// --- Dart — Libraries ---
|
|
2227
|
+
// Kept to Dart-specific names with negligible cross-ecosystem collision risk
|
|
2228
|
+
// (generic names like "get"/"provider" are intentionally omitted).
|
|
2229
|
+
["riverpod", { displayName: "Riverpod", kind: "library" }],
|
|
2230
|
+
["flutter_bloc", { displayName: "Bloc", kind: "library" }],
|
|
2231
|
+
["dio", { displayName: "Dio", kind: "library" }],
|
|
2232
|
+
// --- .NET (C# / VB) — Libraries ---
|
|
2233
|
+
// NuGet package IDs. Microsoft.* families are also covered by prefix rules below.
|
|
2234
|
+
["EntityFramework", { displayName: "Entity Framework", kind: "library" }],
|
|
2235
|
+
["NUnit", { displayName: "NUnit", kind: "library" }],
|
|
2236
|
+
["Moq", { displayName: "Moq", kind: "library" }],
|
|
2237
|
+
["NSubstitute", { displayName: "NSubstitute", kind: "library" }],
|
|
2238
|
+
["FluentAssertions", { displayName: "FluentAssertions", kind: "library" }],
|
|
2239
|
+
["Shouldly", { displayName: "Shouldly", kind: "library" }],
|
|
2240
|
+
["NLog", { displayName: "NLog", kind: "library" }],
|
|
2241
|
+
["log4net", { displayName: "log4net", kind: "library" }],
|
|
2242
|
+
["Newtonsoft.Json", { displayName: "Json.NET", kind: "library" }],
|
|
2243
|
+
["AutoMapper", { displayName: "AutoMapper", kind: "library" }],
|
|
2244
|
+
["Dapper", { displayName: "Dapper", kind: "library" }],
|
|
2245
|
+
["MediatR", { displayName: "MediatR", kind: "library" }]
|
|
2031
2246
|
]);
|
|
2032
2247
|
var DEPENDENCY_PREFIX_RULES = [
|
|
2033
2248
|
{ prefix: "@angular/", classification: { displayName: "Angular", kind: "framework" } },
|
|
@@ -2057,7 +2272,35 @@ var DEPENDENCY_PREFIX_RULES = [
|
|
|
2057
2272
|
{ prefix: "@shopify/", classification: { displayName: "Shopify", kind: "platform" } },
|
|
2058
2273
|
{ prefix: "@aws-sdk/", classification: { displayName: "AWS SDK", kind: "library" } },
|
|
2059
2274
|
{ prefix: "@google-cloud/", classification: { displayName: "Google Cloud", kind: "library" } },
|
|
2060
|
-
{ prefix: "@azure/", classification: { displayName: "Azure SDK", kind: "library" } }
|
|
2275
|
+
{ prefix: "@azure/", classification: { displayName: "Azure SDK", kind: "library" } },
|
|
2276
|
+
// Groovy — Grails/GORM artifact families (build.gradle artifactIds).
|
|
2277
|
+
{ prefix: "grails-", classification: { displayName: "Grails", kind: "framework" } },
|
|
2278
|
+
{ prefix: "gorm-", classification: { displayName: "GORM", kind: "library" } },
|
|
2279
|
+
// .NET — Microsoft package families and common test/logging libraries. More
|
|
2280
|
+
// specific prefixes (Blazor) must precede broader ones (ASP.NET Core).
|
|
2281
|
+
{
|
|
2282
|
+
prefix: "Microsoft.AspNetCore.Components.WebAssembly",
|
|
2283
|
+
classification: { displayName: "Blazor", kind: "framework" }
|
|
2284
|
+
},
|
|
2285
|
+
{
|
|
2286
|
+
prefix: "Microsoft.AspNetCore.",
|
|
2287
|
+
classification: { displayName: "ASP.NET Core", kind: "framework" }
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
prefix: "Microsoft.EntityFrameworkCore",
|
|
2291
|
+
classification: { displayName: "Entity Framework", kind: "library" }
|
|
2292
|
+
},
|
|
2293
|
+
{
|
|
2294
|
+
prefix: "Microsoft.Azure.Functions",
|
|
2295
|
+
classification: { displayName: "Azure Functions", kind: "framework" }
|
|
2296
|
+
},
|
|
2297
|
+
{
|
|
2298
|
+
prefix: "Microsoft.Azure.WebJobs",
|
|
2299
|
+
classification: { displayName: "Azure Functions", kind: "framework" }
|
|
2300
|
+
},
|
|
2301
|
+
{ prefix: "xunit", classification: { displayName: "xUnit", kind: "library" } },
|
|
2302
|
+
{ prefix: "MSTest.", classification: { displayName: "MSTest", kind: "library" } },
|
|
2303
|
+
{ prefix: "Serilog", classification: { displayName: "Serilog", kind: "library" } }
|
|
2061
2304
|
];
|
|
2062
2305
|
function classifyDependency(name) {
|
|
2063
2306
|
const exact = DEPENDENCY_CATALOG.get(name);
|
|
@@ -2086,7 +2329,12 @@ var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
|
|
|
2086
2329
|
"Package.swift",
|
|
2087
2330
|
"Jenkinsfile",
|
|
2088
2331
|
"Vagrantfile",
|
|
2089
|
-
"Procfile"
|
|
2332
|
+
"Procfile",
|
|
2333
|
+
"pubspec.yaml",
|
|
2334
|
+
"mix.exs",
|
|
2335
|
+
"shard.yml",
|
|
2336
|
+
"packages.config",
|
|
2337
|
+
"Directory.Packages.props"
|
|
2090
2338
|
]);
|
|
2091
2339
|
var NOTABLE_ROOT_ONLY = /* @__PURE__ */ new Set([
|
|
2092
2340
|
".dockerignore",
|
|
@@ -2146,6 +2394,7 @@ function isNotableFile(filePath) {
|
|
|
2146
2394
|
const isRoot = !filePath.includes("/");
|
|
2147
2395
|
if (NOTABLE_ANY_DEPTH.has(basename)) return true;
|
|
2148
2396
|
if (isRoot && NOTABLE_ROOT_ONLY.has(basename)) return true;
|
|
2397
|
+
if (/\.(csproj|vbproj|fsproj|sln)$/i.test(basename)) return true;
|
|
2149
2398
|
for (const prefix of NOTABLE_PATH_PREFIXES) {
|
|
2150
2399
|
if (filePath.startsWith(prefix)) return true;
|
|
2151
2400
|
}
|
|
@@ -2352,6 +2601,51 @@ function parsePackageSwift(content) {
|
|
|
2352
2601
|
}
|
|
2353
2602
|
return deps;
|
|
2354
2603
|
}
|
|
2604
|
+
function parsePubspecYaml(content) {
|
|
2605
|
+
const deps = [];
|
|
2606
|
+
let inSection = false;
|
|
2607
|
+
let childIndent = -1;
|
|
2608
|
+
for (const rawLine of content.split("\n")) {
|
|
2609
|
+
const trimmed = rawLine.trim();
|
|
2610
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
2611
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
2612
|
+
if (indent === 0) {
|
|
2613
|
+
inSection = /^(dependencies|dev_dependencies|dependency_overrides):/.test(trimmed);
|
|
2614
|
+
childIndent = -1;
|
|
2615
|
+
continue;
|
|
2616
|
+
}
|
|
2617
|
+
if (!inSection) continue;
|
|
2618
|
+
if (childIndent === -1) childIndent = indent;
|
|
2619
|
+
if (indent !== childIndent) continue;
|
|
2620
|
+
const m = trimmed.match(/^([a-zA-Z0-9_.]+)\s*:(?:\s*["']?\^?([0-9][^"'#\s]*))?/);
|
|
2621
|
+
if (m) deps.push({ name: m[1], version: m[2] });
|
|
2622
|
+
}
|
|
2623
|
+
return deps;
|
|
2624
|
+
}
|
|
2625
|
+
function parseXmlDependencyElements(content, elementRegex, nameAttr) {
|
|
2626
|
+
const deps = [];
|
|
2627
|
+
const nameRegex = new RegExp(`\\b${nameAttr}\\s*=\\s*["']([^"']+)["']`, "i");
|
|
2628
|
+
const versionRegex = /\bversion\s*=\s*["']([^"']+)["']/i;
|
|
2629
|
+
let match;
|
|
2630
|
+
while ((match = elementRegex.exec(content)) !== null) {
|
|
2631
|
+
const attrs = match[1];
|
|
2632
|
+
const nameMatch = attrs.match(nameRegex);
|
|
2633
|
+
if (!nameMatch) continue;
|
|
2634
|
+
const versionMatch = attrs.match(versionRegex);
|
|
2635
|
+
deps.push({ name: nameMatch[1].trim(), version: versionMatch?.[1]?.trim() });
|
|
2636
|
+
}
|
|
2637
|
+
return deps;
|
|
2638
|
+
}
|
|
2639
|
+
function parseCsproj(content) {
|
|
2640
|
+
return parseXmlDependencyElements(
|
|
2641
|
+
content,
|
|
2642
|
+
/<Package(?:Reference|Version)\s+([^>]+?)\/?>/gi,
|
|
2643
|
+
"Include"
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2646
|
+
function parsePackagesConfig(content) {
|
|
2647
|
+
return parseXmlDependencyElements(content, /<package\s+([^>]+?)\/?>/gi, "id");
|
|
2648
|
+
}
|
|
2355
2649
|
var MANIFEST_PARSERS = /* @__PURE__ */ new Map([
|
|
2356
2650
|
["package.json", parsePackageJson],
|
|
2357
2651
|
["requirements.txt", parseRequirementsTxt],
|
|
@@ -2365,7 +2659,15 @@ var MANIFEST_PARSERS = /* @__PURE__ */ new Map([
|
|
|
2365
2659
|
["Cargo.toml", parseCargoToml],
|
|
2366
2660
|
["composer.json", parseComposerJson],
|
|
2367
2661
|
["Podfile", parsePodfile],
|
|
2368
|
-
["Package.swift", parsePackageSwift]
|
|
2662
|
+
["Package.swift", parsePackageSwift],
|
|
2663
|
+
["pubspec.yaml", parsePubspecYaml],
|
|
2664
|
+
["packages.config", parsePackagesConfig],
|
|
2665
|
+
["Directory.Packages.props", parseCsproj]
|
|
2666
|
+
]);
|
|
2667
|
+
var MANIFEST_SUFFIX_PARSERS = /* @__PURE__ */ new Map([
|
|
2668
|
+
[".csproj", parseCsproj],
|
|
2669
|
+
[".vbproj", parseCsproj],
|
|
2670
|
+
[".fsproj", parseCsproj]
|
|
2369
2671
|
]);
|
|
2370
2672
|
|
|
2371
2673
|
// src/discover.ts
|
|
@@ -2415,39 +2717,39 @@ function findNotableFiles(files) {
|
|
|
2415
2717
|
}
|
|
2416
2718
|
return Array.from(notable).sort();
|
|
2417
2719
|
}
|
|
2418
|
-
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
manifestFiles.set(basename, existing);
|
|
2426
|
-
}
|
|
2720
|
+
function resolveManifestParser(filePath) {
|
|
2721
|
+
const basename = filePath.split("/").pop() ?? filePath;
|
|
2722
|
+
const byName = MANIFEST_PARSERS.get(basename);
|
|
2723
|
+
if (byName) return byName;
|
|
2724
|
+
const lowerBasename = basename.toLowerCase();
|
|
2725
|
+
for (const [suffix, parser] of MANIFEST_SUFFIX_PARSERS) {
|
|
2726
|
+
if (lowerBasename.endsWith(suffix)) return parser;
|
|
2427
2727
|
}
|
|
2728
|
+
return void 0;
|
|
2729
|
+
}
|
|
2730
|
+
async function extractDependencies(repositoryRoot, files) {
|
|
2428
2731
|
const allDeps = [];
|
|
2429
|
-
for (const
|
|
2430
|
-
const parser =
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
}
|
|
2732
|
+
for (const filePath of files) {
|
|
2733
|
+
const parser = resolveManifestParser(filePath);
|
|
2734
|
+
if (!parser) continue;
|
|
2735
|
+
let content;
|
|
2736
|
+
try {
|
|
2737
|
+
content = await import_promises2.default.readFile(import_path5.default.join(repositoryRoot, filePath), "utf-8");
|
|
2738
|
+
} catch {
|
|
2739
|
+
continue;
|
|
2740
|
+
}
|
|
2741
|
+
const rawDeps = parser(content);
|
|
2742
|
+
for (const raw of rawDeps) {
|
|
2743
|
+
const classification = classifyDependency(raw.name);
|
|
2744
|
+
if (classification) {
|
|
2745
|
+
allDeps.push({
|
|
2746
|
+
name: raw.name,
|
|
2747
|
+
displayName: classification.displayName,
|
|
2748
|
+
kind: classification.kind,
|
|
2749
|
+
source: "manifest",
|
|
2750
|
+
manifestFile: filePath,
|
|
2751
|
+
version: raw.version
|
|
2752
|
+
});
|
|
2451
2753
|
}
|
|
2452
2754
|
}
|
|
2453
2755
|
}
|
|
@@ -6083,6 +6385,37 @@ async function listIgnoredFiles(provider, org, repo) {
|
|
|
6083
6385
|
}
|
|
6084
6386
|
|
|
6085
6387
|
// src/init.ts
|
|
6388
|
+
function deriveApiToolId(tool) {
|
|
6389
|
+
return tool.prefix ? tool.prefix.replace(/_$/, "") : tool.shortName;
|
|
6390
|
+
}
|
|
6391
|
+
function isTrackableTool(tool) {
|
|
6392
|
+
if (tool.clientSide) return false;
|
|
6393
|
+
if (tool.standalone) return false;
|
|
6394
|
+
if (/\bdeprecated\b/i.test(tool.name ?? "")) return false;
|
|
6395
|
+
return true;
|
|
6396
|
+
}
|
|
6397
|
+
function buildAdapterByUuid(adapters, apiTools) {
|
|
6398
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
6399
|
+
for (const adapter of adapters) {
|
|
6400
|
+
byKey.set(adapter.id.toLowerCase(), adapter);
|
|
6401
|
+
byKey.set(adapter.displayName.toLowerCase(), adapter);
|
|
6402
|
+
}
|
|
6403
|
+
const result = /* @__PURE__ */ new Map();
|
|
6404
|
+
for (const tool of apiTools) {
|
|
6405
|
+
const candidates = [];
|
|
6406
|
+
if (tool.prefix) candidates.push(tool.prefix.replace(/_$/, "").toLowerCase());
|
|
6407
|
+
if (tool.shortName) candidates.push(tool.shortName.toLowerCase());
|
|
6408
|
+
if (tool.name) candidates.push(tool.name.toLowerCase());
|
|
6409
|
+
for (const candidate of candidates) {
|
|
6410
|
+
const adapter = byKey.get(candidate);
|
|
6411
|
+
if (adapter) {
|
|
6412
|
+
result.set(tool.uuid, adapter);
|
|
6413
|
+
break;
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
}
|
|
6417
|
+
return result;
|
|
6418
|
+
}
|
|
6086
6419
|
async function detectLanguages(repositoryRoot, precomputedFiles) {
|
|
6087
6420
|
let files;
|
|
6088
6421
|
if (precomputedFiles) {
|
|
@@ -6185,34 +6518,32 @@ async function initDefaultConfig(repositoryRoot, adapters) {
|
|
|
6185
6518
|
);
|
|
6186
6519
|
const apiTools = await listTools();
|
|
6187
6520
|
const repoLanguages = new Set(base.metadata.languages);
|
|
6521
|
+
const adapterByUuid = buildAdapterByUuid(adapters, apiTools);
|
|
6188
6522
|
const updatedTools = [...base.tools];
|
|
6189
6523
|
const coveredToolIds = new Set(updatedTools.map((t) => t.toolId));
|
|
6190
6524
|
let addedTools = 0;
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
const
|
|
6194
|
-
|
|
6195
|
-
);
|
|
6196
|
-
if (!apiTool) continue;
|
|
6525
|
+
let addedUnsupported = 0;
|
|
6526
|
+
for (const apiTool of apiTools) {
|
|
6527
|
+
const adapter = adapterByUuid.get(apiTool.uuid);
|
|
6528
|
+
if (adapter && toolsWithLocalConfig.has(adapter.id)) continue;
|
|
6529
|
+
if (!adapter && !isTrackableTool(apiTool)) continue;
|
|
6197
6530
|
if (!apiTool.enabledByDefault) continue;
|
|
6198
6531
|
if (apiTool.languages.length > 0 && repoLanguages.size > 0) {
|
|
6199
6532
|
const hasOverlap = apiTool.languages.some((lang) => repoLanguages.has(lang));
|
|
6200
6533
|
if (!hasOverlap) continue;
|
|
6201
6534
|
}
|
|
6535
|
+
const toolId = adapter ? adapter.id : deriveApiToolId(apiTool);
|
|
6536
|
+
if (coveredToolIds.has(toolId)) continue;
|
|
6202
6537
|
let patterns = [];
|
|
6203
6538
|
try {
|
|
6204
6539
|
const apiPatterns = await listPatterns(apiTool.uuid);
|
|
6205
6540
|
patterns = apiPatterns.filter((p) => p.enabled).map((p) => ({ patternId: p.id }));
|
|
6206
6541
|
} catch {
|
|
6207
6542
|
}
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
});
|
|
6213
|
-
coveredToolIds.add(adapter.id);
|
|
6214
|
-
addedTools++;
|
|
6215
|
-
}
|
|
6543
|
+
updatedTools.push({ toolId, patterns });
|
|
6544
|
+
coveredToolIds.add(toolId);
|
|
6545
|
+
if (adapter) addedTools++;
|
|
6546
|
+
else addedUnsupported++;
|
|
6216
6547
|
}
|
|
6217
6548
|
const baseConfig = createCodacyConfig({
|
|
6218
6549
|
source: "default",
|
|
@@ -6224,7 +6555,10 @@ async function initDefaultConfig(repositoryRoot, adapters) {
|
|
|
6224
6555
|
repositoryRoot,
|
|
6225
6556
|
adapters
|
|
6226
6557
|
);
|
|
6227
|
-
return {
|
|
6558
|
+
return {
|
|
6559
|
+
config,
|
|
6560
|
+
stats: { totalApiTools: apiTools.length, addedTools, addedUnsupported, codacyYamlApplied }
|
|
6561
|
+
};
|
|
6228
6562
|
}
|
|
6229
6563
|
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters, apiToken, onRemoteToolsFound, onToolProcessed, onIgnoredFilesFetched) {
|
|
6230
6564
|
configureApiToken(apiToken);
|
|
@@ -6236,56 +6570,61 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
6236
6570
|
} catch {
|
|
6237
6571
|
}
|
|
6238
6572
|
onIgnoredFilesFetched?.(ignoredFilesResult.totalFetched, ignoredFilesResult.capped);
|
|
6239
|
-
const adapterByName = /* @__PURE__ */ new Map();
|
|
6240
|
-
for (const adapter of adapters) {
|
|
6241
|
-
adapterByName.set(adapter.id, adapter);
|
|
6242
|
-
adapterByName.set(adapter.displayName.toLowerCase(), adapter);
|
|
6243
|
-
}
|
|
6244
6573
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
6245
6574
|
const allApiTools = await listTools();
|
|
6246
|
-
const adapterByUuid =
|
|
6247
|
-
|
|
6248
|
-
const adapter = apiTool.prefix ? adapterByName.get(apiTool.prefix.replace(/_$/, "")) : adapterByName.get(apiTool.name);
|
|
6249
|
-
if (adapter) {
|
|
6250
|
-
adapterByUuid.set(apiTool.uuid, adapter);
|
|
6251
|
-
}
|
|
6252
|
-
}
|
|
6575
|
+
const adapterByUuid = buildAdapterByUuid(adapters, allApiTools);
|
|
6576
|
+
const apiToolByUuid = new Map(allApiTools.map((t) => [t.uuid, t]));
|
|
6253
6577
|
const matchedAdapterCount = remoteTools.filter((t) => adapterByUuid.has(t.uuid)).length;
|
|
6254
6578
|
onRemoteToolsFound?.(remoteTools.length, matchedAdapterCount);
|
|
6579
|
+
const fetchPatterns = async (uuid) => {
|
|
6580
|
+
try {
|
|
6581
|
+
const configuredPatterns = await listRepositoryToolPatterns(
|
|
6582
|
+
provider,
|
|
6583
|
+
organization,
|
|
6584
|
+
repositoryName,
|
|
6585
|
+
uuid
|
|
6586
|
+
);
|
|
6587
|
+
return configuredPatterns.map((cp) => {
|
|
6588
|
+
const params = cp.parameters.length > 0 ? Object.fromEntries(cp.parameters.map((p) => [p.name, p.value])) : void 0;
|
|
6589
|
+
return {
|
|
6590
|
+
patternId: cp.patternDefinition.id,
|
|
6591
|
+
...params ? { parameters: params } : {}
|
|
6592
|
+
};
|
|
6593
|
+
});
|
|
6594
|
+
} catch {
|
|
6595
|
+
return [];
|
|
6596
|
+
}
|
|
6597
|
+
};
|
|
6255
6598
|
const tools = [];
|
|
6256
6599
|
let matchedAdapters = 0;
|
|
6600
|
+
let unsupportedTools = 0;
|
|
6257
6601
|
for (const remoteTool of remoteTools) {
|
|
6258
|
-
const adapter = adapterByUuid.get(remoteTool.uuid);
|
|
6259
|
-
if (!adapter) continue;
|
|
6260
|
-
matchedAdapters++;
|
|
6261
6602
|
const usesLocalConfig = remoteTool.settings.usesConfigurationFile;
|
|
6262
|
-
const
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
patterns
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
};
|
|
6278
|
-
});
|
|
6279
|
-
} catch {
|
|
6280
|
-
}
|
|
6603
|
+
const adapter = adapterByUuid.get(remoteTool.uuid);
|
|
6604
|
+
if (adapter) {
|
|
6605
|
+
matchedAdapters++;
|
|
6606
|
+
const localConfigPath = localConfigs.get(adapter.id);
|
|
6607
|
+
const patterns2 = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid);
|
|
6608
|
+
onToolProcessed?.(adapter.displayName, patterns2.length, usesLocalConfig);
|
|
6609
|
+
tools.push({
|
|
6610
|
+
toolId: adapter.id,
|
|
6611
|
+
...localConfigPath ? { localConfigurationFile: localConfigPath } : {},
|
|
6612
|
+
// Honour local config when the API says usesConfigurationFile, OR when a
|
|
6613
|
+
// local config exists and no patterns were configured (backwards-compat).
|
|
6614
|
+
...usesLocalConfig || localConfigPath && patterns2.length === 0 ? { useLocalConfigurationFile: true } : {},
|
|
6615
|
+
patterns: patterns2
|
|
6616
|
+
});
|
|
6617
|
+
continue;
|
|
6281
6618
|
}
|
|
6282
|
-
|
|
6619
|
+
const apiTool = apiToolByUuid.get(remoteTool.uuid);
|
|
6620
|
+
if (!apiTool || !isTrackableTool(apiTool)) continue;
|
|
6621
|
+
const toolId = deriveApiToolId(apiTool);
|
|
6622
|
+
const patterns = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid);
|
|
6623
|
+
unsupportedTools++;
|
|
6624
|
+
onToolProcessed?.(apiTool.name ?? toolId, patterns.length, usesLocalConfig);
|
|
6283
6625
|
tools.push({
|
|
6284
|
-
toolId
|
|
6285
|
-
...
|
|
6286
|
-
// Honour local config when the API says usesConfigurationFile, OR when a
|
|
6287
|
-
// local config exists and no patterns were configured (backwards-compat).
|
|
6288
|
-
...usesLocalConfig || localConfigPath && patterns.length === 0 ? { useLocalConfigurationFile: true } : {},
|
|
6626
|
+
toolId,
|
|
6627
|
+
...usesLocalConfig ? { useLocalConfigurationFile: true } : {},
|
|
6289
6628
|
patterns
|
|
6290
6629
|
});
|
|
6291
6630
|
}
|
|
@@ -6311,6 +6650,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
6311
6650
|
stats: {
|
|
6312
6651
|
totalRemoteTools: remoteTools.length,
|
|
6313
6652
|
matchedAdapters,
|
|
6653
|
+
unsupportedTools,
|
|
6314
6654
|
codacyYamlApplied,
|
|
6315
6655
|
ignoredFilesCount: ignoredFilesResult.totalFetched,
|
|
6316
6656
|
ignoredFilesCapped: ignoredFilesResult.capped
|
|
@@ -6372,7 +6712,7 @@ async function initContainerConfig(repositoryRoot, adapters) {
|
|
|
6372
6712
|
}
|
|
6373
6713
|
return createCodacyConfig({ source: "container", tools, includeFiles });
|
|
6374
6714
|
}
|
|
6375
|
-
async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString) {
|
|
6715
|
+
async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString, loadUnsupportedPatterns) {
|
|
6376
6716
|
const discoveryResult = await discover(repositoryRoot);
|
|
6377
6717
|
onDiscoveryComplete?.(discoveryResult);
|
|
6378
6718
|
const detectedKeys = /* @__PURE__ */ new Set();
|
|
@@ -6421,6 +6761,34 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
6421
6761
|
});
|
|
6422
6762
|
}
|
|
6423
6763
|
}
|
|
6764
|
+
let selectedUnsupported = 0;
|
|
6765
|
+
const selectedFamilies = new Set(tools.map((t) => toolFamily(t.toolId)));
|
|
6766
|
+
for (const desc of descriptors) {
|
|
6767
|
+
if (!desc.unsupported) continue;
|
|
6768
|
+
if (!loadUnsupportedPatterns) break;
|
|
6769
|
+
if (!desc.languages.includes("*") && desc.languages.length > 0 && repoLanguages.size > 0 && !desc.languages.some((lang) => repoLanguages.has(lang))) {
|
|
6770
|
+
continue;
|
|
6771
|
+
}
|
|
6772
|
+
const family = toolFamily(desc.id);
|
|
6773
|
+
if (selectedFamilies.has(family)) continue;
|
|
6774
|
+
const patternsMetadata = loadUnsupportedPatterns(desc.id);
|
|
6775
|
+
if (patternsMetadata.length === 0) continue;
|
|
6776
|
+
const selectedPatterns = filterAutoPatterns(
|
|
6777
|
+
patternsMetadata,
|
|
6778
|
+
desc.frameworkMappings,
|
|
6779
|
+
detectedKeys,
|
|
6780
|
+
repoLanguages,
|
|
6781
|
+
filters
|
|
6782
|
+
);
|
|
6783
|
+
if (selectedPatterns.length > 0) {
|
|
6784
|
+
tools.push({
|
|
6785
|
+
toolId: desc.id,
|
|
6786
|
+
patterns: selectedPatterns.map((p) => ({ patternId: p.id }))
|
|
6787
|
+
});
|
|
6788
|
+
selectedFamilies.add(family);
|
|
6789
|
+
selectedUnsupported++;
|
|
6790
|
+
}
|
|
6791
|
+
}
|
|
6424
6792
|
const baseConfig = createCodacyConfig({
|
|
6425
6793
|
source: "auto",
|
|
6426
6794
|
languages: discoveryResult.languages,
|
|
@@ -6439,6 +6807,7 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
6439
6807
|
detectedFrameworks: discoveryResult.frameworks.length,
|
|
6440
6808
|
detectedLibraries: discoveryResult.libraries.length,
|
|
6441
6809
|
selectedTools: tools.length,
|
|
6810
|
+
selectedUnsupported,
|
|
6442
6811
|
codacyYamlApplied
|
|
6443
6812
|
}
|
|
6444
6813
|
};
|
|
@@ -6760,6 +7129,7 @@ function createLogger(options = {}) {
|
|
|
6760
7129
|
readCodacyYaml,
|
|
6761
7130
|
registerAdapter,
|
|
6762
7131
|
registerLazyAdapter,
|
|
7132
|
+
registerUnsupportedTool,
|
|
6763
7133
|
resolveConfigPath,
|
|
6764
7134
|
resolveEngineToAdapter,
|
|
6765
7135
|
rotateLogFiles,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codacy/analysis-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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.11.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.11.0"
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"author": "Codacy <support@codacy.com> (https://www.codacy.com)",
|