Skip to content

Create PRs

Create PRs #374

Workflow file for this run

name: Create PRs
on:
workflow_dispatch:
inputs:
branch:
description: "Branch from which to merge PRs"
required: false
dry-run:
description: "Whether to run in dry run mode"
required: false
default: 'false'
schedule:
- cron: '10 1 * * *' # https://crontab.guru/#10_1_*_*_*
jobs:
create-prs:
name: Create PRs
runs-on: ubuntu-latest
steps:
- name: Create PRs
env:
BRANCH: ${{ github.event.inputs.branch }}
DRY_RUN: ${{ github.event.inputs.dry-run }}
uses: actions/github-script@v7
with:
github-token: ${{ secrets.UCI_GITHUB_TOKEN }}
retries: 0
script: |
const request = async function(req, opts) {
try {
return await req(opts)
} catch(err) {
opts._attempt = (opts._attempt || 0) + 1
if (err.status === 403) {
if (err.response.headers['x-ratelimit-remaining'] === '0') {
const retryAfter = err.response.headers['x-ratelimit-reset'] - Math.floor(Date.now() / 1000) || 1
core.info(`Rate limit exceeded, retrying in ${retryAfter} seconds`)
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
return request(req, opts)
}
if (err.message.toLowerCase().includes('secondary rate limit')) {
const retryAfter = Math.pow(2, opts._attempt)
core.info(`Secondary rate limit exceeded, retrying in ${retryAfter} seconds`)
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
return request(req, opts)
}
}
throw err
}
}
github.hook.wrap('request', request)
core.info(`Looking for repositories the user has direct access to`)
const items = await github.paginate(github.rest.repos.listForAuthenticatedUser, {
affiliation: 'collaborator'
})
core.info(`Filtering out the repositories without unmerged branches`)
const repoBranches = []
for (const item of items) {
if (item.archived) {
core.info(`Skipping archived repository ${item.html_url}`)
continue
}
core.info(`Looking for matching branches for ${item.html_url}`)
const branches = (await github.paginate(github.rest.repos.listBranches, {
owner: item.owner.login,
repo: item.name
})).filter(branch => {
if (process.env.BRANCH) {
return branch.name == process.env.BRANCH
} else {
return branch.name.startsWith('uci/')
}
})
for (const branch of branches) {
core.info(`Checking if ${item.html_url}/tree/${branch.name} requires a PR to be created`)
const {data: pulls} = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: item.owner.login,
repo: item.name,
commit_sha: branch.commit.sha
})
if (pulls.length == 0) {
core.info(`PR for ${item.html_url}/tree/${branch.name} should be created`)
repoBranches.push({repo: item, branch})
} else {
core.info(`PR for ${item.html_url}/tree/${branch.name} already exists`)
}
}
}
core.info(`Attempting to create the PRs`)
const failed = []
for (const {repo, branch} of repoBranches) {
core.info(`Creating PR for ${repo.html_url}/tree/${branch.name}`)
if (process.env.DRY_RUN == 'true') {
core.info(`Skipping PR creation because this is a dry run`)
continue
}
try {
const pr = await github.rest.pulls.create({
owner: repo.owner.login,
repo: repo.name,
head: branch.name,
base: repo.default_branch,
title: `ci: ${branch.name}`,
body: `This PR was created automatically by the @web3-bot as a part of the [Unified CI](https://github.com/ipdxco/unified-github-workflows) project.`
})
core.info(`${pr.data.html_url} created successfully`)
} catch(error) {
core.error(`Couldn't create a PR for ${repo.html_url}/tree/${branch.name}, got: ${error}`)
failed.push({repo, branch})
}
}
if (failed.length != 0) {
throw new Error(`Failed to create ${failed.length} PRs`)
}