Skip to content

Commit b8bfb01

Browse files
authored
Add a new cherry-pick GHA workflow (#56493)
1 parent 06c3da5 commit b8bfb01

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
name: Create cherry pick PR
2+
3+
on:
4+
repository_dispatch:
5+
types: [create-cherry-pick-pr]
6+
workflow_dispatch:
7+
inputs:
8+
pr:
9+
description: PR number to cherry-pick
10+
required: true
11+
type: number
12+
target_branch:
13+
description: Target branch to cherry-pick to
14+
required: true
15+
type: string
16+
requesting_user:
17+
description: User who requested the cherry-pick
18+
required: true
19+
type: string
20+
21+
permissions:
22+
contents: read
23+
24+
# Ensure scripts are run with pipefail. See:
25+
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference
26+
defaults:
27+
run:
28+
shell: bash
29+
30+
jobs:
31+
open-pr:
32+
runs-on: ubuntu-latest
33+
if: github.repository == 'microsoft/TypeScript'
34+
35+
steps:
36+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
37+
with:
38+
filter: blob:none # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/
39+
fetch-depth: 0 # Default is 1; need to set to 0 to get the benefits of blob:none.
40+
token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
41+
42+
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
43+
env:
44+
PR: ${{ inputs.pr || github.event.client_payload.pr }}
45+
TARGET_BRANCH: ${{ inputs.target_branch || github.event.client_payload.target_branch }}
46+
REQUESTING_USER: ${{ inputs.requesting_user || github.event.client_payload.requesting_user }}
47+
with:
48+
retries: 3
49+
github-token: ${{ secrets.TS_BOT_GITHUB_TOKEN }}
50+
script: |
51+
const { PR, TARGET_BRANCH, REQUESTING_USER } = process.env;
52+
53+
const pr = await github.rest.pulls.get({
54+
owner: context.repo.owner,
55+
repo: context.repo.repo,
56+
pull_number: +PR,
57+
});
58+
59+
if (!pr.data.merge_commit_sha) throw new Error("No merge commit sha found");
60+
61+
const pickBranch = `cherry-pick/${PR}/${TARGET_BRANCH}`;
62+
63+
const title = `🤖 Pick PR #${PR} (${pr.data.title.substring(0, 35)}${pr.data.title.length > 35 ? "..." : ""}) into ${TARGET_BRANCH}`;
64+
65+
await exec.exec("git", ["switch", "--detach", TARGET_BRANCH]);
66+
await exec.exec("git", ["switch", "-c", pickBranch]);
67+
await exec.exec("git", ["cherry-pick", pr.data.merge_commit_sha]);
68+
await exec.exec("git", ["push", "--force", "--set-upstream", "origin", pickBranch]);
69+
70+
const existingPulls = await github.rest.pulls.list({
71+
owner: context.repo.owner,
72+
repo: context.repo.repo,
73+
head: `${context.repo.owner}:${pickBranch}`,
74+
});
75+
76+
if (existingPulls.data.length === 0) {
77+
console.log(`No existing PRs found for ${pickBranch}`);
78+
79+
const body = `This cherry-pick was triggered by a request on #${PR}.\n\nPlease review the diff and merge if no changes are unexpected.`;
80+
81+
const newPr = await github.rest.pulls.create({
82+
owner: context.repo.owner,
83+
repo: context.repo.repo,
84+
base: TARGET_BRANCH,
85+
head: pickBranch,
86+
title,
87+
body,
88+
assignees: ["DanielRosenwasser"],
89+
reviewers: ["DanielRosenwasser", REQUESTING_USER],
90+
});
91+
92+
await github.rest.issues.createComment({
93+
owner: context.repo.owner,
94+
repo: context.repo.repo,
95+
issue_number: +PR,
96+
body: `Hey @${REQUESTING_USER}, I've created #${newPr.data.number} for you.`,
97+
});
98+
}
99+
else {
100+
const existing = existingPulls.data[0];
101+
console.log(`Found existing PR #${existing.number} for ${pickBranch}`);
102+
103+
await github.rest.pulls.update({
104+
owner: context.repo.owner,
105+
repo: context.repo.repo,
106+
pull_number: existing.number,
107+
title,
108+
});
109+
110+
await github.rest.issues.createComment({
111+
owner: context.repo.owner,
112+
repo: context.repo.repo,
113+
issue_number: +PR,
114+
body: `Hey @${REQUESTING_USER}, I've updated #${existing.number} for you.`,
115+
});
116+
}
117+
118+
- run: |
119+
MESSAGE="Hey @$REQUESTING_USER, I was unable to cherry-pick this PR."
120+
MESSAGE+=$'\n\n'
121+
MESSAGE+="Check the logs at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
122+
123+
gh pr comment "$PR" --repo ${{ github.repository }} --body "$MESSAGE"
124+
if: ${{ failure() }}
125+
env:
126+
PR: ${{ inputs.pr || github.event.client_payload.pr }}
127+
TARGET_BRANCH: ${{ inputs.target_branch || github.event.client_payload.target_branch }}
128+
REQUESTING_USER: ${{ inputs.requesting_user || github.event.client_payload.requesting_user }}
129+
GH_TOKEN: ${{ secrets.TS_BOT_GITHUB_TOKEN }}

0 commit comments

Comments
 (0)