
















@@ -73,7 +73,49 @@ function barnacleContext(
7373};
7474}
757576-function barnacleGithub(files: ReturnType<typeof file>[]) {
76+function barnacleIssueContext(
77+issue: Record<string, unknown>,
78+labels: string[] = [],
79+options: Record<string, unknown> = {},
80+) {
81+return {
82+repo: {
83+owner: "openclaw",
84+repo: "openclaw",
85+},
86+payload: {
87+action: options.action ?? "opened",
88+label: options.label,
89+sender: options.sender,
90+issue: {
91+number: 456,
92+title: "OpenClaw issue",
93+body: "",
94+author_association: "CONTRIBUTOR",
95+user: {
96+login: "contributor",
97+},
98+labels: labels.map((name) => ({ name })),
99+ ...issue,
100+},
101+comment: options.comment,
102+},
103+};
104+}
105+106+function barnacleGithub(
107+files: ReturnType<typeof file>[],
108+options: { maintainerLogins?: string[]; repositoryRoles?: Record<string, string> } = {},
109+) {
110+const maintainerLogins = new Set(
111+(options.maintainerLogins ?? []).map((login) => login.toLowerCase()),
112+);
113+const repositoryRoles = Object.fromEntries(
114+Object.entries(options.repositoryRoles ?? {}).map(([login, role]) => [
115+login.toLowerCase(),
116+role,
117+]),
118+);
77119const calls = {
78120addLabels: [] as Array<{ issue_number: number; labels: string[] }>,
79121createComment: [] as Array<{ issue_number: number; body: string }>,
@@ -115,14 +157,25 @@ function barnacleGithub(files: ReturnType<typeof file>[]) {
115157listFiles: async () => files,
116158},
117159repos: {
118-getCollaboratorPermissionLevel: async () => ({
119-data: {
120-role_name: "read",
121-},
122-}),
160+getCollaboratorPermissionLevel: async ({ username }: { username: string }) => {
161+const role = repositoryRoles[username.toLowerCase()] ?? "read";
162+return {
163+data: {
164+permission: role,
165+role_name: role,
166+},
167+};
168+},
123169},
124170teams: {
125-getMembershipForUserInOrg: async () => {
171+getMembershipForUserInOrg: async ({ username }: { username: string }) => {
172+if (maintainerLogins.has(username.toLowerCase())) {
173+return {
174+data: {
175+state: "active",
176+},
177+};
178+}
126179const error = new Error("not found") as Error & { status: number };
127180error.status = 404;
128181throw error;
@@ -234,7 +287,7 @@ describe("barnacle-auto-response", () => {
234287expect(labels).not.toContain(candidateLabels.externalPluginCandidate);
235288});
236289237-it("does not add candidate labels to maintainer-authored PRs", async () => {
290+it("does not mutate maintainer-authored PRs", async () => {
238291const { calls, github } = barnacleGithub([
239292file("ui/src/app.ts"),
240293file("src/gateway/server.ts"),
@@ -256,9 +309,12 @@ describe("barnacle-auto-response", () => {
256309});
257310258311expect(calls.addLabels).toEqual([]);
312+expect(calls.createComment).toEqual([]);
313+expect(calls.removeLabel).toEqual([]);
314+expect(calls.update).toEqual([]);
259315});
260316261-it("removes stale Barnacle candidate and PR-limit labels from maintainer-authored PRs", async () => {
317+it("leaves stale Barnacle labels alone on maintainer-authored PRs", async () => {
262318const { calls, github } = barnacleGithub([
263319file("ui/src/app.ts"),
264320file("src/gateway/server.ts"),
@@ -282,12 +338,93 @@ describe("barnacle-auto-response", () => {
282338},
283339});
284340285-expect(calls.removeLabel).toEqual(
286-expect.arrayContaining([
287-expect.objectContaining({ name: candidateLabels.dirtyCandidate }),
288-expect.objectContaining({ name: "r: too-many-prs" }),
289-]),
290-);
341+expect(calls.addLabels).toEqual([]);
342+expect(calls.createComment).toEqual([]);
343+expect(calls.removeLabel).toEqual([]);
344+expect(calls.update).toEqual([]);
345+});
346+347+it("does not mutate maintainer-authored issues", async () => {
348+const { calls, github } = barnacleGithub([]);
349+350+await runBarnacleAutoResponse({
351+ github,
352+context: barnacleIssueContext({
353+title: "TestFlight access",
354+author_association: "OWNER",
355+user: {
356+login: "maintainer",
357+},
358+}),
359+core: {
360+info: () => undefined,
361+},
362+});
363+364+expect(calls.addLabels).toEqual([]);
365+expect(calls.createComment).toEqual([]);
366+expect(calls.update).toEqual([]);
367+});
368+369+it("does not action close labels on maintainer-authored issues", async () => {
370+const { calls, github } = barnacleGithub([]);
371+372+await runBarnacleAutoResponse({
373+ github,
374+context: barnacleIssueContext(
375+{
376+title: "Need help with setup",
377+author_association: "MEMBER",
378+user: {
379+login: "maintainer",
380+},
381+},
382+["r: support"],
383+{
384+action: "labeled",
385+label: { name: "r: support" },
386+},
387+),
388+core: {
389+info: () => undefined,
390+},
391+});
392+393+expect(calls.createComment).toEqual([]);
394+expect(calls.update).toEqual([]);
395+});
396+397+it("does not respond to maintainer comments on contributor items", async () => {
398+const { calls, github } = barnacleGithub([], { maintainerLogins: ["maintainer"] });
399+400+await runBarnacleAutoResponse({
401+ github,
402+context: barnacleIssueContext(
403+{
404+title: "Contributor issue",
405+user: {
406+login: "contributor",
407+},
408+},
409+[],
410+{
411+action: "created",
412+comment: {
413+body: "testflight",
414+user: {
415+login: "maintainer",
416+type: "User",
417+},
418+},
419+},
420+),
421+core: {
422+info: () => undefined,
423+},
424+});
425+426+expect(calls.createComment).toEqual([]);
427+expect(calls.update).toEqual([]);
291428});
292429293430it("does not close automation PRs for the active PR limit", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。