You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add `codacy image <provider> <org> <image> --tag <tag> --upload <file>` to upload an SBOM (SPDX or CycloneDX) for a container image tag.
6
+
7
+
`-e, --environment <name>` and `-r, --repository <name>` optionally record where the image is deployed and which repository it belongs to. The file is checked before the request, so a wrong path or an empty file fails immediately.
Copy file name to clipboardExpand all lines: SPECS/README.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,7 +9,6 @@ This is the single source of truth for all project tasks and specs.
9
9
| Task | Spec | Notes |
10
10
|---|---|---|
11
11
| Tag count on `ImageSummary`|[images.md](commands/images.md)|**Backend**: add a tag count to `listOrganizationImages`' response so `images` can show a Tags column without one extra request per image. Deriving it client-side was deliberately dropped from OD-710's first PR |
12
-
|`image --upload` (`uploadImageSbom`) |[images.md](commands/images.md)| Stacked PR 2 of OD-710. The operation is already in the generated client |
13
12
|`image --delete --keep-latest <n>`|[images.md](commands/images.md)| Stacked PR 3 of OD-710. **Blocked**: every tag delete zero-fills org-wide Container Scanning metrics until the next nightly scan, so a delete loop fires it N times. Waits on "Fix org-wide metrics wipe on image tag deletion" |
14
13
15
14
## Command Inventory
@@ -32,7 +31,7 @@ This is the single source of truth for all project tasks and specs.
@@ -96,4 +95,5 @@ This is the single source of truth for all project tasks and specs.
96
95
| 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) |
97
96
| 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) |
98
97
| 2026-09-21 | (OD-710) New `images` (`imgs`) and `image` (`img`) commands — container images with SBOMs uploaded to an organization, and the tags under them. Built entirely on operations already present in the generated client (`SbomService`): **no `npm run update-api`**. `images <provider> <org>` lists images (Image / Latest Tag / Last Upload / Last Generated) in a single request. `image <provider> <org> <image>` lists that image's tags (Tag / Environment / Repository / Generated / Uploaded / Last Analysed), shows one with `-t, --tag <tag>`, and deletes with `-D, --delete`. **`--delete` is the action and `--tag` is the scope**, the same split `issues --ignore` makes with its filters — the flag that narrows what is acted on is the flag that narrows what is shown — so `--delete` alone removes the image and every SBOM under it, `--tag X --delete` removes one tag, and there is no second delete verb needing a mutual-exclusion guard. `--tag` without an action pages the listing and matches exactly (the tags endpoint has no per-tag filter — the same shape `pull-request --issue <id>` uses) and errors naming the tag when absent; with `--delete` it skips the lookup, since the API 404s on a missing tag at lower cost. **No tag count on the `images` table**: it is the number an org at the 1000-tag cap actually wants, but `ImageSummary` doesn't carry one and deriving it costs one request per image, so it is being added server-side instead (pending task above) rather than fanned out from the client. A whole-image `--delete` still reads a `limit: 1` `pagination.total` so its prompt can name how many tags are about to go; a failed lookup drops the count rather than blocking, and `-y` skips it. Both delete scopes go through the shared `confirmAction` (non-TTY without `-y` aborts, as in `issues --ignore`) and print a yellow notice first: a single SBOM delete currently zero-fills Container Scanning metrics for the **whole organization** until the next nightly scan. That defect is also why this is the first of three stacked PRs — bulk cleanup (`--keep-latest`) would fire it once per tag and waits on the fix; `--upload` is PR 2. Both commands are **account-token only** — no image operation is on the 14-operation repository-token whitelist — and are covered by the cross-cutting `repository-token-refusals.test.ts` rather than their own token tests. Image/tag/environment/repository names arrive with the SBOM upload, so all four go through `sanitizeText()` (26 new tests, 699 total) |
98
+
| 2026-09-21 | (OD-710, PR 2) `image --upload <file>`: push an SBOM (SPDX or CycloneDX) for one image tag, via the already-generated `uploadImageSbom`. Fits the command's existing action/scope split — `--upload` is a verb, `--tag` scopes it — and **requires `--tag`**, since the API keys the upload on image *and* tag with no untagged fallback; that is refused by name before the file is read. The file is validated locally first (unreadable path, empty file), so the common mistakes fail with something actionable instead of a 400 from the other side of the network. Sent as a `File` rather than a bare `Blob` so the multipart part carries the real filename — a `Blob` goes out as `filename="blob"` — with the media type inferred from the extension (`.json`/`.xml`, else `application/octet-stream`, letting the API decide rather than guessing wrong in the request); the generated `isBlob` accepts both shapes. Optional `-e, --environment` and `-r, --repository` map to the API's `environment`/`repositoryName` and are omitted from the form rather than sent as undefined. `--upload` and `--delete` are refused together: unlike `--delete`'s two scopes these are two different verbs, and asking for both says nothing coherent about what should happen to the SBOM. Still account-token only, which is the awkward part — uploading from a pipeline is exactly where a project token would be natural, so the gap is logged in `SPECS/missing-endpoints.md` (9 new tests, 708 total) |
99
99
| 2026-09-22 | (OD-710, review follow-up) `confirmAction` now prompts on **stderr**, not stdout. `process.stdin.isTTY` is true whenever *stdin* is a terminal — including when stdout is a pipe — so `image … --delete --output json | jq` sent the question and the echoed keystroke into `jq`. Fixed in `utils/prompt.ts` rather than per command, so every caller is covered and no command has to thread its output format into the prompt; new `utils/prompt.test.ts` pins the stream, the y/N parsing and the non-TTY refusal. Also replaced two `images.test.ts` assertions that could not fail: the dash test asserted only that the image name appeared, and the `--limit` cap test asserted a page size that is constant for any limit ≥ 100 (5 new tests, 705 total) |
| 4 |`searchRepositoryIgnoredIssues`| POST |`codacy issues --ignored`| Read-only, and a natural sibling of the already-whitelisted `searchRepositoryIssues`. |
21
21
| 5 |`bulkIgnoreIssues`| POST |`codacy issues --ignore`| Write. Would let the auto-configuration flow ignore noisy issues instead of only disabling patterns. Note: a future read-only repository token must block this by operationId. |
22
22
| 6 |`updateIssueState`| PATCH |`codacy issue --ignore/--unignore`| Same category as #5, single-issue. |
23
-
| 7 |`listCoverageReports`| GET | The coverage-expectation suffix on `codacy repository`'s Analysis row | Lowest value of the reads: it affects one optional suffix and has **zero** JSON impact (no coverage key is projected). Listed for completeness. |
23
+
| 7 |`uploadImageSbom`| POST |`codacy image --upload` from CI | Write, and the odd one out on this list: it is organization-scoped like the rest of `SbomService`, but it is the one image operation a *pipeline* runs, and pipelines are exactly where a repository token is the natural credential (the coverage reporter already reads `CODACY_PROJECT_TOKEN` there). The upload already names a `repositoryName`, so a repository-scoped token has an obvious meaning for it. Needs a decision from the API owners rather than an assumption. |
24
+
| 8 |`listCoverageReports`| GET | The coverage-expectation suffix on `codacy repository`'s Analysis row | Lowest value of the reads: it affects one optional suffix and has **zero** JSON impact (no coverage key is projected). Listed for completeness. |
0 commit comments