Skip to content

Commit d41ed3d

Browse files
committed
added repository commands
1 parent 22f7018 commit d41ed3d

File tree

7 files changed

+264
-28
lines changed

7 files changed

+264
-28
lines changed

README.md

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ You can get a token from **Codacy > My Account > Access Management > Account API
3333
## Usage
3434

3535
```bash
36-
codacy-cloud-cli <command> [options]
36+
codacy <command> [options]
3737
```
3838

3939
### Global Options
@@ -51,8 +51,8 @@ codacy-cloud-cli <command> [options]
5151
Show authenticated user information and their organizations.
5252

5353
```bash
54-
codacy-cloud-cli info
55-
codacy-cloud-cli info --output json
54+
codacy info
55+
codacy info --output json
5656
```
5757

5858
Displays:
@@ -64,9 +64,9 @@ Displays:
6464
List repositories for an organization with analysis data.
6565

6666
```bash
67-
codacy-cloud-cli repositories gh my-org
68-
codacy-cloud-cli repositories gh my-org --search my-repo
69-
codacy-cloud-cli repositories gl my-org --output json
67+
codacy repositories gh my-org
68+
codacy repositories gh my-org --search my-repo
69+
codacy repositories gl my-org --output json
7070
```
7171

7272
| Argument | Description |
@@ -82,14 +82,25 @@ Displays: name, grade, issues, complexity, duplication, coverage, and last updat
8282

8383
#### `repository <provider> <organization> <repository>`
8484

85-
Show detailed status and metrics for a specific repository.
85+
Show detailed status and metrics for a specific repository, or perform an action on it.
8686

8787
```bash
88-
codacy-cloud-cli repository gh my-org my-repo
89-
codacy-cloud-cli repository gh my-org my-repo --output json
88+
codacy repository gh my-org my-repo
89+
codacy repository gh my-org my-repo --output json
90+
codacy repository gh my-org my-repo --add
91+
codacy repository gh my-org my-repo --remove
92+
codacy repository gh my-org my-repo --follow
93+
codacy repository gh my-org my-repo --unfollow
9094
```
9195

92-
Displays a multi-section dashboard:
96+
| Option | Description |
97+
|---|---|
98+
| `-a, --add` | Add this repository to Codacy |
99+
| `-r, --remove` | Remove this repository from Codacy |
100+
| `-f, --follow` | Follow this repository on Codacy |
101+
| `-u, --unfollow` | Unfollow this repository on Codacy |
102+
103+
Without an action flag, displays a multi-section dashboard:
93104
- **About** -- repository info, default branch, last analysis
94105
- **Setup** -- languages, coding standards, quality gate, problems
95106
- **Metrics** -- issues (count + per kLoC), coverage, complexity, duplication
@@ -133,11 +144,11 @@ With `--overview`, displays issue count totals grouped by: category, severity, l
133144
Show security findings for a repository or an entire organization.
134145

135146
```bash
136-
codacy-cloud-cli findings gh my-org my-repo
137-
codacy-cloud-cli findings gh my-org
138-
codacy-cloud-cli findings gh my-org --severities Critical,High
139-
codacy-cloud-cli findings gh my-org my-repo --statuses Overdue,DueSoon
140-
codacy-cloud-cli findings gh my-org my-repo --output json
147+
codacy findings gh my-org my-repo
148+
codacy findings gh my-org
149+
codacy findings gh my-org --severities Critical,High
150+
codacy findings gh my-org my-repo --statuses Overdue,DueSoon
151+
codacy findings gh my-org my-repo --output json
141152
```
142153

143154
| Argument | Description |
@@ -167,8 +178,8 @@ When browsing org-wide (no repository argument), the repository name is shown on
167178
Show full details of a single quality issue, including extended code context and pattern documentation.
168179

169180
```bash
170-
codacy-cloud-cli issue gh my-org my-repo 12345
171-
codacy-cloud-cli issue gh my-org my-repo 12345 --output json
181+
codacy issue gh my-org my-repo 12345
182+
codacy issue gh my-org my-repo 12345 --output json
172183
```
173184

174185
| Argument | Description |
@@ -184,13 +195,37 @@ Displays:
184195
- **False positive warning** — if the issue is likely a false positive
185196
- **Pattern documentation** — full description, rationale ("Why is this a problem?"), solution ("How to fix it?"), tags, and tool/pattern reference
186197

198+
#### `finding <provider> <organization> <repository> <findingId>`
199+
200+
Show full details for a single security finding, with CVE enrichment when a CVE ID is present.
201+
202+
```bash
203+
codacy finding gh my-org my-repo 12345
204+
codacy finding gh my-org my-repo 12345 --output json
205+
```
206+
207+
| Argument | Description |
208+
|---|---|
209+
| `provider` | Git provider: `gh` (GitHub), `gl` (GitLab), or `bb` (Bitbucket) |
210+
| `organization` | Organization name on the provider |
211+
| `repository` | Repository name |
212+
| `findingId` | Finding ID shown at the bottom of each card in the `findings` command output |
213+
214+
Displays:
215+
- **Header** — priority, security category, scan type, status, CVE/CWE identifiers, affected/fixed versions
216+
- **Code context** — if the finding comes from a Codacy quality issue: ±5 lines of code with the issue line highlighted and pattern documentation
217+
- **Pattern documentation** — description ("About this pattern"), rationale, solution, tags, tool/pattern reference
218+
- **CVE details** — (when a CVE ID is present) CVSS v3/v4 scores colored by severity, published/updated dates, title, English description, and deduplicated references fetched in parallel from the NVD database
219+
187220
#### `pull-request <provider> <organization> <repository> <prNumber>`
188221

189222
Show details, analysis status, issues, and changed files for a specific pull request.
190223

191224
```bash
192-
codacy-cloud-cli pull-request gh my-org my-repo 42
193-
codacy-cloud-cli pull-request gh my-org my-repo 42 --output json
225+
codacy pull-request gh my-org my-repo 42
226+
codacy pull-request gh my-org my-repo 42 --output json
227+
codacy pull-request gh my-org my-repo 42 --issue 9901
228+
codacy pull-request gh my-org my-repo 42 --diff
194229
```
195230

196231
| Argument | Description |
@@ -200,11 +235,21 @@ codacy-cloud-cli pull-request gh my-org my-repo 42 --output json
200235
| `repository` | Repository name |
201236
| `prNumber` | Pull request number |
202237

203-
Displays:
204-
- **About** -- PR title, status, author, branches, head commit
205-
- **Analysis** -- up-to-standards status, issues, coverage, complexity, duplication with gate pass/fail details
206-
- **Issues** -- issues introduced by the PR, sorted by severity, with file path and line content
207-
- **Files** -- changed files with metric deltas (issues, coverage, complexity, duplication)
238+
| Option | Description |
239+
|---|---|
240+
| `-i, --issue <issueId>` | Show full details for a specific issue introduced by this PR (use the `#id` shown on issue cards) |
241+
| `-d, --diff` | Show the git diff annotated with coverage hits/misses and new issues |
242+
243+
Default view displays:
244+
- **About** — PR title, status, author, branches, head commit
245+
- **Analysis** — up-to-standards status, issues, coverage, complexity, duplication with gate pass/fail details; "To check" hints when a gate is configured but the metric has no data yet
246+
- **Issues** — all issues introduced by the PR (confirmed + potential), sorted by severity, with file path, line content, and false-positive warnings
247+
- **Diff Coverage Summary** — per-file diff coverage percentage and compressed uncovered line ranges (when coverage data is available)
248+
- **Files** — changed files with metric deltas (issues, coverage, complexity, duplication)
249+
250+
With `--issue <id>`: shows full detail for that issue (code context + pattern docs), same format as the `issue` command.
251+
252+
With `--diff`: shows the annotated git diff — only blocks containing covered/uncovered lines or new issues, with 3 lines of context around each. Coverage is shown with ✓ (green, covered) and ✘ (red, uncovered) symbols; issues are annotated inline with severity, category, and message.
208253

209254
## Development
210255

SPECS/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ _No pending tasks._
1414
|---|---|---|---|
1515
| `info` | `inf` | ✅ Done | [info.md](commands/info.md) |
1616
| `repositories` | `repos` | ✅ Done | [repositories.md](commands/repositories.md) |
17-
| `repository` | `repo` | ✅ Done | [repository.md](commands/repository.md) |
17+
| `repository` | `repo` | ✅ Done (actions added) | [repository.md](commands/repository.md) |
1818
| `pull-request` | `pr` | ✅ Done (--diff + Diff Coverage Summary added) | [pull-request.md](commands/pull-request.md) |
1919
| `issues` | `is` | ✅ Done | [issues.md](commands/issues.md) |
2020
| `issue` | `iss` | ✅ Done | [issue.md](commands/issue.md) |
@@ -49,3 +49,4 @@ _No pending tasks._
4949
| 2026-02-24 | CVE enrichment for `finding`: fetches `cveawg.mitre.org` in parallel, shows CVSS/description/references (5 new tests, 102 total) |
5050
| 2026-02-24 | SPECS folder created — TODO.md split into `SPECS/README.md` + per-command specs + setup/deployment |
5151
| 2026-02-25 | `pull-request --diff` option + Diff Coverage Summary section (6 new tests, 108 total) |
52+
| 2026-02-25 | `repository` actions: `--add`, `--remove`, `--follow`, `--unfollow` (4 new tests, 112 total) |

SPECS/commands/repository.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# `repository` Command Spec
22

3-
**Status:** ✅ Done (2026-02-18)
3+
**Status:** ✅ Done (2026-02-18); actions (`--add`, `--remove`, `--follow`, `--unfollow`) added 2026-02-25
44

55
## Purpose
66

@@ -62,6 +62,39 @@ Shows pagination warning if more PRs exist.
6262

6363
By category, severity level, and language — sorted descending by count.
6464

65+
## Actions
66+
In all cases, return a success message when the action is completed successfully, otherwise return an error message with the error details provided by the API.
67+
68+
### Add repository to Codacy
69+
- API Endpoint: [`addRepository`](https://api.codacy.com/api/api-docs#addrepository)
70+
- Usage:
71+
```
72+
codacy repository <provider> <organization> <repository> --add
73+
```
74+
75+
- Add an additional message explaining that the repository will be available after a few minutes (after first cloning and analysis is completed), depending on the size of the repository.
76+
77+
### Remove repository from Codacy
78+
- API Endpoint: [`deleteRepository`](https://api.codacy.com/api/api-docs#deleterepository)
79+
- Usage:
80+
```
81+
codacy repository <provider> <organization> <repository> --remove
82+
```
83+
84+
### Follow repository
85+
- API Endpoint: [`followAddedRepository`](https://api.codacy.com/api/api-docs#followaddedrepository)
86+
- Usage:
87+
```
88+
codacy repository <provider> <organization> <repository> --follow
89+
```
90+
91+
### Unfollow repository
92+
- API Endpoint: [`unfollowRepository`](https://api.codacy.com/api/api-docs#unfollowrepository)
93+
- Usage:
94+
```
95+
codacy repository <provider> <organization> <repository> --unfollow
96+
```
97+
6598
## Tests
6699

67100
File: `src/commands/repository.test.ts` — 5 tests.

src/commands/pull-request.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { RepositoryService } from "../api/client/services/RepositoryService";
99

1010
vi.mock("../api/client/services/AnalysisService");
1111
vi.mock("../api/client/services/CoverageService");
12+
vi.mock("../api/client/services/RepositoryService");
1213
vi.mock("../api/client/services/ToolsService");
1314
vi.mock("../api/client/services/FileService");
1415
vi.spyOn(console, "log").mockImplementation(() => {});

src/commands/pull-request.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { FileDiffCoverage } from "../api/client/models/FileDiffCoverage";
3737
import { PullRequestIssuesResponse } from "../api/client/models/PullRequestIssuesResponse";
3838
import { FileAnalysisListResponse } from "../api/client/models/FileAnalysisListResponse";
3939
import { parseDiff } from "../utils/diff";
40-
import { RepositoryService } from "../api/client";
40+
import { RepositoryService } from "../api/client/services/RepositoryService";
4141

4242
const SEVERITY_ORDER: Record<string, number> = {
4343
Error: 0,

src/commands/repository.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
22
import { Command } from "commander";
33
import { registerRepositoryCommand } from "./repository";
44
import { AnalysisService } from "../api/client/services/AnalysisService";
5+
import { RepositoryService } from "../api/client/services/RepositoryService";
56

67
vi.mock("../api/client/services/AnalysisService");
8+
vi.mock("../api/client/services/RepositoryService");
79
vi.spyOn(console, "log").mockImplementation(() => {});
810

911
function createProgram(): Command {
@@ -472,4 +474,84 @@ describe("repository command", () => {
472474

473475
mockExit.mockRestore();
474476
});
477+
478+
// ─── Actions ─────────────────────────────────────────────────────────────
479+
480+
it("should add repository to Codacy with --add", async () => {
481+
vi.mocked(RepositoryService.addRepository).mockResolvedValue({} as any);
482+
483+
const program = createProgram();
484+
await program.parseAsync([
485+
"node", "test", "repository", "gh", "test-org", "test-repo", "--add",
486+
]);
487+
488+
expect(RepositoryService.addRepository).toHaveBeenCalledWith({
489+
repositoryFullPath: "test-org/test-repo",
490+
provider: "gh",
491+
});
492+
493+
const allOutput = (console.log as ReturnType<typeof vi.fn>).mock.calls
494+
.map((c) => c[0])
495+
.join("\n");
496+
expect(allOutput).toContain("test-repo");
497+
expect(allOutput).toContain("added");
498+
expect(allOutput).toContain("few minutes");
499+
});
500+
501+
it("should remove repository from Codacy with --remove", async () => {
502+
vi.mocked(RepositoryService.deleteRepository).mockResolvedValue(undefined as any);
503+
504+
const program = createProgram();
505+
await program.parseAsync([
506+
"node", "test", "repository", "gh", "test-org", "test-repo", "--remove",
507+
]);
508+
509+
expect(RepositoryService.deleteRepository).toHaveBeenCalledWith(
510+
"gh", "test-org", "test-repo",
511+
);
512+
513+
const allOutput = (console.log as ReturnType<typeof vi.fn>).mock.calls
514+
.map((c) => c[0])
515+
.join("\n");
516+
expect(allOutput).toContain("test-repo");
517+
expect(allOutput).toContain("removed");
518+
});
519+
520+
it("should follow repository with --follow", async () => {
521+
vi.mocked(RepositoryService.followAddedRepository).mockResolvedValue({} as any);
522+
523+
const program = createProgram();
524+
await program.parseAsync([
525+
"node", "test", "repository", "gh", "test-org", "test-repo", "--follow",
526+
]);
527+
528+
expect(RepositoryService.followAddedRepository).toHaveBeenCalledWith(
529+
"gh", "test-org", "test-repo",
530+
);
531+
532+
const allOutput = (console.log as ReturnType<typeof vi.fn>).mock.calls
533+
.map((c) => c[0])
534+
.join("\n");
535+
expect(allOutput).toContain("test-repo");
536+
expect(allOutput).toContain("following");
537+
});
538+
539+
it("should unfollow repository with --unfollow", async () => {
540+
vi.mocked(RepositoryService.unfollowRepository).mockResolvedValue(undefined as any);
541+
542+
const program = createProgram();
543+
await program.parseAsync([
544+
"node", "test", "repository", "gh", "test-org", "test-repo", "--unfollow",
545+
]);
546+
547+
expect(RepositoryService.unfollowRepository).toHaveBeenCalledWith(
548+
"gh", "test-org", "test-repo",
549+
);
550+
551+
const allOutput = (console.log as ReturnType<typeof vi.fn>).mock.calls
552+
.map((c) => c[0])
553+
.join("\n");
554+
expect(allOutput).toContain("test-repo");
555+
expect(allOutput).toContain("Unfollowed");
556+
});
475557
});

0 commit comments

Comments
 (0)