Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions apps/cloud/src/automations/mergePullRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@ describe("a cloud thread merges its pull request through the Merge gate", () =>
);
});

it.effect("the gate sees where a fork's pull request comes from and goes (#5)", () => {
const { cloud, github, open, tool, getRun, run } = makePullRequestThread();
return run(
Effect.gen(function* () {
yield* open;
github.update(1, { checks: [...PASSED], files: ["app.ts"], headRepository: "fork/repo" });
yield* cloud.syncPullRequests(threadId);

const { waitId } = (yield* tool("merge_pull_request", { url: URL })) as {
readonly waitId: string;
};
yield* cloud.settle;
const read = (yield* getRun(waitId)).steps.find(
(step) => step.label === "Read the pull request",
);
expect(read?.result).toMatchObject({
headBranch: github.pulls[0]!.head,
baseBranch: "main",
headRepository: "fork/repo",
});
}),
);
});

it.effect("an unready one goes back to the agent, and nothing merges", () => {
const { cloud, github, open, tool, messages, run } = makePullRequestThread();
return run(
Expand Down
12 changes: 12 additions & 0 deletions apps/cloud/src/github/PullRequestMerges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export interface MergeEvidence {
readonly title: string;
readonly state: PullRequestState;
readonly headSha: string;
/** Where it comes from and goes: a folder's gate can treat its own branches differently. */
readonly headBranch: string;
readonly baseBranch: string;
/** The head branch's repository; another one's means a fork. Null when GitHub lost it (deleted fork). */
readonly headRepository: string | null;
readonly isDraft: boolean;
readonly mergeability: PullRequestMergeability;
/** GitHub's own decision; approvals by apps alone don't count here as they do in upstream's summary. */
Expand Down Expand Up @@ -83,6 +88,7 @@ const EVIDENCE_QUERY = `query PullRequestMergeEvidence($owner: String!, $name: S
repository(owner: $owner, name: $name) {
pullRequest(number: $number) {
title state isDraft mergeable reviewDecision headRefOid
headRefName baseRefName headRepository { nameWithOwner }
reviewThreads(first: 100) { nodes { isResolved } pageInfo { hasNextPage } }
files(first: 100) { nodes { path } pageInfo { hasNextPage endCursor } }
}
Expand Down Expand Up @@ -142,6 +148,9 @@ const decodeEvidence = answerOf(
Schema.Literals(["APPROVED", "CHANGES_REQUESTED", "REVIEW_REQUIRED"]),
),
headRefOid: Schema.String,
headRefName: Schema.String,
baseRefName: Schema.String,
headRepository: Schema.NullOr(Schema.Struct({ nameWithOwner: Schema.String })),
reviewThreads: Schema.Struct({
nodes: Schema.Array(Schema.Struct({ isResolved: Schema.Boolean })),
pageInfo: Schema.Struct({ hasNextPage: Schema.Boolean }),
Expand Down Expand Up @@ -295,6 +304,9 @@ const make = Effect.gen(function* () {
title: pullRequest.title,
state: STATES[pullRequest.state] ?? "open",
headSha: pullRequest.headRefOid,
headBranch: pullRequest.headRefName,
baseBranch: pullRequest.baseRefName,
headRepository: pullRequest.headRepository?.nameWithOwner ?? null,
isDraft: pullRequest.isDraft,
mergeability: MERGEABILITY[pullRequest.mergeable],
reviewDecision:
Expand Down
5 changes: 4 additions & 1 deletion apps/cloud/src/github/githubTesting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ export const makeFakeGitHub = (options: {
openThreads?: number;
/** The head commit when it merged, as GitHub keeps it after the branch moves or goes. */
headOid?: string;
/** The head branch's repository when it isn't this one: a fork. */
headRepository?: string;
}> = [];
const calls: Array<string> = [];
/** Changes made through `update`, so each moves `updatedAt` a minute on. */
Expand Down Expand Up @@ -168,7 +170,8 @@ export const makeFakeGitHub = (options: {
reviewDecision: null,
headRefOid: head(pull.head) ?? "0".repeat(40),
headRefName: pull.head,
headRepository: { nameWithOwner: options.repository },
baseRefName: pull.base,
headRepository: { nameWithOwner: pull.headRepository ?? options.repository },
reviewThreads: {
nodes: Array.from({ length: pull.openThreads ?? 0 }, () => ({ isResolved: false })),
pageInfo: { hasNextPage: false },
Expand Down
6 changes: 6 additions & 0 deletions apps/server/src/workflows/builtinTools/layer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ it.effect(
state: "merged",
title: "Fix login",
headSha: "abc",
headBranch: "fix/login",
baseBranch: "main",
headRepositoryNameWithOwner: "fork/app",
isDraft: false,
mergeability: "unknown",
checks: [{ name: "test", status: "success", description: null, url: null }],
Expand Down Expand Up @@ -280,6 +283,9 @@ it.effect(
title: "Fix login",
state: "merged",
headSha: "abc",
headBranch: "fix/login",
baseBranch: "main",
headRepository: "fork/app",
isDraft: false,
mergeability: "unknown",
reviewDecision: null,
Expand Down
8 changes: 7 additions & 1 deletion apps/server/src/workflows/builtinTools/pullRequestAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,17 @@ const Target = {

const PullRequestReadTool = Tool.make("pull_request_read", {
description:
"Read a pull request linked to a thread, fresh from the host: where it lives, its title, state and head commit, and the merge gate's evidence at that commit. Parts the host can't answer here are null: review threads, the review decision and the changed files are read only in the cloud so far.",
"Read a pull request linked to a thread, fresh from the host: where it lives, its title, state, head commit, head and base branches and the head's repository (another one's is a fork), and the merge gate's evidence at that commit. Parts the host can't answer here are null: review threads, the review decision and the changed files are read only in the cloud so far.",
parameters: Schema.Struct(Target),
success: Schema.Struct({
repository: Schema.String,
number: Schema.Int,
title: Schema.String,
state: PullRequestState,
headSha: Schema.NullOr(Schema.String),
headBranch: Schema.String,
baseBranch: Schema.String,
headRepository: Schema.NullOr(Schema.String),
isDraft: Schema.Boolean,
mergeability: PullRequestMergeability,
reviewDecision: Schema.Null,
Expand Down Expand Up @@ -173,6 +176,9 @@ export const layer = McpToolAccess.toLayer(
title: current.title,
state: current.state,
headSha: current.headSha ?? null,
headBranch: current.headBranch,
baseBranch: current.baseBranch,
headRepository: current.headRepositoryNameWithOwner ?? null,
isDraft: current.isDraft,
mergeability: current.mergeability,
reviewDecision: null,
Expand Down
4 changes: 4 additions & 0 deletions packages/automations/src/defaults/mergeGate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ type PullRequest = {
title: string;
state: string;
headSha: string | null;
/** Where it comes from and goes. A headRepository other than repository is a fork. */
headBranch?: string;
baseBranch?: string;
headRepository?: string | null;
isDraft?: boolean | null;
mergeability?: string | null;
reviewDecision?: string | null;
Expand Down
50 changes: 25 additions & 25 deletions packages/automations/src/defaults/precompiled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s1",
line: 119,
line: 123,
verb: "call",
label: {
text: "Read the pull request",
Expand All @@ -657,7 +657,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "branch",
id: "s2",
line: 122,
line: 126,
source: "condition",
label: {
text: 'pr.state !== "open"',
Expand All @@ -670,7 +670,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s3/s4",
line: 63,
line: 67,
verb: "call",
label: {
text: "Tell the agent",
Expand All @@ -689,7 +689,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "end",
id: "s5",
line: 124,
line: 128,
exit: "workflow",
},
],
Expand All @@ -703,7 +703,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "branch",
id: "s6",
line: 128,
line: 132,
source: "condition",
label: {
text: "notReady.length > 0",
Expand All @@ -716,7 +716,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s7/s4",
line: 63,
line: 67,
verb: "call",
label: {
text: "Tell the agent",
Expand All @@ -735,7 +735,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "end",
id: "s8",
line: 130,
line: 134,
exit: "workflow",
},
],
Expand All @@ -749,7 +749,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "branch",
id: "s9",
line: 134,
line: 138,
source: "condition",
label: {
text: "reasons.length > 0",
Expand All @@ -762,7 +762,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s10",
line: 136,
line: 140,
verb: "ask",
label: {
text: "Merge …#…?",
Expand All @@ -787,7 +787,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "branch",
id: "s13",
line: 147,
line: 151,
source: "outcome",
label: {
text: "Merge …#…?",
Expand All @@ -801,7 +801,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s11/s4",
line: 63,
line: 67,
verb: "call",
label: {
text: "Tell the agent",
Expand All @@ -820,7 +820,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "end",
id: "s12",
line: 152,
line: 156,
exit: "workflow",
},
],
Expand All @@ -842,7 +842,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "try",
id: "s14",
line: 157,
line: 161,
label: {
text: "If something fails",
dynamic: false,
Expand All @@ -851,7 +851,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s15",
line: 158,
line: 162,
verb: "call",
label: {
text: "Merge it",
Expand All @@ -873,7 +873,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s16/s4",
line: 63,
line: 67,
verb: "call",
label: {
text: "Tell the agent",
Expand All @@ -892,15 +892,15 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "end",
id: "s17",
line: 166,
line: 170,
exit: "workflow",
},
],
},
{
type: "branch",
id: "s18",
line: 168,
line: 172,
source: "condition",
label: {
text: "merged.moved",
Expand All @@ -913,7 +913,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s19/s4",
line: 63,
line: 67,
verb: "call",
label: {
text: "Tell the agent",
Expand All @@ -932,7 +932,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "end",
id: "s20",
line: 170,
line: 174,
exit: "workflow",
},
],
Expand All @@ -946,7 +946,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "branch",
id: "s21",
line: 173,
line: 177,
source: "condition",
label: {
text: "A host with a merge queue takes it in instead of merging; the branch stays until it lands.",
Expand All @@ -959,7 +959,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s22/s4",
line: 63,
line: 67,
verb: "call",
label: {
text: "Tell the agent",
Expand All @@ -978,7 +978,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "end",
id: "s23",
line: 175,
line: 179,
exit: "workflow",
},
],
Expand All @@ -992,7 +992,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "try",
id: "s24",
line: 178,
line: 182,
label: {
text: "If something fails",
dynamic: false,
Expand All @@ -1001,7 +1001,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s25",
line: 179,
line: 183,
verb: "call",
label: {
text: "Delete its branch",
Expand All @@ -1023,7 +1023,7 @@ export const PRECOMPILED_BUILT_INS: Readonly<Record<string, CompiledWorkflow>> =
{
type: "step",
id: "s26/s4",
line: 63,
line: 67,
verb: "call",
label: {
text: "Tell the agent",
Expand Down
Loading