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
28 changes: 28 additions & 0 deletions .changeset/coverage-status-in-repo-metrics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
"@codacy/codacy-cloud-cli": minor
---

Show the repository's coverage **status**, not just its percentage.

Codacy now reports whether a repository's coverage is up to date, still waiting
on a report, has stopped receiving them, or was never set up — and the CLI can
tell those apart:

- `codacy repos` marks a repository whose latest commit has no report yet with a
dim `⋯` after its last known value, and shows a dim `⊘` instead of a number
for one that has stopped receiving reports. A legend under the table explains
only the states actually present in the listing.
- `codacy repo`'s Metrics section spells the same states out, with the date and
commit of the last report, and notes when a stopped repository's coverage gate
is no longer being enforced. A repository that never had coverage now reads
`Not set up` rather than a bare `N/A`.
- `codacy repo`'s Analysis row reads coverage state from the API's own status
field instead of inferring it from a separate request. This fixes repositories
that were reported as healthy while showing a stale percentage, drops one
request per run, and makes the coverage state available under a repository
token for the first time.

`--output json` gains `coverage.status`, `coverage.lastCommitWithCoverage`,
`coverage.statusUpdatedAt` and `coverage.valueUpdatedAt` on both commands. Under
a repository token, `codacy repo`'s `unavailable` array is now `["pullRequests"]`
only.
6 changes: 5 additions & 1 deletion .codacy/instructions/review.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ flag a finding when it points at a concrete defect.
- Test files are deliberately long and repetitive: fixtures are written out in
full rather than factored into builders, so each test reads standalone. **File-level
length and duplication findings on `*.test.ts` are expected** and should not be
reported.
reported. This covers suggestions to *reorganize* as well as metrics — "split
this file into focused files", "extract shared fixtures into a module" and the
like. Tests are co-located one-per-module by design (`<module>.test.ts` beside
its source), so a helper's tests belong in its module's file however long that
file grows; splitting by theme instead would break that mapping.
- Each command test builds its own bare `new Command()` harness rather than
importing `src/index.ts`. That duplication is intentional — it keeps a command's
tests independent of global CLI wiring.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Codacy accepts repository tokens on a **limited set of repository-scoped operati
| `repository`, `repository --reanalyze` | `repository --add`/`--remove`/`--follow`/`--unfollow`/`--link-standard`/`--unlink-standard` |
| | `pull-request`, `pull-requests`, `ls`, `directories`, `findings`, `finding` |

`codacy repository` works, but omits the pull request and coverage-report sections — those endpoints don't accept repository tokens. In `--output json` it marks them as `"unavailable": ["pullRequests", "coverageReports"]`, so a consumer can tell "none" apart from "couldn't look". Note that skipping coverage reports also suppresses the "waiting for / missing coverage reports" hint on the Analysis row.
`codacy repository` works, but omits the pull request section — that endpoint doesn't accept repository tokens. In `--output json` it marks it as `"unavailable": ["pullRequests"]`, so a consumer can tell "none" apart from "couldn't look". Everything else, including the coverage status, is available.

`codacy login` stores account tokens only; pass repository tokens per command or via `CODACY_PROJECT_TOKEN`.

Expand Down
5 changes: 3 additions & 2 deletions SPECS/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ _No pending tasks._ All commands implemented.
| Command | Alias | Status | Spec |
|---|---|---|---|
| `info` | `inf` | ✅ Done | [info.md](commands/info.md) |
| `repositories` | `repos` | ✅ Done | [repositories.md](commands/repositories.md) |
| `repository` | `repo` | ✅ Done (actions added) | [repository.md](commands/repository.md) |
| `repositories` | `repos` | ✅ Done (coverage status added) | [repositories.md](commands/repositories.md) |
| `repository` | `repo` | ✅ Done (actions + coverage status added) | [repository.md](commands/repository.md) |
| `ls` | N/A | ✅ Done | [ls.md](commands/ls.md) |
| `directories` | `dirs` | ✅ Done | [directories.md](commands/directories.md) |
| `pull-request` | `pr` | ✅ Done (--diff + Diff Coverage Summary added) | [pull-request.md](commands/pull-request.md) |
Expand Down Expand Up @@ -88,3 +88,4 @@ _No pending tasks._ All commands implemented.
| 2026-08-11 | (OD-489) Repository (project) token support. New `--repository-token <token>` on every command (plus `CODACY_PROJECT_TOKEN`), sent as the `project-token` header; account tokens keep `api-token`. `src/utils/auth.ts` rewritten around a `RemoteAuth` discriminated union carrying both kind and source, replacing `checkApiToken()` with `resolveAuth(this)` / `resolveAccountAuth(this, why)` / `requireAccountToken(...)` / `fetchIfAccountToken(...)`. Precedence matches `codacy-analysis` exactly — flag > `CODACY_PROJECT_TOKEN` > `CODACY_API_TOKEN` > stored login — so `vitest.config.mts` now blanks `CODACY_PROJECT_TOKEN` (it outranks the account token and is exported job-wide by the coverage reporter, so tests would otherwise depend on the developer's shell). Codacy whitelists only 13 operations for repository tokens, so `tool`/`patterns`/`pattern` work unchanged, `issues` (incl. `--overview`) and `tools --import` work, and the 9 account-only commands plus `repository`'s 6 management flags, `issues --ignore`/`--ignored`, and `tools --import --force` (only when standards exist) **fail fast before any request** with a message naming the operation, the reason, and where the token came from. `repository`'s dashboard skips the two non-whitelisted calls: the table keeps the "Open Pull Requests" header with an explanatory line, and JSON keeps `pullRequests: []` (so `jq '.pullRequests[]'` still works) plus an additive `unavailable: ["pullRequests"]` — under an account token the payload is byte-identical. Also added the long-missing `.catch()` on the PR call so an account token lacking PR access degrades instead of losing the whole dashboard, and fixed `login`'s 401 message, which told repository-token users their token was "invalid" when it is rejected by `/user` by design. New `SPECS/repository-tokens.md` (whitelist + matrix, re-verify on every `npm run update-api`) and `SPECS/missing-endpoints.md` (ranked gaps for follow-up Linear tasks) (40 new tests, 606 total) |
| 2026-09-07 | HTTP/HTTPS proxy + TLS support (issue #40). Node's global `fetch` — used by the generated client and the MITRE CVE lookup in `commands/finding.ts` — ignores `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY`, so the CLI was unusable behind a corporate proxy. Rather than reimplement it, this delegates to `configureProxy()` from `@codacy/tooling` (pinned `0.1.0` → `0.23.0`, the same function `analysis-cli` calls), which installs a global `undici` dispatcher doing per-request protocol + `NO_PROXY` routing, bare `host:port` normalization, and `SSL_CERT_FILE`/`NODE_EXTRA_CA_CERTS` CA loading. New `src/utils/proxy.ts` is a ~4-line seam — `configureProxyFromEnv()` calls it and routes its deliberate fail-loud throw (unreadable/non-PEM CA bundle) into `handleError()`, giving red `Error: <message>` and exit 1 like every other failure here; `analysis-cli` exits 2 because it has a documented exit-code scheme, which this CLI does not. Called at the top of `src/index.ts`, above `OpenAPI.BASE` (ordering is only constrained to precede `program.parse`, since the dispatcher is resolved per request). Kept top-level rather than in the `preAction` hook so a typo'd `SSL_CERT_FILE` fails even on `--version`. Deliberately zero-argument: env is the sole input, which is what keeps parity exact. Superseded external PR #39, which hand-rolled the same feature with `undici@8.10.1` — that requires Node ≥ 22.19.0 against this package's `engines: ">=20"`, so `require("undici")` threw at module load and the CLI would not start at all on any Node 20.x; tooling's `undici@^6.21.0` supports Node ≥ 18.17. That regression passed CI, so `ci.yml` gained a smoke step running the built entry point (plain, with `HTTPS_PROXY`, and with a bad `SSL_CERT_FILE` expected to fail) — previously nothing executed `src/index.ts`, since every command test builds a bare `new Command()`. Upstream owns the proxy semantics and their 24 tests, so only the seam is tested here. Pinned exactly rather than with a caret: for a pre-1.0 package `^0.22.0` spans patches only (`>=0.22.0 <0.23.0-0`), so a caret would have bought silent patch drift against a dependency this repo has no proxy coverage for, without ever picking up a minor. Two findings from this work were fixed upstream and taken here via 0.23.0 — `undici` now loads lazily behind `configureProxy`'s early-out (an unproxied `--version` went from +27 ms to +0 ms against a `main` build), and a malformed proxy URL now fails with `Invalid HTTPS_PROXY value "...": <reason>`, naming the setting and redacting any credentials instead of surfacing a bare `Invalid URL` (4 new tests, 614 total) |
| 2026-09-09 | New `-k, --matches-stack [value]` filter on `patterns`, surfacing the API's `matchesStack` query param (filter a tool's code patterns by whether they match the repository's detected stack). Tri-state, matching the existing `issues --false-positives`: the bare flag or `true` sends `matchesStack=true`, `false` sends `matchesStack=false`, omitting it sends nothing — read explicitly rather than by truthiness so an explicit `false` stays distinct from "not requested". Applies in **both** list mode (`listRepositoryToolPatterns`) and bulk mode (`updateRepositoryToolPatterns`), like every other filter; the post-update `toolPatternsOverview` call deliberately stays unfiltered, since its counts describe the whole tool rather than the updated subset. The shared tri-state coercion `parseBooleanOption` moved out of `issues.ts` into a new `utils/options.ts` (+ tests) and is now imported by both commands. **Required an API bump: pinned `57.3.9` → `57.4.17`** (`matchesStack` first ships in `57.4.14`; `57.4.17` is the latest published build). The spec delta is purely additive — 2 unused new operations, 4 new schemas, `stackTagsFilterParam` on `listOrganizationRepositories` (unused; the CLI calls `...WithAnalysis`) — but `matchesStack` is inserted *mid-signature* on `listRepositoryToolPatterns` (arg 12, before `sort`), so every full-positional-arg assertion in `patterns.test.ts` gained a trailing `undefined`; `pattern.ts`/`issues.ts` stop at `search` (arg 9) and were unaffected. `SPECS/repository-tokens.md` re-verified: `57.4.x` now declares the `ProjectTokenAuth` scheme in the spec (it was absent in `57.3.9`), making the whitelist machine-checkable, and it is **14** operations, not 13 — the addition is `searchAiInventoryCategories`, unused here. `patterns` stays fully whitelisted, so no new token guard (11 new tests, 625 total) |
| 2026-09-10 | Coverage **status** surfaced in `repositories` and `repository`, from the API's new `Coverage.status` ([`CoverageStatus`](https://api.codacy.com/api/api-docs#tocs_coveragestatus): `None`/`UpToDate`/`Waiting`/`Stopped`), mirroring codacy-spa#3110. **No API bump** — pinned `57.4.17` already ships the field. It rides on `Coverage`, embedded only in `RepositoryWithAnalysis`, so these two commands are the only places it can appear; `ls`/`directories` (flat `coverageWithDecimals`) and `pull-request`/`pull-requests` (`PullRequestCoverage`/`DiffCoverage`) carry no status. The payload shapes differ in more than `status`, which is what drove the rendering: `Waiting` returns a **stale** percentage (from `lastCommitWithCoverage`, `valueUpdatedAt` older than `statusUpdatedAt`), `Stopped` returns **no percentage at all**, `None` returns nothing but the status, and `status` is `undefined` on a large share of repositories. **`repositories`:** a dim `⋯` after a `Waiting` value, a dim `⊘` *instead of* a `Stopped` value, and a legend under the table carrying only the statuses actually present (`coverageStatusLegend`). Glyphs follow the existing vocabulary — `⋯` is already `formatStandards`'s "not final yet" marker, `⊘` shares the Mathematical Operators block with the `⊙` public-repo marker — no emojis. **`repository`:** the Metrics row spells the state out (`Not reported yet for the latest commit — value from 11h ago (5474cbf)` / `Stopped receiving reports 2026-08-26 — last report 8752dbd`, plus `— coverage gate no longer enforced` when `goals.minCoveragePercentage` is set / dim `Not set up` for `None`, which a bare `N/A` could never distinguish from an uncomputed metric), colored on `formatAnalysisStatus`'s existing scale (blueBright = in-flight, yellow = attention, dim = nothing there). **Analysis row rewritten:** `formatAnalysisStatus` gained an authoritative `coverageStatus` that wins over its `expectsCoverage`/`hasCoverageData` heuristic, extracted into `coverageAnalysisSuffix`. The heuristic was *wrong* for `Waiting` — a waiting repo still reports a (stale) percentage, so `hasCoverageData` was true and the row said nothing in exactly the case worth surfacing — and vague for `Stopped` ("Missing coverage reports"). That let `repository` **drop its `listCoverageReports` call entirely** (5 parallel requests → 4), which in turn means repository-token users get the coverage state for the first time (`getRepositoryWithAnalysis` is whitelisted, `listCoverageReports` is not) and `unavailable` is now `["pullRequests"]` alone. `pull-request` keeps the heuristic — its coverage models have no status field — and is untouched. Accepted trade-off: with `status` undefined, `repository`'s Analysis row now shows no coverage hint where the heuristic might have said "Missing coverage reports". Also de-duplicated `repositories.ts`'s local `formatMetric` (a stale copy of the shared `colorMetric` returning a bare `"N/A"`), which would otherwise have let coverage and complexity/duplication drift inside the same table. Five new helpers in `utils/formatting.ts` (`coverageStatusGlyph`/`formatRepoCoverageCell`/`coverageStatusLegend`/`coverageStatusNote`/`formatRepoCoverageDetail`); `formatCoverageCell` deliberately untouched — it renders file/folder coverage, which has no status. JSON gains `coverage.status`/`lastCommitWithCoverage`/`statusUpdatedAt`/`valueUpdatedAt` on both commands (`valueUpdatedAt` is what tells a consumer the `Waiting` value is stale — the job the glyph does in the table); `pickDeep` drops undefined, so a `None` repo emits `{"status":"None"}` and a status-less one gains no keys (39 new tests, 664 total) |
Loading
Loading