3 # Controls when the action will run - in this case triggered manually
8 description: 'Branch prefix to find combinable PRs based on'
12 description: 'Only combine PRs that are green (status is success)'
16 description: 'Name of the branch to combine PRs into'
18 default: 'combine-prs-branch'
20 description: 'Exclude PRs with this label'
24 # A workflow run is made up of one or more jobs that can run sequentially or in parallel
26 # This workflow contains a single job called "combine-prs"
28 # The type of runner that the job will run on
29 runs-on: ubuntu-latest
31 # Steps represent a sequence of tasks that will be executed as part of the job
33 - uses: actions/github-script@v6
34 id: create-combined-pr
35 name: Create Combined PR
37 github-token: ${{secrets.GITHUB_TOKEN}}
39 const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', {
40 owner: context.repo.owner,
41 repo: context.repo.repo
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);
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,
59 const state = statusResponse['data']['state'];
60 console.log('Validating status: ' + state);
61 if(state != 'success') {
62 console.log('Discarding ' + branch + ' with status ' + state);
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);
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'];
85 if (branchesAndPRStrings.length == 0) {
86 core.setFailed('No PRs/branches matched criteria');
90 await github.rest.git.createRef({
91 owner: context.repo.owner,
92 repo: context.repo.repo,
93 ref: 'refs/heads/' + '${{ github.event.inputs.combineBranchName }}',
98 core.setFailed('Failed to create combined branch - maybe a branch by that name already exists?');
102 let combinedPRs = [];
103 let mergeFailedPRs = [];
104 for(const { branch, prString } of branchesAndPRStrings) {
106 await github.rest.repos.merge({
107 owner: context.repo.owner,
108 repo: context.repo.repo,
109 base: '${{ github.event.inputs.combineBranchName }}',
112 console.log('Merged branch ' + branch);
113 combinedPRs.push(prString);
115 console.log('Failed to merge branch ' + branch);
116 mergeFailedPRs.push(prString);
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
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 }}',