2020-07-16 10:57:13 +02:00
|
|
|
import * as core from '@actions/core'
|
|
|
|
import {createOrUpdateBranch} from './create-or-update-branch'
|
|
|
|
import {GitHubHelper} from './github-helper'
|
|
|
|
import {GitCommandManager} from './git-command-manager'
|
2020-07-17 13:54:39 +02:00
|
|
|
import {GitAuthHelper} from './git-auth-helper'
|
2020-07-16 10:57:13 +02:00
|
|
|
import {GitIdentityHelper} from './git-identity-helper'
|
|
|
|
import * as utils from './utils'
|
|
|
|
|
|
|
|
const DEFAULT_COMMIT_MESSAGE = '[create-pull-request] automated change'
|
|
|
|
const DEFAULT_TITLE = 'Changes by create-pull-request action'
|
|
|
|
const DEFAULT_BODY =
|
|
|
|
'Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action'
|
|
|
|
const DEFAULT_BRANCH = 'create-pull-request/patch'
|
|
|
|
|
|
|
|
export interface Inputs {
|
|
|
|
token: string
|
|
|
|
path: string
|
|
|
|
commitMessage: string
|
|
|
|
committer: string
|
|
|
|
author: string
|
|
|
|
title: string
|
|
|
|
body: string
|
|
|
|
labels: string[]
|
|
|
|
assignees: string[]
|
|
|
|
reviewers: string[]
|
|
|
|
teamReviewers: string[]
|
|
|
|
milestone: number
|
|
|
|
draft: boolean
|
|
|
|
branch: string
|
2020-07-18 08:33:46 +02:00
|
|
|
pushToFork: string
|
2020-07-16 10:57:13 +02:00
|
|
|
base: string
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function createPullRequest(inputs: Inputs): Promise<void> {
|
2020-07-17 13:54:39 +02:00
|
|
|
let gitAuthHelper
|
2020-07-16 10:57:13 +02:00
|
|
|
try {
|
|
|
|
// Get the repository path
|
|
|
|
const repoPath = utils.getRepoPath(inputs.path)
|
|
|
|
// Create a git command manager
|
|
|
|
const git = await GitCommandManager.create(repoPath)
|
2020-07-16 12:13:28 +02:00
|
|
|
|
2020-07-17 13:54:39 +02:00
|
|
|
// Save and unset the extraheader auth config if it exists
|
2020-07-16 12:13:28 +02:00
|
|
|
core.startGroup('Save persisted git credentials')
|
2020-07-17 13:54:39 +02:00
|
|
|
gitAuthHelper = new GitAuthHelper(git)
|
|
|
|
await gitAuthHelper.savePersistedAuth()
|
2020-07-16 12:13:28 +02:00
|
|
|
core.endGroup()
|
2020-07-16 10:57:13 +02:00
|
|
|
|
|
|
|
// Set defaults
|
|
|
|
inputs.commitMessage = inputs.commitMessage
|
|
|
|
? inputs.commitMessage
|
|
|
|
: DEFAULT_COMMIT_MESSAGE
|
|
|
|
inputs.title = inputs.title ? inputs.title : DEFAULT_TITLE
|
|
|
|
inputs.body = inputs.body ? inputs.body : DEFAULT_BODY
|
|
|
|
inputs.branch = inputs.branch ? inputs.branch : DEFAULT_BRANCH
|
|
|
|
|
2020-07-18 08:33:46 +02:00
|
|
|
// Init the GitHub client
|
|
|
|
const githubHelper = new GitHubHelper(inputs.token)
|
|
|
|
|
|
|
|
core.startGroup('Determining the base and head repositories')
|
|
|
|
// Determine the base repository from git config
|
2020-07-17 13:54:39 +02:00
|
|
|
const remoteUrl = await git.tryGetRemoteUrl()
|
2020-07-18 08:33:46 +02:00
|
|
|
const baseRemote = utils.getRemoteDetail(remoteUrl)
|
|
|
|
// Determine the head repository; the target for the pull request branch
|
|
|
|
const branchRemoteName = inputs.pushToFork ? 'fork' : 'origin'
|
|
|
|
const branchRepository = inputs.pushToFork
|
|
|
|
? inputs.pushToFork
|
|
|
|
: baseRemote.repository
|
|
|
|
if (inputs.pushToFork) {
|
|
|
|
// Check if the supplied fork is really a fork of the base
|
|
|
|
const parentRepository = await githubHelper.getRepositoryParent(
|
|
|
|
branchRepository
|
|
|
|
)
|
|
|
|
if (parentRepository != baseRemote.repository) {
|
|
|
|
throw new Error(
|
|
|
|
`Repository '${branchRepository}' is not a fork of '${baseRemote.repository}'. Unable to continue.`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
// Add a remote for the fork
|
|
|
|
const remoteUrl = utils.getRemoteUrl(
|
|
|
|
baseRemote.protocol,
|
|
|
|
branchRepository
|
|
|
|
)
|
|
|
|
await git.exec(['remote', 'add', 'fork', remoteUrl])
|
|
|
|
}
|
2020-07-16 12:13:28 +02:00
|
|
|
core.endGroup()
|
2020-07-16 10:57:13 +02:00
|
|
|
core.info(
|
2020-07-18 08:33:46 +02:00
|
|
|
`Pull request branch target repository set to ${branchRepository}`
|
2020-07-16 10:57:13 +02:00
|
|
|
)
|
|
|
|
|
2020-07-18 08:33:46 +02:00
|
|
|
// Configure auth
|
|
|
|
if (baseRemote.protocol == 'HTTPS') {
|
2020-07-16 12:13:28 +02:00
|
|
|
core.startGroup('Configuring credential for HTTPS authentication')
|
2020-07-17 14:06:16 +02:00
|
|
|
await gitAuthHelper.configureToken(inputs.token)
|
2020-07-16 12:13:28 +02:00
|
|
|
core.endGroup()
|
2020-07-16 10:57:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Determine if the checked out ref is a valid base for a pull request
|
|
|
|
// The action needs the checked out HEAD ref to be a branch
|
|
|
|
// This check will fail in the following cases:
|
|
|
|
// - HEAD is detached
|
|
|
|
// - HEAD is a merge commit (pull_request events)
|
|
|
|
// - HEAD is a tag
|
2020-07-16 12:13:28 +02:00
|
|
|
core.startGroup('Checking the checked out ref')
|
2020-07-16 10:57:13 +02:00
|
|
|
const symbolicRefResult = await git.exec(
|
|
|
|
['symbolic-ref', 'HEAD', '--short'],
|
|
|
|
true
|
|
|
|
)
|
|
|
|
if (symbolicRefResult.exitCode != 0) {
|
|
|
|
core.debug(`${symbolicRefResult.stderr}`)
|
|
|
|
throw new Error(
|
|
|
|
'The checked out ref is not a valid base for a pull request. Unable to continue.'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
const workingBase = symbolicRefResult.stdout.trim()
|
|
|
|
// Exit if the working base is a PR branch created by this action.
|
|
|
|
// This may occur when using a PAT instead of GITHUB_TOKEN because
|
|
|
|
// a PAT allows workflow actions to trigger further events.
|
|
|
|
if (workingBase.startsWith(inputs.branch)) {
|
|
|
|
throw new Error(
|
|
|
|
`Working base branch '${workingBase}' was created by this action. Unable to continue.`
|
|
|
|
)
|
|
|
|
}
|
2020-07-16 12:13:28 +02:00
|
|
|
core.endGroup()
|
2020-07-16 10:57:13 +02:00
|
|
|
|
|
|
|
// Output head branch
|
|
|
|
core.info(
|
|
|
|
`Pull request branch to create or update set to '${inputs.branch}'`
|
|
|
|
)
|
|
|
|
|
|
|
|
// Determine the committer and author
|
2020-07-16 12:13:28 +02:00
|
|
|
core.startGroup('Configuring the committer and author')
|
2020-07-16 10:57:13 +02:00
|
|
|
const gitIdentityHelper = new GitIdentityHelper(git)
|
|
|
|
const identity = await gitIdentityHelper.getIdentity(
|
|
|
|
inputs.author,
|
|
|
|
inputs.committer
|
|
|
|
)
|
|
|
|
git.setIdentityGitOptions([
|
|
|
|
'-c',
|
|
|
|
`author.name=${identity.authorName}`,
|
|
|
|
'-c',
|
|
|
|
`author.email=${identity.authorEmail}`,
|
|
|
|
'-c',
|
|
|
|
`committer.name=${identity.committerName}`,
|
|
|
|
'-c',
|
|
|
|
`committer.email=${identity.committerEmail}`
|
|
|
|
])
|
|
|
|
core.info(
|
|
|
|
`Configured git committer as '${identity.committerName} <${identity.committerEmail}>'`
|
|
|
|
)
|
|
|
|
core.info(
|
|
|
|
`Configured git author as '${identity.authorName} <${identity.authorEmail}>'`
|
|
|
|
)
|
2020-07-16 12:13:28 +02:00
|
|
|
core.endGroup()
|
2020-07-16 10:57:13 +02:00
|
|
|
|
|
|
|
// Create or update the pull request branch
|
2020-07-16 12:13:28 +02:00
|
|
|
core.startGroup('Create or update the pull request branch')
|
2020-07-16 10:57:13 +02:00
|
|
|
const result = await createOrUpdateBranch(
|
|
|
|
git,
|
|
|
|
inputs.commitMessage,
|
|
|
|
inputs.base,
|
2020-07-18 08:33:46 +02:00
|
|
|
inputs.branch,
|
|
|
|
branchRemoteName
|
2020-07-16 10:57:13 +02:00
|
|
|
)
|
2020-07-16 12:13:28 +02:00
|
|
|
core.endGroup()
|
2020-07-16 10:57:13 +02:00
|
|
|
|
|
|
|
if (['created', 'updated'].includes(result.action)) {
|
|
|
|
// The branch was created or updated
|
2020-07-16 12:13:28 +02:00
|
|
|
core.startGroup(
|
2020-07-18 08:33:46 +02:00
|
|
|
`Pushing pull request branch to '${branchRemoteName}/${inputs.branch}'`
|
2020-07-16 12:13:28 +02:00
|
|
|
)
|
2020-07-17 03:37:09 +02:00
|
|
|
await git.push([
|
|
|
|
'--force-with-lease',
|
2020-07-18 08:33:46 +02:00
|
|
|
branchRemoteName,
|
2020-07-17 03:37:09 +02:00
|
|
|
`HEAD:refs/heads/${inputs.branch}`
|
|
|
|
])
|
2020-07-16 12:13:28 +02:00
|
|
|
core.endGroup()
|
2020-07-16 10:57:13 +02:00
|
|
|
|
|
|
|
// Set the base. It would have been '' if not specified as an input
|
|
|
|
inputs.base = result.base
|
|
|
|
|
|
|
|
if (result.hasDiffWithBase) {
|
|
|
|
// Create or update the pull request
|
2020-07-18 08:33:46 +02:00
|
|
|
await githubHelper.createOrUpdatePullRequest(
|
|
|
|
inputs,
|
|
|
|
baseRemote.repository,
|
|
|
|
branchRepository
|
|
|
|
)
|
2020-07-16 10:57:13 +02:00
|
|
|
} else {
|
|
|
|
// If there is no longer a diff with the base delete the branch
|
|
|
|
core.info(
|
|
|
|
`Branch '${inputs.branch}' no longer differs from base branch '${inputs.base}'`
|
|
|
|
)
|
|
|
|
core.info(`Closing pull request and deleting branch '${inputs.branch}'`)
|
|
|
|
await git.push([
|
|
|
|
'--delete',
|
|
|
|
'--force',
|
2020-07-18 08:33:46 +02:00
|
|
|
branchRemoteName,
|
2020-07-16 10:57:13 +02:00
|
|
|
`refs/heads/${inputs.branch}`
|
|
|
|
])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
core.setFailed(error.message)
|
|
|
|
} finally {
|
2020-07-17 13:54:39 +02:00
|
|
|
// Remove auth and restore persisted auth config if it existed
|
2020-07-16 12:13:28 +02:00
|
|
|
core.startGroup('Restore persisted git credentials')
|
2020-07-17 13:54:39 +02:00
|
|
|
await gitAuthHelper.removeAuth()
|
|
|
|
await gitAuthHelper.restorePersistedAuth()
|
2020-07-16 12:13:28 +02:00
|
|
|
core.endGroup()
|
2020-07-16 10:57:13 +02:00
|
|
|
}
|
|
|
|
}
|