diff --git a/modules/gitea/client.ts b/modules/gitea/client.ts index 8c022545..14ced5b1 100644 --- a/modules/gitea/client.ts +++ b/modules/gitea/client.ts @@ -560,7 +560,7 @@ export class GiteaClient { * **A listing the forge cut is no listing** (it says `truncated` on a page with nothing more to read, or the pages * run past a bound): the call fails naming the repository and the commit, never answering the directories it did * see, since a directory left out would be taken for one without a module. Reads only. */ - async commitTrees(owner: string, repo: string, sha: string, paths?: Record, listed?: string[]): Promise<{ root: string; trees: Record; paths?: Record>; listed?: Record> }> { + async commitTrees(owner: string, repo: string, sha: string, paths?: Record): Promise<{ root: string; trees: Record; paths?: Record> }> { const base = `${repoPath(owner, repo)}/git/trees/${commitSegment(sha)}?recursive=true`; const cut = (why: string) => new Error(`the forge's tree listing of ${owner}/${repo} at ${sha} was cut (${why}); no trees are answered from a part`); const entries: any[] = []; @@ -587,23 +587,7 @@ export class GiteaClient { if (!tree) throw new Error(`the forge's tree listing of ${owner}/${repo} at ${sha} holds ${e.path} but not the tree of ${dir}`); trees[dir] = tree; } - // **Listed directories** (novox/hq ADR 0549 §2, issue 1126): each asked directory's files directly under it, by name - // to blob id, from the same listing: how the forge bridge finds the catalogue's placement files, one per node, at a - // merge. A directory the commit lacks lists nothing (the catalogue before its first placement file). - let files: Record> | undefined; - if (listed !== undefined) { - files = {}; - for (const dir of listed) { - files[dir] = {}; - const prefix = dir === "" ? "" : `${dir}/`; - for (const e of entries) { - if (e?.type !== "blob" || typeof e.path !== "string" || !e.path.startsWith(prefix)) continue; - const name = e.path.slice(prefix.length); - if (name !== "" && !name.includes("/")) files[dir][name] = String(e.sha); - } - } - } - if (paths === undefined) return files === undefined ? { root, trees } : { root, trees, listed: files }; + if (paths === undefined) return { root, trees }; // **Named paths** (novox/hq ADR 0499 §3, issue 1758): each asked path's git object id at the commit, a file's or a // directory's, from the same listing, by directory and by path relative to it. A path the listing lacks fails // naming both, and nothing is answered in part. @@ -618,7 +602,7 @@ export class GiteaClient { named[dir][p] = id; } } - return files === undefined ? { root, trees, paths: named } : { root, trees, paths: named, listed: files }; + return { root, trees, paths: named }; } /** Whether a repository holds a file at a commit: true, false for a 404, thrown when the forge cannot say. */ diff --git a/modules/gitea/test/commit-trees.test.ts b/modules/gitea/test/commit-trees.test.ts index f1ae6b7e..0c36f409 100644 --- a/modules/gitea/test/commit-trees.test.ts +++ b/modules/gitea/test/commit-trees.test.ts @@ -183,15 +183,6 @@ test("named paths: each asked path's git object id at the commit, a file's or a }); assert.deepEqual(got.trees, { "": ROOT, "modules/a": h("4"), "modules/b/c": h("8") }); assert.equal((await client.commitTrees("novox", "hq", COMMIT)).paths, undefined, "no paths asked, none answered"); - // Listed directories (ADR 0549 §2, issue 1126): the files directly under each, not deeper; a missing one lists none. - const listed = await client.commitTrees("novox", "hq", COMMIT, undefined, ["modules/a", "modules/b", "", "placement"]); - assert.deepEqual(listed.listed, { - "modules/a": { "main.go": h("6"), "module.json": h("5") }, - "modules/b": {}, - "": { "README.md": h("1"), "module.json": h("2") }, - placement: {}, - }); - assert.equal((await client.commitTrees("novox", "hq", COMMIT)).listed, undefined, "no directory asked, none listed"); } finally { server.close(); } @@ -218,12 +209,7 @@ test("gitea_commit_trees takes paths as {directory: [paths]} and refuses another const { client, server, asked } = await serve([{ sha: COMMIT, url: "u", tree: LISTING, truncated: false, page: 1, total_count: LISTING.length }]); try { const tool = getGiteaTools(client).find((t) => t.name === "gitea_commit_trees")!; - assert.ok("paths" in tool.input && "listed" in tool.input, "the tool names the arguments"); - const listedAnswer: any = await tool.run({ owner: "novox", repo: "hq", sha: COMMIT, listed: ["modules/d"] } as any); - assert.deepEqual(listedAnswer.listed, { "modules/d": { "notes.md": h("b") } }); - for (const bad of ["modules/d", ["../x"], ["/abs"], [1]]) { - await assert.rejects(tool.run({ owner: "novox", repo: "hq", sha: COMMIT, listed: bad } as any), /listed/, JSON.stringify(bad)); - } + assert.ok("paths" in tool.input, "the tool names the arguments"); const got: any = await tool.run({ owner: "novox", repo: "hq", sha: COMMIT, paths: { "modules/a": ["main.go"] } } as any); assert.deepEqual(got.paths, { "modules/a": { "main.go": h("6") } }); const before = asked.length; diff --git a/modules/gitea/tools/index.ts b/modules/gitea/tools/index.ts index e1029d65..4d1a94db 100644 --- a/modules/gitea/tools/index.ts +++ b/modules/gitea/tools/index.ts @@ -79,17 +79,6 @@ function namedPaths(raw: unknown): Record | undefined { return out; } -/** The `listed` argument of gitea_commit_trees: [directory], each plain (no `.` or `..` segment, not absolute, the root - * the empty name); anything else is refused before the forge is asked. Absent is absent. */ -function listedDirs(raw: unknown): string[] | undefined { - if (raw === undefined || raw === null) return undefined; - if (!Array.isArray(raw) || raw.some((d) => typeof d !== "string")) throw new Error("listed is [directory, ...]"); - for (const d of raw as string[]) { - if (d !== "" && d.split("/").some((seg) => seg === "" || seg === "." || seg === "..")) throw new Error(`listed: ${JSON.stringify(d)} is not a plain relative directory`); - } - return raw as string[]; -} - export function getGiteaTools(gitea: GiteaClient, admin?: () => GiteaAdmin, source: SourceRead = sourceFromBus(state)): ToolDefinition[] { return namesChecked(allGiteaTools(gitea, admin, source)); } @@ -733,20 +722,19 @@ function allGiteaTools(gitea: GiteaClient, admin: (() => GiteaAdmin) | undefined }, { name: "gitea_commit_trees", - description: "A commit's trees from the forge's one recursive listing: the root tree, and the tree of every directory that holds a module.json (the root as an empty name), each by its git hash. What the forge bridge puts in a merge's message (novox/hq ADR 0437 §5). With paths, {directory: [path relative to it]}, the answer also carries paths, {directory: {path: git object id}} of each named file or directory at the commit (novox/hq ADR 0499 §3); a path the commit lacks fails naming the directory and the path. The commit is a full 40-hex sha. With listed, [directory], the answer also carries listed, {directory: {name: git blob id}} of the files directly under each (the catalog's placement files, novox/hq ADR 0549 §2). A listing the forge cut is refused, never answered in part. Reads only.", + description: "A commit's trees from the forge's one recursive listing: the root tree, and the tree of every directory that holds a module.json (the root as an empty name), each by its git hash. What the forge bridge puts in a merge's message (novox/hq ADR 0437 §5). With paths, {directory: [path relative to it]}, the answer also carries paths, {directory: {path: git object id}} of each named file or directory at the commit (novox/hq ADR 0499 §3); a path the commit lacks fails naming the directory and the path. The commit is a full 40-hex sha. A listing the forge cut is refused, never answered in part. Reads only.", input: { owner: { type: "string", description: "the repository owner" }, repo: { type: "string", description: "the repository name" }, sha: { type: "string", description: "the commit, its full 40-hex sha" }, paths: { type: "object", description: "optional, {directory: [path relative to it]}: the git object id of each named file or directory is answered under paths; the root directory is the empty name" }, - listed: { type: "array", items: { type: "string" }, description: "optional, [directory]: the files directly under each, {directory: {name: git blob id}}, are answered under listed; a directory the commit lacks lists none" }, }, run: async (args) => { const owner = String(args.owner ?? "").trim(), repo = String(args.repo ?? "").trim(); const sha = String(args.sha ?? "").trim(); if (!owner || !repo) throw new Error("name the repository: owner and repo"); if (!/^[0-9a-f]{40}$/i.test(sha)) throw new Error("sha is a commit's full 40-hex sha"); - return gitea.commitTrees(owner, repo, sha, namedPaths(args.paths), listedDirs(args.listed)); + return gitea.commitTrees(owner, repo, sha, namedPaths(args.paths)); }, }, {