Skip to content

Commit 50bee3c

Browse files
committed
fix(codeclimate): always emit location.lines.begin for GitLab.
File-level recipe rows (e.g. boundary-violations) default begin to 1 when line_start is absent; clarify README format comment.
1 parent 30dc729 commit 50bee3c

4 files changed

Lines changed: 31 additions & 10 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ codemap audit --base v1.0.0 --files-baseline pre-release-files # mix --base wit
131131
# non-git projects get a clean `codemap audit: --base requires a git repository.` error.
132132
# Recipes that define per-row action templates append "actions" hints (kebab-case verb +
133133
# description) in --json output; ad-hoc SQL never carries actions. Inspect via --recipes-json.
134-
# --format <text|json|sarif|annotations|mermaid|diff|diff-json|codeclimate|badge> — pipe results into GitHub Code Scanning
135-
# (SARIF 2.1.0), surface findings inline on PRs (GH Actions ::notice file=…,line=…::msg), or
136-
# render edge-shaped recipes as Mermaid `flowchart LR`, or preview edits as unified diffs. All
134+
# --format <text|json|sarif|annotations|mermaid|diff|diff-json|codeclimate|badge> — SARIF for GitHub
135+
# Code Scanning; annotations for GH Actions ::notice lines; codeclimate for GitLab Code Quality;
136+
# badge for issue-count summaries; mermaid/diff for graph and edit previews. All
137137
# formatted outputs require a flat row list
138138
# (no --summary / --group-by / baseline). SARIF / annotations auto-detect file_path /
139139
# path / to_path / from_path; rule.id is codemap.<recipe-id> (or codemap.adhoc). Mermaid

docs/plans/ci-output-formats.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Moat A: formatters only — no new analysis.
4848
| # | Decision | Source |
4949
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
5050
| F.1 | **Two new format ids**`codeclimate` and `badge` on `codemap query` / MCP `query` / `query_recipe` (same `output-formatters.ts` home as SARIF). | [Moat A](../roadmap.md#moats-load-bearing) — output mode only |
51-
| F.2 | **Code Climate shape** — JSON array of objects: `description`, `check_name`, `fingerprint`, `location.path`, `location.lines.begin`, `severity` (`info`\|`minor`\|`major`\|`critical`\|`blocker`). | [GitLab Code Quality format](https://docs.gitlab.com/ee/ci/testing/code_quality.html) |
51+
| F.2 | **Code Climate shape** — JSON array of objects: `description`, `check_name`, `fingerprint`, `location.path`, `location.lines.begin` (`line_start` when present, else `1` for file-level rows), `severity` (`info`\|`minor`\|`major`\|`critical`\|`blocker`). | [GitLab Code Quality format](https://docs.gitlab.com/ee/ci/testing/code_quality.html) |
5252
| F.3 | **Fingerprint** — stable hash from `(recipe_id, file_path, line_start, check_name)` (FNV-1a or SHA-256 truncated) so GitLab dedupes across runs. | GitLab dedup semantics |
5353
| F.4 | **Badge count source** — row count after location filtering (same rows Code Climate would emit); no network fetch in core. | Output formatter only |
5454
| F.5 | **Location contract** — reuse `detectLocationColumn` from SARIF/annotations; skip rows without locatable columns (same stderr warning as SARIF aggregates). | `output-formatters.ts` |

src/application/output-formatters.test.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,13 +317,31 @@ describe("formatCodeClimate", () => {
317317
expect(JSON.parse(out)).toHaveLength(1);
318318
});
319319

320-
it("omits location.lines when line_start is absent", () => {
320+
it("defaults location.lines.begin to 1 when line_start is absent", () => {
321321
const out = formatCodeClimate({
322322
rows: [{ file_path: "a.ts", fan_in: 17 }],
323323
recipeId: "fan-in",
324324
});
325325
const issues = JSON.parse(out);
326-
expect(issues[0].location).toEqual({ path: "a.ts" });
326+
expect(issues[0].location).toEqual({
327+
path: "a.ts",
328+
lines: { begin: 1 },
329+
});
330+
});
331+
332+
it("emits begin for boundary-style rows without line_start", () => {
333+
const out = formatCodeClimate({
334+
rows: [
335+
{
336+
file_path: "src/ui/App.tsx",
337+
to_path: "src/server/db.ts",
338+
rule_name: "ui-cant-touch-server",
339+
},
340+
],
341+
recipeId: "boundary-violations",
342+
});
343+
const issues = JSON.parse(out);
344+
expect(issues[0].location.lines.begin).toBe(1);
327345
});
328346

329347
it("buildCodeClimateFingerprint uses adhoc when recipeId omitted", () => {

src/application/output-formatters.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,13 @@ export function formatCodeClimate(opts: FormatOpts): string {
227227
typeof lineStartRaw === "number" && lineStartRaw > 0
228228
? lineStartRaw
229229
: undefined;
230-
const location: { path: string; lines?: { begin: number } } = { path };
231-
if (lineStart !== undefined) {
232-
location.lines = { begin: lineStart };
233-
}
230+
// GitLab requires location.lines.begin — file-level rows (e.g. boundary-violations)
231+
// fall back to 1 when the recipe has no line_start column.
232+
const begin = lineStart ?? 1;
233+
const location: { path: string; lines: { begin: number } } = {
234+
path,
235+
lines: { begin },
236+
};
234237
return [
235238
{
236239
description: buildMessageText(row),

0 commit comments

Comments
 (0)