@codacy/analysis-runner 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -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,
@@ -207,6 +210,91 @@ function routeFilesToTool(files, adapter, toolConfig) {
207
210
  );
208
211
  });
209
212
  }
213
+ function listStagedFiles(repositoryRoot) {
214
+ try {
215
+ const output = (0, import_child_process.execSync)("git diff --cached --name-only --diff-filter=d", {
216
+ cwd: repositoryRoot,
217
+ encoding: "utf-8",
218
+ maxBuffer: 10 * 1024 * 1024
219
+ }).trim();
220
+ return output ? output.split("\n").filter(Boolean).sort() : [];
221
+ } catch {
222
+ throw new Error("Cannot list staged files: not inside a git repository");
223
+ }
224
+ }
225
+ function listDiffFiles(repositoryRoot, base) {
226
+ const baseBranch = base ?? detectDefaultBranch(repositoryRoot);
227
+ try {
228
+ const output = (0, import_child_process.execFileSync)(
229
+ "git",
230
+ ["diff", "--name-only", "--diff-filter=d", `${baseBranch}...HEAD`],
231
+ { cwd: repositoryRoot, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
232
+ ).trim();
233
+ return output ? output.split("\n").filter(Boolean).sort() : [];
234
+ } catch {
235
+ throw new Error(
236
+ `Cannot diff against "${baseBranch}": ref not found or not inside a git repository. Specify a base explicitly with --diff <base>.`
237
+ );
238
+ }
239
+ }
240
+ function listPrFiles(repositoryRoot) {
241
+ try {
242
+ const output = (0, import_child_process.execSync)("gh pr view --json files --jq '.files[].path'", {
243
+ cwd: repositoryRoot,
244
+ encoding: "utf-8",
245
+ maxBuffer: 10 * 1024 * 1024,
246
+ timeout: 15e3
247
+ }).trim();
248
+ if (output) {
249
+ return output.split("\n").filter(Boolean).sort();
250
+ }
251
+ } catch {
252
+ }
253
+ const baseBranch = detectDefaultBranch(repositoryRoot);
254
+ try {
255
+ const mergeBase = (0, import_child_process.execFileSync)("git", ["merge-base", "HEAD", baseBranch], {
256
+ cwd: repositoryRoot,
257
+ encoding: "utf-8"
258
+ }).trim();
259
+ const output = (0, import_child_process.execFileSync)(
260
+ "git",
261
+ ["diff", "--name-only", "--diff-filter=d", `${mergeBase}...HEAD`],
262
+ { cwd: repositoryRoot, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
263
+ ).trim();
264
+ return output ? output.split("\n").filter(Boolean).sort() : [];
265
+ } catch {
266
+ throw new Error(
267
+ `Cannot determine PR files: gh CLI unavailable and git merge-base with "${baseBranch}" failed. Use --diff <base> instead.`
268
+ );
269
+ }
270
+ }
271
+ function detectDefaultBranch(repositoryRoot) {
272
+ try {
273
+ const ref = (0, import_child_process.execSync)("git rev-parse --abbrev-ref origin/HEAD", {
274
+ cwd: repositoryRoot,
275
+ encoding: "utf-8",
276
+ stdio: ["pipe", "pipe", "pipe"]
277
+ }).trim();
278
+ if (ref && ref !== "origin/HEAD") {
279
+ return ref;
280
+ }
281
+ } catch {
282
+ }
283
+ for (const branch of ["origin/main", "origin/master"]) {
284
+ try {
285
+ (0, import_child_process.execFileSync)("git", ["rev-parse", "--verify", branch], {
286
+ cwd: repositoryRoot,
287
+ encoding: "utf-8",
288
+ stdio: ["pipe", "pipe", "pipe"]
289
+ });
290
+ return branch;
291
+ } catch {
292
+ }
293
+ }
294
+ throw new Error(
295
+ "Cannot auto-detect default branch: no origin/HEAD, origin/main, or origin/master found. Specify a base explicitly with --diff <base>."
296
+ );
297
+ }
210
298
  function filterByTargetFiles(allFiles, targetPatterns) {
211
299
  if (targetPatterns.length === 0) return allFiles;
212
300
  const normalized = targetPatterns.map((p) => p.replace(/^\.\//, ""));
@@ -551,6 +639,20 @@ async function analyze(options) {
551
639
  const globalExclude = codacyConfig?.exclude || [];
552
640
  let allFiles = listRepoFiles(options.repositoryRoot);
553
641
  logger.debug("File discovery", { totalFiles: allFiles.length });
642
+ if (options.gitScope) {
643
+ const scopeFiles = resolveGitScope(options.repositoryRoot, options.gitScope);
644
+ logger.debug("Git scope resolved", {
645
+ scope: typeof options.gitScope === "string" ? options.gitScope : "diff",
646
+ fileCount: scopeFiles.length
647
+ });
648
+ if (scopeFiles.length === 0) {
649
+ logger.info("Git scope resolved to zero files, skipping analysis");
650
+ return buildResult(startedAt, options.repositoryRoot, mode, capability, [], [], []);
651
+ }
652
+ const scopeSet = new Set(scopeFiles);
653
+ allFiles = allFiles.filter((f) => scopeSet.has(f));
654
+ logger.debug("After git scope intersection", { fileCount: allFiles.length });
655
+ }
554
656
  if (options.files && options.files.length > 0) {
555
657
  allFiles = filterByTargetFiles(allFiles, options.files);
556
658
  logger.debug("Filtered by target files", { afterFilter: allFiles.length });
@@ -641,6 +743,12 @@ function normalizeIssues(issues) {
641
743
  return issue;
642
744
  });
643
745
  }
746
+ function resolveGitScope(repositoryRoot, scope) {
747
+ if (scope === "staged") return listStagedFiles(repositoryRoot);
748
+ if (scope === "pr") return listPrFiles(repositoryRoot);
749
+ const base = scope.diff === true ? void 0 : scope.diff;
750
+ return listDiffFiles(repositoryRoot, base);
751
+ }
644
752
  function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors, toolResults) {
645
753
  const completedAt = /* @__PURE__ */ new Date();
646
754
  return {
@@ -4988,6 +5096,9 @@ function createLogger(options = {}) {
4988
5096
  initDefaultConfig,
4989
5097
  initLocalConfig,
4990
5098
  initRemoteConfig,
5099
+ listDiffFiles,
5100
+ listPrFiles,
5101
+ listStagedFiles,
4991
5102
  readCodacyConfig,
4992
5103
  readCodacyYaml,
4993
5104
  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.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.1.1"
15
+ "@codacy/tooling": "0.2.0"
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.0"
20
20
  },
21
21
  "license": "MIT",
22
22
  "author": "Codacy <support@codacy.com> (https://www.codacy.com)",