Gate the token mint's main-rule write by keep-main-rules too
mesh/delivery delivered
mesh/check pass 68dfca105794 on 3bec357968da | unit 4443 run, 0 kept, 15 skipped | integration 4483 run, 0 kept, 23 skipped | over bound: unit, integr…
mesh/delivery delivered
mesh/check pass 68dfca105794 on 3bec357968da | unit 4443 run, 0 kept, 15 skipped | integration 4483 run, 0 kept, 23 skipped | over bound: unit, integr…
The operator, 2026-10-23: "disable the gitea branch protection reset thing". setUpToolsAccount (run at every token mint) called ensureMainMerges regardless; it now does so only when the setting is true.
This commit is contained in:
@@ -41,3 +41,24 @@ test("true: every step puts the rules right", async () => {
|
||||
assert.equal(writes, 2);
|
||||
assert.deepEqual(said, ["[gitea] main's rule is right in 2 repositories"]);
|
||||
});
|
||||
|
||||
test("a token mint's account setup writes no protection while keep-main-rules is false, and does when true", async () => {
|
||||
const { setUpToolsAccount } = await import("../dist/tools-account.js");
|
||||
for (const [keep, want] of [[false, 0], [true, 1]] as const) {
|
||||
let merges = 0;
|
||||
const forge = {
|
||||
ensureUser: async () => {}, ensureTeamWith: async () => 1, addUserToTeam: async () => {},
|
||||
ensureMainMerges: async () => { merges++; return []; },
|
||||
};
|
||||
await setUpToolsAccount(forge, "mesh-tools", () => "pw", keep);
|
||||
assert.equal(merges, want);
|
||||
}
|
||||
// The default comes from the setting, which is false when the config does not say true.
|
||||
let merges = 0;
|
||||
const saved = process.env.MESH_GITEA_CONFIG_FILE;
|
||||
delete process.env.MESH_GITEA_CONFIG_FILE;
|
||||
try {
|
||||
await setUpToolsAccount({ ensureUser: async () => {}, ensureTeamWith: async () => 1, addUserToTeam: async () => {}, ensureMainMerges: async () => { merges++; return []; } }, "mesh-tools", () => "pw");
|
||||
} finally { if (saved !== undefined) process.env.MESH_GITEA_CONFIG_FILE = saved; }
|
||||
assert.equal(merges, 0);
|
||||
});
|
||||
|
||||
@@ -192,11 +192,14 @@ function fakeForge() {
|
||||
const forge = await fakeForge();
|
||||
after(() => forge.close());
|
||||
|
||||
async function environment(): Promise<NodeJS.ProcessEnv & { MESH_GITEA_STATE_DIR: string }> {
|
||||
async function environment(keepMainRules = false): Promise<NodeJS.ProcessEnv & { MESH_GITEA_STATE_DIR: string }> {
|
||||
const dir = await mkdtemp(join(tmpdir(), "gitea-tools-account-"));
|
||||
const passwordFile = join(dir, "admin.secret");
|
||||
await writeFile(passwordFile, PASSWORD + "\n", { mode: 0o600 });
|
||||
const config = join(dir, "config.json");
|
||||
await writeFile(config, JSON.stringify({ "keep-main-rules": keepMainRules }));
|
||||
return {
|
||||
MESH_GITEA_CONFIG_FILE: config,
|
||||
MESH_GITEA_URL: forge.url,
|
||||
MESH_GITEA_ADMIN_USER: ADMIN,
|
||||
MESH_GITEA_ADMIN_PASSWORD_FILE: passwordFile,
|
||||
@@ -351,7 +354,7 @@ test("a team another process made between the look and the create is found, not
|
||||
});
|
||||
|
||||
test("setting the tools' account up puts main's rule right in every repository: no push, only the tools' account merges (issue 948)", async () => {
|
||||
const env = await environment();
|
||||
const env = await environment(true);
|
||||
const from = forge.calls.length;
|
||||
await fresh(env).listRepos();
|
||||
const hq = forge.mainRules.get("hq");
|
||||
@@ -369,7 +372,7 @@ test("setting the tools' account up puts main's rule right in every repository:
|
||||
});
|
||||
|
||||
test("a glob rule covering main keeps its statuses when main is given a rule of its own name (issue 948, review round 1)", async () => {
|
||||
const env = await environment();
|
||||
const env = await environment(true);
|
||||
await fresh(env).listRepos();
|
||||
const rule = forge.mainRules.get("globbed");
|
||||
assert.ok(rule, "main is given its own rule");
|
||||
@@ -390,3 +393,11 @@ test("a start with the token kept still puts main's rules right: the module's st
|
||||
await GiteaAdmin.fromEnv(env).ensureMainMerges("novox");
|
||||
assert.equal(forge.mainRules.get("hq").enable_push, false, "a rule gone is made again with no token minted");
|
||||
});
|
||||
|
||||
test("a mint with keep-main-rules false (the default) writes no branch protection", async () => {
|
||||
const env = await environment();
|
||||
const from = forge.calls.length;
|
||||
await fresh(env).listRepos();
|
||||
const writes = forge.calls.slice(from).filter((c: string) => /branch_protections/.test(c) && !/^GET /.test(c) && !/^(GET|HEAD) /.test(c));
|
||||
assert.deepEqual(writes, []);
|
||||
});
|
||||
|
||||
@@ -39,6 +39,8 @@ import { dirname, join } from "node:path";
|
||||
import { setUpToolsAccount, TOOLS_ACCOUNT, type AdminCall } from "./tools-account.js";
|
||||
|
||||
/** The name the token carries in the forge's own list — one per mesh runtime, found by name. */
|
||||
import { keepMainRules } from "./mainrules.js";
|
||||
|
||||
export const TOKEN_NAME = "mesh-tools";
|
||||
|
||||
/**
|
||||
@@ -181,7 +183,7 @@ export class MintedToken implements TokenSource {
|
||||
// GiteaAdmin is loaded when first used: client.ts imports this file.
|
||||
setUp: async (password) => {
|
||||
const { GiteaAdmin } = await import("./client.js");
|
||||
await setUpToolsAccount(new GiteaAdmin(url, admin!, password), account, () => randomBytes(24).toString("base64url"));
|
||||
await setUpToolsAccount(new GiteaAdmin(url, admin!, password), account, () => randomBytes(24).toString("base64url"), keepMainRules(env));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
// or a membership lost behind the module's back is made again at the next mint.
|
||||
|
||||
/** The account the tools act as. */
|
||||
import { keepMainRules } from "./mainrules.js";
|
||||
|
||||
export const TOOLS_ACCOUNT = "mesh-tools";
|
||||
/** The organisation whose repositories are the mesh's, and the team through which the tools' account writes there. */
|
||||
export const TOOLS_ORG = "novox";
|
||||
@@ -47,11 +49,12 @@ export interface ToolsForge {
|
||||
|
||||
/** Make the tools' account, plain, a member of its team on every repository of the organisation. Its password is
|
||||
* set afresh each time and kept nowhere: the account is reached only by the tokens the administrator mints. */
|
||||
export async function setUpToolsAccount(forge: ToolsForge, account = TOOLS_ACCOUNT, password: () => string): Promise<void> {
|
||||
export async function setUpToolsAccount(forge: ToolsForge, account = TOOLS_ACCOUNT, password: () => string, keepRules: boolean = keepMainRules()): Promise<void> {
|
||||
await forge.ensureUser(account, password(), `${account}@noreply.localhost`, true);
|
||||
const team = await forge.ensureTeamWith(TOOLS_ORG, TOOLS_TEAM, { name: TOOLS_TEAM, ...TOOLS_TEAM_UNITS });
|
||||
await forge.addUserToTeam(team, account);
|
||||
// Main takes no push and only this account merges into it (novox/hq ADR 0398 §2, issue 948): put right in every
|
||||
// repository now, by the administrator's login, which the account itself is not.
|
||||
await forge.ensureMainMerges?.(TOOLS_ORG);
|
||||
// Only while the setting keep-main-rules is true (mainrules.ts; the operator, 2026-10-23); else no protection is written.
|
||||
if (keepRules) await forge.ensureMainMerges?.(TOOLS_ORG);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user