Give the forge bridge a merge commit's trees in one call, and a pull request's head branch (issue 1234)
gitea_commit_trees reads the commit's recursive tree listing and answers the root tree and the tree of every directory holding a module.json; a cut listing fails loudly. gitea_open_pulls entries gain head. The query keys recursive and per_page join the allow-list, read flags only. mesh-delivery decodes open pulls with json.Unmarshal into a struct (cmd/mesh-delivery/ports.go:405-410), which ignores the added key.
This commit is contained in:
+42
-1
@@ -92,6 +92,8 @@ export interface OpenPull {
|
||||
repo: string;
|
||||
number: number;
|
||||
base: string;
|
||||
/** The pull request's head branch: what a delivery group is named by (novox/hq ADR 0249). */
|
||||
head?: string;
|
||||
head_sha: string;
|
||||
html_url?: string;
|
||||
/** The contexts the base branch's protection requires; none when it requires none, or cannot be read. */
|
||||
@@ -374,7 +376,7 @@ export class GiteaClient {
|
||||
const rule = rules.get(base);
|
||||
const checks = await this.headChecks(found.owner, found.name, pull.head_sha);
|
||||
const labels = found.labels ?? [];
|
||||
out.push({ owner: found.owner, repo: found.name, number: pull.number, base: pull.base ?? "", head_sha: pull.head_sha,
|
||||
out.push({ owner: found.owner, repo: found.name, number: pull.number, base: pull.base ?? "", head: pull.head, head_sha: pull.head_sha,
|
||||
html_url: pull.html_url, required: rule?.enable_status_check ? (rule.status_check_contexts ?? []) : [],
|
||||
statuses: Object.keys(checks), checks, labels: labels.map((l) => l.name) });
|
||||
}
|
||||
@@ -510,6 +512,45 @@ export class GiteaClient {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** **A commit's trees: its root, and the tree of every directory that holds a `module.json`** (novox/hq issue 1234,
|
||||
* ADR 0437 §4 and §5), from the forge's one recursive listing of the commit, read page by page. A directory is
|
||||
* spelled as the repository spells it, no slash at either end, the root as `""`.
|
||||
*
|
||||
* **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): Promise<{ root: string; trees: Record<string, string> }> {
|
||||
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`);
|
||||
let root = "";
|
||||
const entries: any[] = [];
|
||||
for (let page = 1; ; page++) {
|
||||
if (page > 200) throw cut("more than 200 pages");
|
||||
const got = await this.request<any>(`${base}&page=${page}&per_page=1000`);
|
||||
if (page === 1) {
|
||||
root = String(got?.sha ?? "");
|
||||
if (!/^[0-9a-f]{40,64}$/i.test(root)) throw new Error(`the forge named no root tree for ${owner}/${repo} at ${sha}`);
|
||||
}
|
||||
if (!Array.isArray(got?.tree)) throw new Error(`the forge's tree listing of ${owner}/${repo} at ${sha} has no tree`);
|
||||
entries.push(...got.tree);
|
||||
if (got.truncated !== true) break;
|
||||
// Truncated with nothing more to ask for: the forge itself could not give the rest.
|
||||
if (got.tree.length === 0 || entries.length >= Number(got.total_count ?? 0)) throw cut("truncated");
|
||||
}
|
||||
const trees: Record<string, string> = {};
|
||||
const dirOf = new Map<string, string>();
|
||||
for (const e of entries) if (e?.type === "tree" && typeof e.path === "string") dirOf.set(e.path, String(e.sha));
|
||||
for (const e of entries) {
|
||||
if (e?.type !== "blob" || typeof e.path !== "string") continue;
|
||||
if (e.path !== "module.json" && !e.path.endsWith("/module.json")) continue;
|
||||
const dir = e.path === "module.json" ? "" : e.path.slice(0, -"/module.json".length);
|
||||
const tree = dir === "" ? root : dirOf.get(dir);
|
||||
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;
|
||||
}
|
||||
return { root, trees };
|
||||
}
|
||||
|
||||
/** Whether a repository holds a file at a commit: true, false for a 404, thrown when the forge cannot say. */
|
||||
async holdsFile(owner: string, repo: string, sha: string, file: string): Promise<boolean> {
|
||||
return this.exists(`${repoPath(owner, repo)}/contents/${encodePath(file)}?ref=${encodeURIComponent(sha)}`);
|
||||
|
||||
Reference in New Issue
Block a user