gitea forge bundle: name the parent the forge used, match protection rules as the forge does, bound a commit's size and a search's time (issue 539)
The forge's contents and diffpatch endpoints take a branch, never a commit to
build on, so the parent is read from the commit the forge made, and a branch
that moved under the commit is said. Blobs are read at the commit read first,
not at the branch name. Protection rules match as Gitea 1.27 matches them
([...], {a,b}, escapes, a plain name in any case), in the Go bundle and in
guard.ts alike. One commit carries at most 4 MiB per file or patch and 16 MiB
together; code search stops at its deadline and says so. The HTTP client the
two Go bundles copied is one package, internal/forgehttp.
This commit is contained in:
+81
-13
@@ -164,21 +164,89 @@ export function judgeApiCall(method: string, rawPath: string, body: unknown): Ap
|
||||
};
|
||||
}
|
||||
|
||||
/** Whether a protection rule's name — a branch, or a glob of them (`*`, `**`) — covers a branch. */
|
||||
/**
|
||||
* Whether a protection rule's name covers a branch, as the forge itself decides it (Gitea 1.27,
|
||||
* models/git/protected_branch.go Match and modules/glob; novox/hq issue 539): a name without a glob character
|
||||
* covers the branch of that name in any case; any other name is a glob with `/` as its separator — `*` any run
|
||||
* within a segment, `**` any run across them, `?` one character of a segment, `[abc]`, `[a-z]` and `[!abc]` one
|
||||
* character, `{a,b}` either, `\` the next character itself — matched case-sensitively; a glob the forge cannot
|
||||
* compile covers its own text exactly. The forge bundle's ruleCovers (cmd/forge/rules.go) is the same rule.
|
||||
*/
|
||||
export function ruleCovers(rule: string, branch: string): boolean {
|
||||
if (rule === branch) return true;
|
||||
if (!/[*?[]/.test(rule)) return false;
|
||||
let re = "";
|
||||
for (let i = 0; i < rule.length; i++) {
|
||||
const c = rule[i];
|
||||
if (c === "*" && rule[i + 1] === "*") {
|
||||
re += ".*";
|
||||
i++;
|
||||
} else if (c === "*") re += "[^/]*";
|
||||
else if (c === "?") re += "[^/]";
|
||||
else re += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
||||
if (!/[*?\\[\]{}]/.test(rule)) return rule.toLowerCase() === branch.toLowerCase();
|
||||
const re = globRegExp(rule);
|
||||
return re ? re.test(branch) : rule === branch;
|
||||
}
|
||||
|
||||
/** The forge's glob as a regular expression, compiled as its modules/glob compiles it with `/` as separator. */
|
||||
function globRegExp(glob: string): RegExp | null {
|
||||
const g = Array.from(glob);
|
||||
let out = "";
|
||||
let pos = 0;
|
||||
const lit = (c: string) => c.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
||||
const chars = (): boolean => {
|
||||
out += "[";
|
||||
if (g[pos] === "!") {
|
||||
pos++;
|
||||
out += "^";
|
||||
}
|
||||
while (pos < g.length) {
|
||||
const c = g[pos++];
|
||||
if (c === "]") {
|
||||
out += "]";
|
||||
return true;
|
||||
}
|
||||
if (c === "\\") {
|
||||
if (pos >= g.length) return false;
|
||||
out += "\\" + g[pos++];
|
||||
} else out += c === "[" ? "\\[" : c;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const compile = (sub: boolean): boolean => {
|
||||
while (pos < g.length) {
|
||||
const c = g[pos++];
|
||||
if (sub && c === "}") {
|
||||
out += ")";
|
||||
return true;
|
||||
}
|
||||
switch (c) {
|
||||
case "*":
|
||||
if (g[pos] === "*") {
|
||||
pos++;
|
||||
out += ".*";
|
||||
} else out += "[^/]*";
|
||||
break;
|
||||
case "?":
|
||||
out += "[^/]";
|
||||
break;
|
||||
case "[":
|
||||
if (!chars()) return false;
|
||||
break;
|
||||
case "{":
|
||||
out += "(";
|
||||
if (!compile(true)) return false;
|
||||
break;
|
||||
case ",":
|
||||
out += sub ? "|" : ",";
|
||||
break;
|
||||
case "\\":
|
||||
if (pos >= g.length) return false;
|
||||
out += lit(g[pos++]);
|
||||
break;
|
||||
default:
|
||||
out += lit(c);
|
||||
}
|
||||
}
|
||||
// An alternative left open is a glob the forge cannot compile.
|
||||
return !sub;
|
||||
};
|
||||
if (!compile(false)) return null;
|
||||
try {
|
||||
return new RegExp(`^${out}$`, "u");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return new RegExp(`^${re}$`).test(branch);
|
||||
}
|
||||
|
||||
/** Refuses a file written to the default branch or to a branch a protection rule names. */
|
||||
|
||||
Reference in New Issue
Block a user