@codacy/analysis-runner 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -34,6 +34,18 @@ interface AnalyzeOptions {
34
34
  /** Target execution environment. "local" = developer machine, "container" = Codacy Docker runner.
35
35
  * Phase 9 implements the actual container path; this field is accepted but currently ignored. */
36
36
  runner?: "local" | "container";
37
+ /**
38
+ * Git-aware file scope. Mutually exclusive options:
39
+ * - `"staged"` — analyze only files in the git staging area
40
+ * - `{ diff: string | true }` — analyze files changed vs a base ref (true = auto-detect)
41
+ * - `"pr"` — analyze files in the current pull request
42
+ *
43
+ * Resolved files are intersected with `files` if both are provided.
44
+ * If the resolved set is empty, analysis returns immediately with zero issues.
45
+ */
46
+ gitScope?: "staged" | {
47
+ diff: string | true;
48
+ } | "pr";
37
49
  /** Logger for pipeline progress messages (install status, etc.). Defaults to no-op. */
38
50
  logger?: Logger;
39
51
  /** Called when a tool starts executing. */
@@ -278,6 +290,32 @@ declare function resolveEngineToAdapter(engineName: string, adapters: Readonly<T
278
290
  */
279
291
  declare function applyCodacyYamlExcludes(config: CodacyConfig, yamlConfig: CodacyYamlConfig, adapters: Readonly<ToolAdapter>[], logger?: Logger): CodacyConfig;
280
292
 
293
+ /**
294
+ * File discovery and routing — finds files in a repository, applies exclusion
295
+ * layers (.gitignore, global exclude, per-tool exclude), and routes files to
296
+ * the appropriate tool adapters based on file patterns.
297
+ */
298
+
299
+ /**
300
+ * Returns files changed in the git staging area (index), excluding deleted files.
301
+ * Used by `--staged` to analyze only what's about to be committed.
302
+ */
303
+ declare function listStagedFiles(repositoryRoot: string): string[];
304
+ /**
305
+ * Returns files changed between a base ref and HEAD, excluding deleted files.
306
+ * Uses the three-dot diff (`base...HEAD`) to show only changes since divergence.
307
+ *
308
+ * If `base` is omitted, auto-detects the default branch via `detectDefaultBranch()`.
309
+ */
310
+ declare function listDiffFiles(repositoryRoot: string, base?: string): string[];
311
+ /**
312
+ * Returns files changed in the current pull request.
313
+ *
314
+ * Tries `gh pr view` first for accuracy (handles renames, squash merges).
315
+ * Falls back to git merge-base diff when gh is unavailable or not in a PR context.
316
+ */
317
+ declare function listPrFiles(repositoryRoot: string): string[];
318
+
281
319
  /**
282
320
  * Codacy API client — thin wrappers around the generated OpenAPI client.
283
321
  *
@@ -361,4 +399,4 @@ declare function rotateLogFiles(logsDir: string, maxFiles: number): void;
361
399
  */
362
400
  declare function createLogger(options?: LoggerOptions): Logger;
363
401
 
364
- export { type AnalyzeOptions, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ExecutionMode, type InitDefaultStats, type InitLocalStats, type InitRemoteStats, type LoggerOptions, analyze, applyCodacyYamlExcludes, buildToolConfig, clearAdapters, configureApiToken, createCodacyConfig, createLogger, detectLanguages, formatOutput, formatTextSummary, getRegisteredAdapters, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, readCodacyConfig, readCodacyYaml, registerAdapter, resolveEngineToAdapter, rotateLogFiles, validateApiToken, writeCodacyConfig };
402
+ export { type AnalyzeOptions, type CodacyYamlConfig, type CodacyYamlEngineConfig, type ExecutionMode, type InitDefaultStats, type InitLocalStats, type InitRemoteStats, type LoggerOptions, analyze, applyCodacyYamlExcludes, buildToolConfig, clearAdapters, configureApiToken, createCodacyConfig, createLogger, detectLanguages, formatOutput, formatTextSummary, getRegisteredAdapters, initContainerConfig, initDefaultConfig, initLocalConfig, initRemoteConfig, listDiffFiles, listPrFiles, listStagedFiles, readCodacyConfig, readCodacyYaml, registerAdapter, resolveEngineToAdapter, rotateLogFiles, validateApiToken, writeCodacyConfig };
package/dist/index.js CHANGED
@@ -45,6 +45,9 @@ __export(index_exports, {
45
45
  initDefaultConfig: () => initDefaultConfig,
46
46
  initLocalConfig: () => initLocalConfig,
47
47
  initRemoteConfig: () => initRemoteConfig,
48
+ listDiffFiles: () => listDiffFiles,
49
+ listPrFiles: () => listPrFiles,
50
+ listStagedFiles: () => listStagedFiles,
48
51
  readCodacyConfig: () => readCodacyConfig,
49
52
  readCodacyYaml: () => readCodacyYaml,
50
53
  registerAdapter: () => registerAdapter,
@@ -92,11 +95,25 @@ async function readCodacyConfig(repositoryRoot) {
92
95
  const configPath = import_path.default.join(repositoryRoot, CODACY_DIR, CONFIG_FILE);
93
96
  try {
94
97
  const raw = await import_promises.default.readFile(configPath, "utf-8");
95
- return JSON.parse(raw);
98
+ const config = JSON.parse(raw);
99
+ coerceParameterValues(config);
100
+ return config;
96
101
  } catch {
97
102
  return null;
98
103
  }
99
104
  }
105
+ function coerceParameterValues(config) {
106
+ for (const tool of config.tools) {
107
+ for (const pattern of tool.patterns) {
108
+ if (!pattern.parameters) continue;
109
+ for (const [key, value] of Object.entries(pattern.parameters)) {
110
+ if (typeof value !== "string") {
111
+ pattern.parameters[key] = String(value);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
100
117
  async function writeCodacyConfig(repositoryRoot, config) {
101
118
  const codacyDir = import_path.default.join(repositoryRoot, CODACY_DIR);
102
119
  await import_promises.default.mkdir(codacyDir, { recursive: true });
@@ -207,6 +224,91 @@ function routeFilesToTool(files, adapter, toolConfig) {
207
224
  );
208
225
  });
209
226
  }
227
+ function listStagedFiles(repositoryRoot) {
228
+ try {
229
+ const output = (0, import_child_process.execSync)("git diff --cached --name-only --diff-filter=d", {
230
+ cwd: repositoryRoot,
231
+ encoding: "utf-8",
232
+ maxBuffer: 10 * 1024 * 1024
233
+ }).trim();
234
+ return output ? output.split("\n").filter(Boolean).sort() : [];
235
+ } catch {
236
+ throw new Error("Cannot list staged files: not inside a git repository");
237
+ }
238
+ }
239
+ function listDiffFiles(repositoryRoot, base) {
240
+ const baseBranch = base ?? detectDefaultBranch(repositoryRoot);
241
+ try {
242
+ const output = (0, import_child_process.execFileSync)(
243
+ "git",
244
+ ["diff", "--name-only", "--diff-filter=d", `${baseBranch}...HEAD`],
245
+ { cwd: repositoryRoot, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
246
+ ).trim();
247
+ return output ? output.split("\n").filter(Boolean).sort() : [];
248
+ } catch {
249
+ throw new Error(
250
+ `Cannot diff against "${baseBranch}": ref not found or not inside a git repository. Specify a base explicitly with --diff <base>.`
251
+ );
252
+ }
253
+ }
254
+ function listPrFiles(repositoryRoot) {
255
+ try {
256
+ const output = (0, import_child_process.execSync)("gh pr view --json files --jq '.files[].path'", {
257
+ cwd: repositoryRoot,
258
+ encoding: "utf-8",
259
+ maxBuffer: 10 * 1024 * 1024,
260
+ timeout: 15e3
261
+ }).trim();
262
+ if (output) {
263
+ return output.split("\n").filter(Boolean).sort();
264
+ }
265
+ } catch {
266
+ }
267
+ const baseBranch = detectDefaultBranch(repositoryRoot);
268
+ try {
269
+ const mergeBase = (0, import_child_process.execFileSync)("git", ["merge-base", "HEAD", baseBranch], {
270
+ cwd: repositoryRoot,
271
+ encoding: "utf-8"
272
+ }).trim();
273
+ const output = (0, import_child_process.execFileSync)(
274
+ "git",
275
+ ["diff", "--name-only", "--diff-filter=d", `${mergeBase}...HEAD`],
276
+ { cwd: repositoryRoot, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
277
+ ).trim();
278
+ return output ? output.split("\n").filter(Boolean).sort() : [];
279
+ } catch {
280
+ throw new Error(
281
+ `Cannot determine PR files: gh CLI unavailable and git merge-base with "${baseBranch}" failed. Use --diff <base> instead.`
282
+ );
283
+ }
284
+ }
285
+ function detectDefaultBranch(repositoryRoot) {
286
+ try {
287
+ const ref = (0, import_child_process.execSync)("git rev-parse --abbrev-ref origin/HEAD", {
288
+ cwd: repositoryRoot,
289
+ encoding: "utf-8",
290
+ stdio: ["pipe", "pipe", "pipe"]
291
+ }).trim();
292
+ if (ref && ref !== "origin/HEAD") {
293
+ return ref;
294
+ }
295
+ } catch {
296
+ }
297
+ for (const branch of ["origin/main", "origin/master"]) {
298
+ try {
299
+ (0, import_child_process.execFileSync)("git", ["rev-parse", "--verify", branch], {
300
+ cwd: repositoryRoot,
301
+ encoding: "utf-8",
302
+ stdio: ["pipe", "pipe", "pipe"]
303
+ });
304
+ return branch;
305
+ } catch {
306
+ }
307
+ }
308
+ throw new Error(
309
+ "Cannot auto-detect default branch: no origin/HEAD, origin/main, or origin/master found. Specify a base explicitly with --diff <base>."
310
+ );
311
+ }
210
312
  function filterByTargetFiles(allFiles, targetPatterns) {
211
313
  if (targetPatterns.length === 0) return allFiles;
212
314
  const normalized = targetPatterns.map((p) => p.replace(/^\.\//, ""));
@@ -551,6 +653,20 @@ async function analyze(options) {
551
653
  const globalExclude = codacyConfig?.exclude || [];
552
654
  let allFiles = listRepoFiles(options.repositoryRoot);
553
655
  logger.debug("File discovery", { totalFiles: allFiles.length });
656
+ if (options.gitScope) {
657
+ const scopeFiles = resolveGitScope(options.repositoryRoot, options.gitScope);
658
+ logger.debug("Git scope resolved", {
659
+ scope: typeof options.gitScope === "string" ? options.gitScope : "diff",
660
+ fileCount: scopeFiles.length
661
+ });
662
+ if (scopeFiles.length === 0) {
663
+ logger.info("Git scope resolved to zero files, skipping analysis");
664
+ return buildResult(startedAt, options.repositoryRoot, mode, capability, [], [], []);
665
+ }
666
+ const scopeSet = new Set(scopeFiles);
667
+ allFiles = allFiles.filter((f) => scopeSet.has(f));
668
+ logger.debug("After git scope intersection", { fileCount: allFiles.length });
669
+ }
554
670
  if (options.files && options.files.length > 0) {
555
671
  allFiles = filterByTargetFiles(allFiles, options.files);
556
672
  logger.debug("Filtered by target files", { afterFilter: allFiles.length });
@@ -641,6 +757,12 @@ function normalizeIssues(issues) {
641
757
  return issue;
642
758
  });
643
759
  }
760
+ function resolveGitScope(repositoryRoot, scope) {
761
+ if (scope === "staged") return listStagedFiles(repositoryRoot);
762
+ if (scope === "pr") return listPrFiles(repositoryRoot);
763
+ const base = scope.diff === true ? void 0 : scope.diff;
764
+ return listDiffFiles(repositoryRoot, base);
765
+ }
644
766
  function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors, toolResults) {
645
767
  const completedAt = /* @__PURE__ */ new Date();
646
768
  return {
@@ -4853,9 +4975,19 @@ async function initContainerConfig(repositoryRoot, adapters2) {
4853
4975
  for (const p of entry["patterns"]) {
4854
4976
  if (typeof p === "object" && p !== null && typeof p["patternId"] === "string") {
4855
4977
  const pe = p;
4978
+ const rawParams = pe["parameters"];
4979
+ let params;
4980
+ if (rawParams && typeof rawParams === "object" && !Array.isArray(rawParams)) {
4981
+ params = Object.fromEntries(
4982
+ Object.entries(rawParams).map(([k, v]) => [
4983
+ k,
4984
+ String(v)
4985
+ ])
4986
+ );
4987
+ }
4856
4988
  patterns.push({
4857
4989
  patternId: pe["patternId"],
4858
- ...pe["parameters"] && typeof pe["parameters"] === "object" ? { parameters: pe["parameters"] } : {}
4990
+ ...params ? { parameters: params } : {}
4859
4991
  });
4860
4992
  }
4861
4993
  }
@@ -4988,6 +5120,9 @@ function createLogger(options = {}) {
4988
5120
  initDefaultConfig,
4989
5121
  initLocalConfig,
4990
5122
  initRemoteConfig,
5123
+ listDiffFiles,
5124
+ listPrFiles,
5125
+ listStagedFiles,
4991
5126
  readCodacyConfig,
4992
5127
  readCodacyYaml,
4993
5128
  registerAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/analysis-runner",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
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.1.1"
15
+ "@codacy/tooling": "0.2.1"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@codacy/openapi-typescript-codegen": "0.0.9",
19
- "@codacy/tools-dummy-1": "0.1.1"
19
+ "@codacy/tools-dummy-1": "0.2.1"
20
20
  },
21
21
  "license": "MIT",
22
22
  "author": "Codacy <support@codacy.com> (https://www.codacy.com)",