From ff6ee252c292a40a405538ff9d15f837f9e05ab3 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 3 Nov 2026 15:18:15 -0800 Subject: [PATCH] devops(bots): let a publish-calls pull request go to a branch --- .github/actions/publish-calls/calls.d.ts | 3 +++ .github/actions/publish-calls/publish-calls.ts | 7 ++++--- .github/workflows/bots.md | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/actions/publish-calls/calls.d.ts b/.github/actions/publish-calls/calls.d.ts index 389d6a7013c51..19b054c2017f7 100644 --- a/.github/actions/publish-calls/calls.d.ts +++ b/.github/actions/publish-calls/calls.d.ts @@ -88,6 +88,9 @@ export type PullRequest = Text & { repo: string; /** A new branch for the PR, such as "triage//-". */ branch: string; + /** The branch that the PR goes to. Leave it out for the default branch of the repo. The checkout + * of that repo must be on this branch, because the patch is against it. */ + base?: string; /** The title of the commit and of the PR. */ title: string; /** A patch file in $OUTPUT_DIR, against the commit of the checkout of that repo, such as diff --git a/.github/actions/publish-calls/publish-calls.ts b/.github/actions/publish-calls/publish-calls.ts index 035267b5cc953..f536ffe26f7ec 100644 --- a/.github/actions/publish-calls/publish-calls.ts +++ b/.github/actions/publish-calls/publish-calls.ts @@ -83,7 +83,7 @@ const KINDS: Kind[] = ['pull-request', 'push', 'issue', 'comment', 'reply']; // The keys of each kind. A kind with text takes exactly one of body and bodyFile. const SCHEMA: { [K in Kind]: { required: KeysOf[], optional: KeysOf[], text: boolean } } = { - 'pull-request': { required: ['repo', 'branch', 'title', 'patch'], optional: ['commitBody', 'labels'], text: true }, + 'pull-request': { required: ['repo', 'branch', 'title', 'patch'], optional: ['base', 'commitBody', 'labels'], text: true }, 'push': { required: ['repo', 'pullRequest', 'title', 'patch'], optional: ['commitBody', 'onto'], text: false }, 'issue': { required: ['repo', 'title'], optional: ['labels', 'filedFor'], text: true }, 'comment': { required: ['repo', 'issue'], optional: [], text: true }, @@ -156,7 +156,7 @@ async function publish({ github, context, core }: { github: GitHub, context: Con // The branch must be new, so that a mistake cannot push to main or to another branch. const cwd = checkoutOf(call.repo, repository); const commit = commitPatch({ cwd, patch: call.patch, title: call.title, body: call.commitBody, branch: call.branch, lease: '' }); - const { data: { default_branch: base } } = await github.rest.repos.get({ owner, repo }); + const base = call.base ?? (await github.rest.repos.get({ owner, repo })).data.default_branch; const { data: pr } = await github.rest.pulls.create({ owner, repo, head: call.branch, base, title: call.title, body: call.body, draft: true, @@ -315,9 +315,10 @@ function check(key: Key, value: unknown, dir: string): string | number | string[ throw new Error(`title must be one line of 1 to ${MAX_TITLE} characters`); return value.trim(); } + case 'base': case 'branch': { if (typeof value !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._/-]{0,199}$/.test(value) || value.includes('..') || value.endsWith('/') || value.endsWith('.lock')) - throw new Error(`branch must be a branch name, such as triage/123/456-1, got ${JSON.stringify(value)}`); + throw new Error(`${key} must be a branch name, such as triage/123/456-1, got ${JSON.stringify(value)}`); return value; } case 'onto': { diff --git a/.github/workflows/bots.md b/.github/workflows/bots.md index da8d61109932b..5b8f318fee26a 100644 --- a/.github/workflows/bots.md +++ b/.github/workflows/bots.md @@ -91,7 +91,7 @@ checks in `publish-calls.ts` are written by hand; keep them in line with `calls. | Call | Keys | What `publish-calls` does | |---|---|---| -| `pull-request` | `repo`, `branch`, `title`, `patch`, `body` or `bodyFile`, `commitBody`, `labels` | Applies the patch to the checkout with `git apply --index`, rejects a patch that touches `.github/`, commits as the app, pushes to the branch, which must not exist yet, and opens a draft PR on the default branch with the labels that the repo has. | +| `pull-request` | `repo`, `branch`, `base`, `title`, `patch`, `body` or `bodyFile`, `commitBody`, `labels` | Applies the patch to the checkout with `git apply --index`, rejects a patch that touches `.github/`, commits as the app, pushes to the branch, which must not exist yet, and opens a draft PR on `base`, or on the default branch without it, with the labels that the repo has. | | `push` | `repo`, `pullRequest`, `title`, `patch`, `commitBody`, `onto` | Checks that the PR is open and comes from a branch of the repo, and pushes to that branch. With `onto`, the patch is the whole PR against that commit: the PR is replayed on it and the branch is replaced. The push fails when the branch moved since the checkout. | | `issue` | `repo`, `title`, `body` or `bodyFile`, `labels`, `filedFor` | Uses an open issue whose text has the `filedFor` URL, or files one with the labels that the repo has. | | `comment` | `repo`, `issue`, `body` or `bodyFile` | Posts the text, with `{{pull-request}}` and `{{issue}}` replaced by the URLs of what the calls before it opened, or by a note when they failed. |