import * as core from '@actions/core' import {Inputs} from './create-pull-request' import {Octokit, OctokitOptions} from './octokit-client' const ERROR_PR_REVIEW_FROM_AUTHOR = 'Review cannot be requested from pull request author' interface Repository { owner: string repo: string } interface Pull { number: number html_url: string created: boolean } export class GitHubHelper { private octokit: InstanceType constructor(token: string) { const options: OctokitOptions = {} if (token) { options.auth = `${token}` } options.baseUrl = process.env['GITHUB_API_URL'] || 'https://api.github.com' this.octokit = new Octokit(options) } private parseRepository(repository: string): Repository { const [owner, repo] = repository.split('/') return { owner: owner, repo: repo } } private async createOrUpdate( inputs: Inputs, baseRepository: string, headRepository: string ): Promise { const [headOwner] = headRepository.split('/') const headBranch = `${headOwner}:${inputs.branch}` const headBranchFull = `${headRepository}:${inputs.branch}` // Try to create the pull request try { core.info(`Attempting creation of pull request`) const {data: pull} = await this.octokit.rest.pulls.create({ ...this.parseRepository(baseRepository), title: inputs.title, head: headBranch, base: inputs.base, body: inputs.body, draft: inputs.draft }) core.info( `Created pull request #${pull.number} (${headBranch} => ${inputs.base})` ) return { number: pull.number, html_url: pull.html_url, created: true } } catch (e: any) { if ( e.message && e.message.includes(`A pull request already exists for ${headBranch}`) ) { core.info(`A pull request already exists for ${headBranch}`) } else { throw e } } // Update the pull request that exists for this branch and base core.info(`Fetching existing pull request`) const {data: pulls} = await this.octokit.rest.pulls.list({ ...this.parseRepository(baseRepository), state: 'open', head: headBranchFull, base: inputs.base }) core.info(`Attempting update of pull request`) const {data: pull} = await this.octokit.rest.pulls.update({ ...this.parseRepository(baseRepository), pull_number: pulls[0].number, title: inputs.title, body: inputs.body }) core.info( `Updated pull request #${pull.number} (${headBranch} => ${inputs.base})` ) return { number: pull.number, html_url: pull.html_url, created: false } } async getRepositoryParent(headRepository: string): Promise { const {data: headRepo} = await this.octokit.rest.repos.get({ ...this.parseRepository(headRepository) }) if (!headRepo.parent) { throw new Error( `Repository '${headRepository}' is not a fork. Unable to continue.` ) } return headRepo.parent.full_name } async createOrUpdatePullRequest( inputs: Inputs, baseRepository: string, headRepository: string ): Promise { // Create or update the pull request const pull = await this.createOrUpdate( inputs, baseRepository, headRepository ) // Apply milestone if (inputs.milestone) { core.info(`Applying milestone '${inputs.milestone}'`) await this.octokit.rest.issues.update({ ...this.parseRepository(baseRepository), issue_number: pull.number, milestone: inputs.milestone }) } // Apply labels if (inputs.labels.length > 0) { core.info(`Applying labels '${inputs.labels}'`) await this.octokit.rest.issues.addLabels({ ...this.parseRepository(baseRepository), issue_number: pull.number, labels: inputs.labels }) } // Apply assignees if (inputs.assignees.length > 0) { core.info(`Applying assignees '${inputs.assignees}'`) await this.octokit.rest.issues.addAssignees({ ...this.parseRepository(baseRepository), issue_number: pull.number, assignees: inputs.assignees }) } // Request reviewers and team reviewers const requestReviewersParams = {} if (inputs.reviewers.length > 0) { requestReviewersParams['reviewers'] = inputs.reviewers core.info(`Requesting reviewers '${inputs.reviewers}'`) } if (inputs.teamReviewers.length > 0) { requestReviewersParams['team_reviewers'] = inputs.teamReviewers core.info(`Requesting team reviewers '${inputs.teamReviewers}'`) } if (Object.keys(requestReviewersParams).length > 0) { try { await this.octokit.rest.pulls.requestReviewers({ ...this.parseRepository(baseRepository), pull_number: pull.number, ...requestReviewersParams }) } catch (e: any) { if (e.message && e.message.includes(ERROR_PR_REVIEW_FROM_AUTHOR)) { core.warning(ERROR_PR_REVIEW_FROM_AUTHOR) } else { throw e } } } return pull } }