From acfe531a4e5172ea3815f654a3e8cc21ce4dc67b Mon Sep 17 00:00:00 2001 From: Laura Koye Date: Fri, 21 Aug 2026 13:40:49 -0500 Subject: [PATCH 1/3] [DIT-13468]: Send scanned path scope in the CLI scan --- lib/src/http/scan.test.ts | 17 +++++++++++++++-- lib/src/http/scan.ts | 18 +++++++++++++++++- lib/src/http/types.ts | 2 ++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/lib/src/http/scan.test.ts b/lib/src/http/scan.test.ts index c084f80..e9bd92b 100644 --- a/lib/src/http/scan.test.ts +++ b/lib/src/http/scan.test.ts @@ -22,17 +22,28 @@ describe("buildInitiateScanBody", () => { gitCommitSha: "d3c1a8148580e1869c91ee6caadda17165ceb0ea", gitBranch: "master", repoRelativeRoot: "lib/src", + scannedAllPaths: false, + scannedPaths: ["lib/src"], }); }); - test("sends an empty repo-relative root when scanning the repo root", () => { + test("scanning the repo root covers every path, with no path list", () => { const body = buildInitiateScanBody("/Users/laura/cli", context); expect(body.repoRelativeRoot).toBe(""); + expect(body.scannedAllPaths).toBe(true); + expect(body).not.toHaveProperty("scannedPaths"); }); - test("omits the repo-relative root when the path is outside the repo", () => { + test("omits the whole scope when the path is outside the repo", () => { const body = buildInitiateScanBody("/elsewhere/src", context); expect(body).not.toHaveProperty("repoRelativeRoot"); + expect(body).not.toHaveProperty("scannedAllPaths"); + expect(body).not.toHaveProperty("scannedPaths"); + }); + + test("scanned paths are relative to the repo root, not the scanned root", () => { + const body = buildInitiateScanBody("/Users/laura/cli/lib/src", context); + expect(body.scannedPaths).toEqual([body.repoRelativeRoot]); }); test("sends a null branch on a detached HEAD", () => { @@ -55,6 +66,8 @@ describe("buildInitiateScanBody", () => { "path", "repoKey", "repoRelativeRoot", + "scannedAllPaths", + "scannedPaths", ]); }); diff --git a/lib/src/http/scan.ts b/lib/src/http/scan.ts index e0a2557..1306ec7 100644 --- a/lib/src/http/scan.ts +++ b/lib/src/http/scan.ts @@ -90,6 +90,22 @@ function repoRelativeRoot( return sep === "/" ? rel : rel.split(sep).join("/"); } +/** + * What the scan looked at, so the server only marks a code link removed where we + * actually looked. `scannedAllPaths` means the whole repo. Otherwise it's the one + * directory we scanned. Empty when the path is outside the repo, which the server + * treats as "looked nowhere". + */ +function scannedScope(root: string | undefined) { + if (root === undefined) return {}; + if (root === "") return { repoRelativeRoot: root, scannedAllPaths: true }; + return { + repoRelativeRoot: root, + scannedAllPaths: false, + scannedPaths: [root], + }; +} + /** * Builds the `POST /v2/scan` body. Without git context the body is exactly what * the CLI has always sent, so a scan outside a repo is unaffected. @@ -105,7 +121,7 @@ export function buildInitiateScanBody( repoKey: gitContext.repoKey, gitCommitSha: gitContext.commitSha, gitBranch: gitContext.branch, - ...(root === undefined ? {} : { repoRelativeRoot: root }), + ...scannedScope(root), }; } diff --git a/lib/src/http/types.ts b/lib/src/http/types.ts index 71d802d..bbcf3ed 100644 --- a/lib/src/http/types.ts +++ b/lib/src/http/types.ts @@ -183,6 +183,8 @@ export const ZInitiateScanBodySchema = z.object({ repoKey: z.string().optional(), gitCommitSha: z.string().optional(), gitBranch: z.string().nullable().optional(), + scannedAllPaths: z.boolean().optional(), + scannedPaths: z.array(z.string()).optional(), repoRelativeRoot: z.string().optional(), }); export type IInitiateScanBody = z.infer; From b40126acb9667e372e6b3561321be47e074d339b Mon Sep 17 00:00:00 2001 From: Laura Koye Date: Fri, 21 Aug 2026 13:46:41 -0500 Subject: [PATCH 2/3] Bumping version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 902bf21..056c274 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dittowords/cli", - "version": "5.8.0", + "version": "5.8.1", "description": "Command Line Interface for Ditto (dittowords.com).", "license": "MIT", "main": "bin/ditto.js", From 94ef03285a377f3725186d4fd01863be9023babe Mon Sep 17 00:00:00 2001 From: Laura Koye Date: Fri, 21 Aug 2026 15:03:14 -0500 Subject: [PATCH 3/3] Making tests more generic with respect to examples --- lib/src/http/scan.test.ts | 16 ++++++++-------- lib/src/scan/git.test.ts | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/src/http/scan.test.ts b/lib/src/http/scan.test.ts index e9bd92b..768e29f 100644 --- a/lib/src/http/scan.test.ts +++ b/lib/src/http/scan.test.ts @@ -4,7 +4,7 @@ import { ZInitiateScanBodySchema } from "./types"; const context: GitContext = { repoKey: "github.com/dittowords/cli", - repoRoot: "/Users/laura/cli", + repoRoot: "/Users/dev/cli", commitSha: "d3c1a8148580e1869c91ee6caadda17165ceb0ea", branch: "master", dirty: false, @@ -16,8 +16,8 @@ describe("buildInitiateScanBody", () => { }); test("sends repo, sha, branch and repo-relative root when there is", () => { - expect(buildInitiateScanBody("/Users/laura/cli/lib/src", context)).toEqual({ - path: "/Users/laura/cli/lib/src", + expect(buildInitiateScanBody("/Users/dev/cli/lib/src", context)).toEqual({ + path: "/Users/dev/cli/lib/src", repoKey: "github.com/dittowords/cli", gitCommitSha: "d3c1a8148580e1869c91ee6caadda17165ceb0ea", gitBranch: "master", @@ -28,7 +28,7 @@ describe("buildInitiateScanBody", () => { }); test("scanning the repo root covers every path, with no path list", () => { - const body = buildInitiateScanBody("/Users/laura/cli", context); + const body = buildInitiateScanBody("/Users/dev/cli", context); expect(body.repoRelativeRoot).toBe(""); expect(body.scannedAllPaths).toBe(true); expect(body).not.toHaveProperty("scannedPaths"); @@ -42,12 +42,12 @@ describe("buildInitiateScanBody", () => { }); test("scanned paths are relative to the repo root, not the scanned root", () => { - const body = buildInitiateScanBody("/Users/laura/cli/lib/src", context); + const body = buildInitiateScanBody("/Users/dev/cli/lib/src", context); expect(body.scannedPaths).toEqual([body.repoRelativeRoot]); }); test("sends a null branch on a detached HEAD", () => { - const body = buildInitiateScanBody("/Users/laura/cli", { + const body = buildInitiateScanBody("/Users/dev/cli", { ...context, branch: null, }); @@ -56,7 +56,7 @@ describe("buildInitiateScanBody", () => { }); test("never sends repoRoot or dirty", () => { - const body = buildInitiateScanBody("/Users/laura/cli/lib", { + const body = buildInitiateScanBody("/Users/dev/cli/lib", { ...context, dirty: true, }); @@ -75,7 +75,7 @@ describe("buildInitiateScanBody", () => { for (const c of [null, context, { ...context, branch: null }]) { expect(() => ZInitiateScanBodySchema.parse( - buildInitiateScanBody("/Users/laura/cli/lib", c) + buildInitiateScanBody("/Users/dev/cli/lib", c) ) ).not.toThrow(); } diff --git a/lib/src/scan/git.test.ts b/lib/src/scan/git.test.ts index 0c23083..2b0b846 100644 --- a/lib/src/scan/git.test.ts +++ b/lib/src/scan/git.test.ts @@ -27,8 +27,8 @@ describe("normalizeRepoKey", () => { "https://gitlab.com/group/sub/deeper/app.git", "gitlab.com/group/sub/deeper/app", ], - ["/Users/laura/Desktop/Ditto/cli", null], - ["file:///Users/laura/Desktop/Ditto/cli", null], + ["/Users/dev/Desktop/Ditto/cli", null], + ["file:///Users/dev/Desktop/Ditto/cli", null], ["https://github.com/app", null], ["", null], ];