Commit | Line | Data |
---|---|---|
905ed4ad JB |
1 | name: 'Combine PRs' |
2 | ||
3 | # Controls when the action will run - in this case triggered manually | |
4 | on: | |
5 | workflow_dispatch: | |
6 | inputs: | |
7 | branchPrefix: | |
8 | description: 'Branch prefix to find combinable PRs based on' | |
9 | required: true | |
10 | default: 'dependabot' | |
11 | mustBeGreen: | |
12 | description: 'Only combine PRs that are green (status is success)' | |
13 | required: true | |
14 | default: true | |
15 | combineBranchName: | |
16 | description: 'Name of the branch to combine PRs into' | |
17 | required: true | |
18 | default: 'combine-prs-branch' | |
19 | ignoreLabel: | |
20 | description: 'Exclude PRs with this label' | |
21 | required: true | |
22 | default: 'nocombine' | |
23 | ||
24 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel | |
25 | jobs: | |
26 | # This workflow contains a single job called "combine-prs" | |
27 | combine-prs: | |
28 | # The type of runner that the job will run on | |
29 | runs-on: ubuntu-latest | |
30 | ||
31 | # Steps represent a sequence of tasks that will be executed as part of the job | |
32 | steps: | |
33 | - uses: actions/github-script@v6 | |
34 | id: create-combined-pr | |
35 | name: Create Combined PR | |
36 | with: | |
37 | github-token: ${{secrets.GITHUB_TOKEN}} | |
38 | script: | | |
39 | const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', { | |
40 | owner: context.repo.owner, | |
41 | repo: context.repo.repo | |
42 | }); | |
43 | let branchesAndPRStrings = []; | |
44 | let baseBranch = null; | |
45 | let baseBranchSHA = null; | |
46 | for (const pull of pulls) { | |
47 | const branch = pull['head']['ref']; | |
48 | console.log('Pull for branch: ' + branch); | |
49 | if (branch.startsWith('${{ github.event.inputs.branchPrefix }}')) { | |
50 | console.log('Branch matched prefix: ' + branch); | |
51 | let statusOK = true; | |
52 | if(${{ github.event.inputs.mustBeGreen }}) { | |
53 | console.log('Checking green status: ' + branch); | |
54 | const statusResponse = await github.rest.repos.getCombinedStatusForRef({ | |
55 | owner: context.repo.owner, | |
56 | repo: context.repo.repo, | |
57 | ref: branch | |
58 | }); | |
59 | const state = statusResponse['data']['state']; | |
60 | console.log('Validating status: ' + state); | |
61 | if(state != 'success') { | |
62 | console.log('Discarding ' + branch + ' with status ' + state); | |
63 | statusOK = false; | |
64 | } | |
65 | } | |
66 | console.log('Checking labels: ' + branch); | |
67 | const labels = pull['labels']; | |
68 | for(const label of labels) { | |
69 | const labelName = label['name']; | |
70 | console.log('Checking label: ' + labelName); | |
71 | if(labelName == '${{ github.event.inputs.ignoreLabel }}') { | |
72 | console.log('Discarding ' + branch + ' with label ' + labelName); | |
73 | statusOK = false; | |
74 | } | |
75 | } | |
76 | if (statusOK) { | |
77 | console.log('Adding branch to array: ' + branch); | |
78 | const prString = '#' + pull['number'] + ' ' + pull['title']; | |
79 | branchesAndPRStrings.push({ branch, prString }); | |
80 | baseBranch = pull['base']['ref']; | |
81 | baseBranchSHA = pull['base']['sha']; | |
82 | } | |
83 | } | |
84 | } | |
85 | if (branchesAndPRStrings.length == 0) { | |
86 | core.setFailed('No PRs/branches matched criteria'); | |
87 | return; | |
88 | } | |
89 | try { | |
90 | await github.rest.git.createRef({ | |
91 | owner: context.repo.owner, | |
92 | repo: context.repo.repo, | |
93 | ref: 'refs/heads/' + '${{ github.event.inputs.combineBranchName }}', | |
94 | sha: baseBranchSHA | |
95 | }); | |
96 | } catch (error) { | |
97 | console.log(error); | |
98 | core.setFailed('Failed to create combined branch - maybe a branch by that name already exists?'); | |
99 | return; | |
100 | } | |
101 | ||
102 | let combinedPRs = []; | |
103 | let mergeFailedPRs = []; | |
104 | for(const { branch, prString } of branchesAndPRStrings) { | |
105 | try { | |
106 | await github.rest.repos.merge({ | |
107 | owner: context.repo.owner, | |
108 | repo: context.repo.repo, | |
109 | base: '${{ github.event.inputs.combineBranchName }}', | |
110 | head: branch, | |
111 | }); | |
112 | console.log('Merged branch ' + branch); | |
113 | combinedPRs.push(prString); | |
114 | } catch (error) { | |
115 | console.log('Failed to merge branch ' + branch); | |
116 | mergeFailedPRs.push(prString); | |
117 | } | |
118 | } | |
119 | ||
120 | console.log('Creating combined PR'); | |
121 | const combinedPRsString = combinedPRs.join('\n'); | |
122 | let body = '✅ This PR was created by the Combine PRs action by combining the following PRs:\n' + combinedPRsString; | |
123 | if(mergeFailedPRs.length > 0) { | |
124 | const mergeFailedPRsString = mergeFailedPRs.join('\n'); | |
125 | body += '\n\n⚠️ The following PRs were left out due to merge conflicts:\n' + mergeFailedPRsString | |
126 | } | |
127 | await github.rest.pulls.create({ | |
128 | owner: context.repo.owner, | |
129 | repo: context.repo.repo, | |
130 | title: 'Combined PR', | |
131 | head: '${{ github.event.inputs.combineBranchName }}', | |
132 | base: baseBranch, | |
133 | body: body | |
134 | }); |