Status: ✅ Done (2026-03-05)
Show analysis status of the HEAD commit in the repository and pull-request commands, and allow triggering reanalysis.
codacy repository <provider> <organization> <repository> --reanalyze
codacy pull-request <provider> <organization> <repository> <prNumber> --reanalyze
On success, show: "Reanalysis requested successfully, new results will be available in a few minutes." On failure, show: "Failed to request reanalysis: <error message>".
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:
- Capture a baseline of current issues — for the repository from
issuesOverview(counts by severity / category / pattern as independent lists); for the pull request by paginglistPullRequestIssues(status="new")(each issue carries its pattern's category + severity). - Trigger
reanalyzeCommitByIdon the HEAD commit. - 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/commitsendpoint (listRepositoryCommitsfor the repo,getPullRequestCommitsfor the PR, bothlimit=1) and look at itsstartedAnalysis/endedAnalysis:- In progress =
startedAnalysismore recent thanendedAnalysisand more recent thant0(the moment we triggered) — i.e. the analysis that started is ours, not a previous one. - Done =
startedAnalysismore recent thant0andendedAnalysisat or afterstartedAnalysis(our analysis has since finished).
- In progress =
- Fetch a fresh snapshot, diff against the baseline, and print:
Analysis finished in <duration>headline (from the commit'sstartedAnalysis→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 jsonemits{ 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.
The "Last Analysis" row is replaced by "Analysis", showing the current analysis status of the HEAD commit:
- Reanalysis in progress (HEAD commit already analyzed, but currently being reanalyzed):
Analysis Finished 12h ago (c00e638) — Reanalysis in progress...
- First analysis (HEAD commit not yet analyzed):
Analysis In progress... (c00e638)
- Analysis finished, no coverage report for the latest commit yet
(
coverage.status === "Waiting"):
Analysis Finished 12h ago (c00e638) — Waiting for coverage reports...
- Analysis finished, coverage reports stopped arriving
(
coverage.status === "Stopped"):
Analysis Finished 12h ago (c00e638) — Stopped receiving coverage reports
- Normal finished state (
UpToDate,None, or no status at all):
Analysis Finished 12h ago (c00e638)
"In progress...", "Reanalysis in progress..." and "Waiting for coverage reports..." are colored light blue. "Stopped receiving coverage reports" (and the pull-request-only "Missing coverage reports") are yellow.
The row deliberately stays short: the Metrics section's Coverage row carries the same state with its dates and commit, so the dashboard states it at two altitudes rather than saying the same sentence twice. See repository.md.
Same "Analysis" row replaces the former "Head Commit" row, with the same status
logic applied to the PR's HEAD commit — except for the coverage state, which
still comes from the heuristic below. PullRequestCoverage/DiffCoverage carry
no status field, so there is nothing authoritative to read. This is the only
remaining caller of the heuristic, and the only reason it still exists.
- Coverage expected, none yet (within 3h):
— Waiting for coverage reports... - Coverage expected, overdue (>3h):
— Missing coverage reports
- Being analyzed:
startedAnalysisis set AND (endedAnalysisis absent ORstartedAnalysis > endedAnalysis)
The coverage half is decided by coverageAnalysisSuffix(), which has two
sources in priority order:
-
coverageStatus— the API's ownCoverage.status, available on the repository endpoints (getRepositoryWithAnalysis). Authoritative, so it wins outright:WaitingandStoppedeach get their line,UpToDate/Noneget nothing. Used byrepository. -
The heuristic — "a coverage overview exists but this commit has no coverage number", with a 3-hour grace period from
endedAnalysis:- Coverage expected:
listCoverageReports(limit=1).data.hasCoverageOverview - Coverage data present:
diffCoverage.value !== undefined OR deltaCoverage !== undefined - Wait threshold: 3 hours from
endedAnalysis
Only
pull-requeststill needs it (see above). - Coverage expected:
Why (1) exists. The heuristic is wrong for Waiting: a waiting repository
still reports a percentage — a stale one, from lastCommitWithCoverage — so
"coverage data present" is true and the heuristic reads the repository as
healthy, leaving the row silent in exactly the case worth surfacing. It was also
vaguer than necessary for Stopped ("Missing coverage reports"), and could never
work under a repository token at all, since listCoverageReports is not
whitelisted while getRepositoryWithAnalysis is.
Accepted trade-off. When status is undefined — which a substantial share
of repositories return — repository now shows no coverage hint, where the
heuristic might have said "Missing coverage reports". That is the honest reading
of an absent status.
Implemented in formatAnalysisStatus() in src/utils/formatting.ts.
reanalyzeCommitById—RepositoryService.reanalyzeCommitById(provider, org, repo, { commitUuid: sha })getPullRequestCommitswithlimit=1— head commit timing for PRlistRepositoryCommitswithlimit=1— head commit timing for repolistCoverageReportswithlimit=1— checkhasCoverageOverview.pull-requestonly —repositorydropped this call whencoverage.statussuperseded it
Additionally used by --reanalyze-and-wait:
listRepositoryCommits(limit=1) — repo first-commit analysis timestamps, polled for statusgetPullRequestCommits(limit=1) — PR first-commit analysis timestamps, polled for statusgetRepositoryPullRequest— fetched once to resolve the PRheadCommitShaissuesOverview— repo baseline/after issue counts (severity / category / pattern)listPullRequestIssues(status="new", paginated) — PR baseline/after issue list
- Update analysis status in the About section of the
repositorycommand - Update analysis status in the About section of the
pull-requestcommand - Add
--reanalyzeoption to therepositorycommand - Add
--reanalyzeoption to thepull-requestcommand - Update existing tests for the status sections
- Add tests for the new
--reanalyzeoption - Add
--reanalyze-and-wait(-w) blocking variant to both commands (2026-06-02) - Drive
repository's coverage state fromCoverage.statusinstead of thelistCoverageReportsheuristic (2026-09-10)
src/utils/formatting.test.ts— 11 unit tests forformatAnalysisStatus(6 for the heuristic, 5 for the authoritativecoverageStatus, including the Waiting-with-a-stale-percentage case the heuristic got wrong); +formatDurationandisBeingAnalyzedtestssrc/commands/repository.test.ts— 4 tests (analysis status, reanalyze) + 3 for--reanalyze-and-waitsrc/commands/pull-request.test.ts— 3 tests (analysis status, reanalyze) + 3 for--reanalyze-and-waitsrc/utils/reanalyze-wait.test.ts— 12 unit tests (snapshots, diff, poll loop incl. timeout, render, json)