Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/issues-overview-noise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@codacy/codacy-cloud-cli": minor
---

Improve `issues --overview`. The False Positives table now uses human-friendly labels ("Not a False Positive" / "Potential False Positive") instead of the raw `belowThreshold` / `equalOrAboveThreshold` API bucket names. The overview also adds a "Suggested actions to reduce noise" section that flags noisy patterns — those accounting for at least 10% of all issues, or at least 3× the average issues-per-pattern — and prints a ready-to-run `codacy pattern <tool> <patternId> --disable` command for each (the owning tool is resolved automatically; suggestions whose tool can't be resolved are omitted). `--output json` output is unchanged.
10 changes: 10 additions & 0 deletions .changeset/pattern-config-file-awareness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@codacy/codacy-cloud-cli": minor
---

Make the pattern commands aware of local configuration files and coding standards.

- `pattern <tool> <patternId>` with no action flag now **shows the pattern's information** (same card as the `patterns` command, with `--output json` support). Since there's no single-pattern endpoint, it searches by ID and keeps the exact match.
- When a tool is driven by a local configuration file, `patterns` (list) and `pattern` (info) print `<tool> is using a local configuration file.` and skip fetching patterns; `patterns --enable-all/--disable-all` and `pattern --enable/--disable/--parameter` refuse with `Tool uses a local configuration file, can't be updated.`
- `pattern --enable/--disable/--parameter` also refuses patterns enforced by a coding standard with `Pattern enforced by <standard> coding standard, can't be modified.`
- `issues --overview` noise suggestions now adapt per pattern: a runnable `codacy pattern … --disable` command when possible, otherwise a manual step — `Update your local <tool> configuration file to disable the pattern` or `Update <coding standard> to disable the pattern`.
5 changes: 5 additions & 0 deletions .changeset/reanalyze-and-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@codacy/codacy-cloud-cli": minor
---

Add a `--reanalyze-and-wait` (`-w`) variant to the `repository` and `pull-request` commands. Unlike `--reanalyze` (which triggers analysis and exits), this blocking variant captures a baseline of the current issues, triggers the reanalysis, polls until it finishes (every 10s, up to 20 minutes), and then prints how long the analysis took and what changed — issue deltas by pattern, severity, and category. Supports `--output json`.
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ codacy-cloud-cli/
- `dayjs` for date formatting — for "last updated" style dates, use `formatFriendlyDate()` from `utils/output.ts` (relative for today, "Yesterday", otherwise YYYY-MM-DD)
- **Output:** Default output is human readable with tables and colors, but can be overridden with the `--output json` flag.
- **Pagination:** All commands calling paginated APIs must call `printPaginationWarning(response.pagination, hint)` from `utils/output.ts` after displaying results. The hint should suggest command-specific filtering options.
- **Polling / waiting:** Commands that wait on a remote operation (e.g. `--reanalyze-and-wait`) use the shared helpers in `utils/reanalyze-wait.ts`.
- Route polling delays through the exported `timers.sleep` so tests can stub it (`vi.spyOn(timers, "sleep").mockResolvedValue()`).
- Prefer that over calling `setTimeout`/`sleep` directly in a command, unless you have a clear reason not to.
- Default cadence is `POLL_INTERVAL_MS` (10s), capped at `MAX_WAIT_MS` (20min).
- **Error handling:** Use `try/catch` with the shared `handleError()` from `src/utils/error.ts`
- **Authentication:** All commands that call the API must call `checkApiToken()` from `src/utils/auth.ts` before making requests
- **API base URL:** `https://app.codacy.com/api/v3` (configured in `src/index.ts` via `OpenAPI.BASE`)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,16 @@ Supported providers: GitHub (`gh`), GitLab (`gl`), Bitbucket (`bb`).
| `logout` | Remove stored Codacy API token |
| `info` | Show authenticated user info and their organizations |
| `repositories <provider> <org>` | List repositories for an organization |
| `repository [provider] [org] [repo]` | Show metrics for a repository, or add/remove/follow/unfollow/reanalyze it |
| `repository [provider] [org] [repo]` | Show metrics for a repository, or add/remove/follow/unfollow/reanalyze it (optionally waiting for results) |
| `issues [provider] [org] [repo]` | Search issues in a repository with filters |
| `issue [provider] [org] [repo] <id>` | Show details for a single issue, or ignore/unignore it |
| `findings [provider] [org] [repo]` | Show security findings for a repository or organization |
| `finding <provider> <org> <id>` | Show details for a single security finding, or ignore/unignore it |
| `pull-request [provider] [org] [repo] <pr>` | Show PR analysis, issues, diff coverage, and changed files; or reanalyze it |
| `pull-request [provider] [org] [repo] <pr>` | Show PR analysis, issues, diff coverage, and changed files; or reanalyze it (optionally waiting for results) |
| `tools [provider] [org] [repo]` | List analysis tools configured for a repository |
| `tool [provider] [org] [repo] <tool>` | Enable, disable, or configure an analysis tool |
| `patterns [provider] [org] [repo] <tool>` | List patterns for a tool, or bulk enable/disable them |
| `pattern [provider] [org] [repo] <tool> <id>` | Enable, disable, or set parameters for a pattern |
| `pattern [provider] [org] [repo] <tool> <id>` | Show a pattern, or enable, disable, or set parameters for it |

Run `codacy <command> --help` for full argument and option details for any command.

Expand Down
5 changes: 4 additions & 1 deletion SPECS/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ _No pending tasks._ All commands implemented.
| `tools` | `tls` | ✅ Done | [tools-and-patterns.md](commands/tools-and-patterns.md) |
| `tool` | `tl` | ✅ Done | [tools-and-patterns.md](commands/tools-and-patterns.md) |
| `patterns` | `pats` | ✅ Done | [tools-and-patterns.md](commands/tools-and-patterns.md) |
| `pattern` | `pat` | ✅ Done | [tools-and-patterns.md](commands/tools-and-patterns.md) |
| `pattern` | `pat` | ✅ Done (info mode + guards added) | [tools-and-patterns.md](commands/tools-and-patterns.md) |
| `analysis` | N/A | ✅ Done | [analysis.md](commands/analysis.md) |
| `json-output` | N/A | ✅ Done | [json-output.md](commands/json-output.md) |
| `login` | N/A | ✅ Done | — |
Expand Down Expand Up @@ -65,3 +65,6 @@ _No pending tasks._ All commands implemented.
| 2026-03-05 | JSON output filtering with `pickDeep` across all commands: `info`, `repositories`, `repository`, `pull-request`, `issues`, `issue`, `findings`, `finding`, `tools`, `patterns`; documented pattern in `src/commands/CLAUDE.md` |
| 2026-03-12 | `patterns --enable-all` / `--disable-all` bulk update with filter support (6 new tests, 196 total) |
| 2026-03-12 | `login` and `logout` commands: encrypted token storage in `~/.codacy/credentials`, masked interactive prompt, `--token` flag for non-interactive use, token resolution chain (env var → stored credentials); `checkApiToken()` updated to set `OpenAPI.HEADERS` dynamically (9 new tests, 219 total) |
| 2026-06-02 | `--reanalyze-and-wait` (`-w`) blocking variant for `repository` and `pull-request`: triggers reanalysis, polls to completion (10s interval, 20min cap), then prints issue deltas by pattern/severity/category. New `src/utils/reanalyze-wait.ts` + `formatDuration`/`isBeingAnalyzed` helpers (26 new tests, 356 total) |
| 2026-06-02 | `issues --overview` improvements: relabel False Positives buckets (`belowThreshold`/`equalOrAboveThreshold` → "Not a False Positive"/"Potential False Positive"), and a "Suggested actions to reduce noise" section that flags noisy patterns (≥10% of issues or ≥3× the average) with a runnable `codacy pattern … --disable` command, resolving the tool via its `prefix` (3 new tests, 360 total) |
| 2026-06-02 | Pattern config-file & coding-standard awareness: new `pattern <tool> <id>` **info mode** (same card as `patterns`); `pattern`/`patterns` skip listing and refuse updates when a tool uses a local config file; `pattern` refuses to modify coding-standard-enforced patterns; `issues --overview` noise suggestions now render a manual "update your config file / coding standard" step instead of a command when a pattern can't be disabled via CLI. `printPatternCard`/`PATTERN_JSON_FIELDS` moved to `utils/formatting.ts` (11 new tests, 371 total) |
63 changes: 60 additions & 3 deletions SPECS/commands/analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,54 @@ codacy pull-request <provider> <organization> <repository> <prNumber> --reanalyz
On success, show: "Reanalysis requested successfully, new results will be available in a few minutes."
On failure, show: "Failed to request reanalysis: \<error message\>".

### `--reanalyze-and-wait` (`-w`) — blocking variant

A second variant triggers the reanalysis and **waits** for it to complete, then
prints what changed. The fire-and-forget `--reanalyze` stays as-is.

```
codacy repository <provider> <organization> <repository> --reanalyze-and-wait
codacy pull-request <provider> <organization> <repository> <prNumber> --reanalyze-and-wait
```

Flow:
1. Capture a **baseline** of current issues — for the repository from
`issuesOverview` (counts by severity / category / pattern as independent
lists); for the pull request by paging `listPullRequestIssues(status="new")`
(each issue carries its pattern's category + severity).
2. Trigger `reanalyzeCommitById` on the HEAD commit.
3. **Poll every 10 s** (giving up after **20 min**), with the spinner showing
`"Analysis requested. Waiting for it to start..."` → `"Analysis in progress.
This may take a few minutes..."` → `"Analysis done. Fetching results to
compare..."`. On each poll, read the **first commit** from the `/commits`
endpoint (`listRepositoryCommits` for the repo, `getPullRequestCommits` for
the PR, both `limit=1`) and look at its `startedAnalysis`/`endedAnalysis`:
- **In progress** = `startedAnalysis` more recent than `endedAnalysis` **and**
more recent than `t0` (the moment we triggered) — i.e. the analysis that
started is ours, not a previous one.
- **Done** = `startedAnalysis` more recent than `t0` **and** `endedAnalysis`
at or after `startedAnalysis` (our analysis has since finished).
4. Fetch a fresh snapshot, diff against the baseline, and print:
- `Analysis finished in <duration>` headline (from the commit's
`startedAnalysis`→`endedAnalysis`, falling back to wall-clock).
- **By pattern / By severity / By category** signed net-delta lists. PR
pattern rows are annotated `(Category · Severity)`; repo pattern rows are
title-only (the overview doesn't map patterns to category/severity).
- `In total: <before> → <after> issues (net ±N)`.

Notes:
- The overview only exposes **net** per-bucket change, so deltas are signed net
values per dimension, not a literal "added vs removed" split or a
severity×category cross-tab.
- The pattern list is soft-capped at 20 rows with a `… (N more)` line.
- `--output json` emits `{ durationMs, durationHuman, totals, deltas }`.
- On timeout, the spinner fails with a "didn't finish within 20 minutes" message.

Shared logic lives in `src/utils/reanalyze-wait.ts` (`pollForAnalysis`,
`snapshotFromOverview`, `snapshotFromPrIssues`, `diffSnapshots`,
`renderReanalyzeReport`, `reanalyzeJson`, and a `timers.sleep` indirection that
tests stub for instant polling). `formatDuration` lives in `utils/formatting.ts`.

## Updates to existing commands

### `repository` command — About section
Expand Down Expand Up @@ -69,6 +117,13 @@ Implemented in `formatAnalysisStatus()` in `src/utils/formatting.ts`.
- [`listRepositoryCommits`](https://api.codacy.com/api/api-docs#listrepositorycommits) with `limit=1` — head commit timing for repo
- [`listCoverageReports`](https://api.codacy.com/api/api-docs#listcoveragereports) with `limit=1` — check `hasCoverageOverview`

Additionally used by `--reanalyze-and-wait`:
- `listRepositoryCommits` (`limit=1`) — repo first-commit analysis timestamps, polled for status
- `getPullRequestCommits` (`limit=1`) — PR first-commit analysis timestamps, polled for status
- `getRepositoryPullRequest` — fetched once to resolve the PR `headCommitSha`
- `issuesOverview` — repo baseline/after issue counts (severity / category / pattern)
- `listPullRequestIssues` (`status="new"`, paginated) — PR baseline/after issue list

## Tasks

- [x] Update analysis status in the About section of the `repository` command
Expand All @@ -77,9 +132,11 @@ Implemented in `formatAnalysisStatus()` in `src/utils/formatting.ts`.
- [x] Add `--reanalyze` option to the `pull-request` command
- [x] Update existing tests for the status sections
- [x] Add tests for the new `--reanalyze` option
- [x] Add `--reanalyze-and-wait` (`-w`) blocking variant to both commands (2026-06-02)

## Tests

- `src/utils/formatting.test.ts` — 6 unit tests for `formatAnalysisStatus`
- `src/commands/repository.test.ts` — 4 new tests (analysis status, reanalyze)
- `src/commands/pull-request.test.ts` — 3 new tests (analysis status, reanalyze)
- `src/utils/formatting.test.ts` — 6 unit tests for `formatAnalysisStatus`; + `formatDuration` and `isBeingAnalyzed` tests
- `src/commands/repository.test.ts` — 4 tests (analysis status, reanalyze) + 3 for `--reanalyze-and-wait`
- `src/commands/pull-request.test.ts` — 3 tests (analysis status, reanalyze) + 3 for `--reanalyze-and-wait`
- `src/utils/reanalyze-wait.test.ts` — 12 unit tests (snapshots, diff, poll loop incl. timeout, render, json)
47 changes: 44 additions & 3 deletions SPECS/commands/issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ codacy is gh my-org my-repo --output json

- [`searchRepositoryIssues`](https://api.codacy.com/api/api-docs#searchrepositoryissues) — `AnalysisService.searchRepositoryIssues(provider, org, repo, cursor, limit, body)`
- [`issuesOverview`](https://api.codacy.com/api/api-docs#issuesoverview) — `AnalysisService.issuesOverview(provider, org, repo, body)` (only when `--overview` is given)
- [`listTools`](https://api.codacy.com/api/api-docs#listtools) — `ToolsService.listTools(cursor, limit)` (only when `--overview` surfaces noisy patterns, to map each pattern's `prefix` to its owning tool)
- [`listRepositoryTools`](https://api.codacy.com/api/api-docs#listrepositorytools) — `AnalysisService.listRepositoryTools(provider, org, repo)` (only when `--overview` surfaces noisy patterns, to detect config-file-driven tools)
- [`listRepositoryToolPatterns`](https://api.codacy.com/api/api-docs#listrepositorytoolpatterns) — `search=<patternId>` (only for noisy patterns on non-config-file tools, to detect coding-standard enforcement)

Both accept the same `SearchRepositoryIssuesBody` for filtering.
`searchRepositoryIssues` and `issuesOverview` accept the same `SearchRepositoryIssuesBody` for filtering.

## Options

Expand Down Expand Up @@ -66,8 +69,46 @@ Shows pagination warning if more results exist.

### Overview mode (`--overview`)

Six count tables sorted descending by count: Category, Severity, Language, Tag, Pattern, Author.
Seven count tables sorted descending by count: Category, Severity, Language, Tag,
Pattern, Author, and False Positives.

The **False Positives** table relabels the API's raw bucket names for readability:
`belowThreshold` → "Not a False Positive", `equalOrAboveThreshold` → "Potential
False Positive" (the bucket is keyed on FP probability vs. the configured
threshold, so at/above threshold = a potential false positive).

After the tables, a **"Suggested actions to reduce noise"** section lists patterns
worth disabling. A pattern is "noisy" when it accounts for **≥10% of all issues**
shown, **or** has **≥3× the average** issues-per-pattern. The owning tool is
resolved by matching the pattern ID against each tool's `prefix` (longest match
wins); patterns whose tool can't be resolved (no/unknown prefix) are dropped
silently. The list is capped at 10 with a "… (N more)" note.

The suggested step depends on **how the pattern is managed**, since not every
pattern can be disabled through the CLI:

```
Suggested actions to reduce noise

Disable "Use of assert detected" (-2.5k issues)
> codacy pattern Bandit Bandit_B101 --disable
```

- **Default** — a runnable `> codacy pattern <tool> <patternId> --disable` command.
- **Tool uses a local configuration file** — no command; instead
`→ Update your local <tool> configuration file to disable the pattern`.
- **Pattern enforced by a coding standard** — no command; instead
`→ Update <standard name(s)> to disable the pattern`.

To classify each noisy pattern, the command additionally fetches the repository
tools (`listRepositoryTools`, for `usesConfigurationFile` and the repo tool
UUID) and, for non-config-file tools, the pattern's `enabledBy` via
`listRepositoryToolPatterns` (`search=<patternId>`, one call per noisy pattern).
A config file takes precedence over coding-standard enforcement. These extra
calls only run when at least one noisy pattern exists.

`--output json` is unaffected (raw counts only — no relabeling or suggestions).

## Tests

File: `src/commands/issues.test.ts` — 39 tests.
File: `src/commands/issues.test.ts` — 46 tests.
Loading
Loading