feat: signed commits (v7) (#3057)
* Add support for signed commits (#3055) * formatting * fix eslint and lint errors * shift setting the base to before the push * sign commits by default for testing * add debug lines * read to buffer not string and use non-legacy method to base64 * debug payload without contents * disable linter for debug code * fix filepath when using path input * try to fix head repo * remove commented code * Try refactor of file changes * add tests for building file changes * add build file changes test for binary files * refactor graphql code into github helper class * build file changes even when there is no diff * add function to get commit detail * fix format * build branch commits * use source mode for deleted files * try rest api route * fix check for branch existence * force push * try fix base tree * debug commit verification * debug commit verification * fix format and cleanup * add executable mode file to test * limit blob creation concurrency * only build commits when feature enabled * remove unused code * update readme link * update docs for commit signing * fix capital letter * update docs * add throttling * set default back to false * output head sha and verified status * log outputs * fix head sha output * default the operation output to none * output retryafter for secondary rate limit * use separate client for branch and pull operations * add maintainer-can-modify input * rename git-token to branch-token * fix branch token input * remove deprecated env output * update docs * fix doc * update docs * build branch commits when there is a diff with the base * check verification status of head commit when not known * fix verified output when no commit signing is being used * draft always-true * convert to draft on branch updates when there is a diff with base * update docs with blob size limit * catch errors during blob creation for debugging * parse empty commits * pass base commit to push signed commits * use parent commit details in create commit * use parent tree for base_tree * multipart tree creation * update docs * update readme about the permissions of the default token * fix edge case where changes are partially merged * add updating documentation * fix typo * update major version --------- Co-authored-by: Ravi <1299606+rustycl0ck@users.noreply.github.com>
This commit is contained in:
@@ -11,7 +11,7 @@ import * as utils from './utils'
|
||||
|
||||
export interface Inputs {
|
||||
token: string
|
||||
gitToken: string
|
||||
branchToken: string
|
||||
path: string
|
||||
addPaths: string[]
|
||||
commitMessage: string
|
||||
@@ -23,6 +23,7 @@ export interface Inputs {
|
||||
branchSuffix: string
|
||||
base: string
|
||||
pushToFork: string
|
||||
signCommits: boolean
|
||||
title: string
|
||||
body: string
|
||||
bodyPath: string
|
||||
@@ -31,7 +32,11 @@ export interface Inputs {
|
||||
reviewers: string[]
|
||||
teamReviewers: string[]
|
||||
milestone: number
|
||||
draft: boolean
|
||||
draft: {
|
||||
value: boolean
|
||||
always: boolean
|
||||
}
|
||||
maintainerCanModify: boolean
|
||||
}
|
||||
|
||||
export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||
@@ -45,8 +50,9 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||
|
||||
core.startGroup('Determining the base and head repositories')
|
||||
const baseRemote = gitConfigHelper.getGitRemote()
|
||||
// Init the GitHub client
|
||||
const githubHelper = new GitHubHelper(baseRemote.hostname, inputs.token)
|
||||
// Init the GitHub clients
|
||||
const ghBranch = new GitHubHelper(baseRemote.hostname, inputs.branchToken)
|
||||
const ghPull = new GitHubHelper(baseRemote.hostname, inputs.token)
|
||||
// Determine the head repository; the target for the pull request branch
|
||||
const branchRemoteName = inputs.pushToFork ? 'fork' : 'origin'
|
||||
const branchRepository = inputs.pushToFork
|
||||
@@ -57,11 +63,11 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||
core.info(
|
||||
`Checking if '${branchRepository}' is a fork of '${baseRemote.repository}'`
|
||||
)
|
||||
const baseParentRepository = await githubHelper.getRepositoryParent(
|
||||
const baseParentRepository = await ghBranch.getRepositoryParent(
|
||||
baseRemote.repository
|
||||
)
|
||||
const branchParentRepository =
|
||||
await githubHelper.getRepositoryParent(branchRepository)
|
||||
await ghBranch.getRepositoryParent(branchRepository)
|
||||
if (branchParentRepository == null) {
|
||||
throw new Error(
|
||||
`Repository '${branchRepository}' is not a fork. Unable to continue.`
|
||||
@@ -91,7 +97,7 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||
// Configure auth
|
||||
if (baseRemote.protocol == 'HTTPS') {
|
||||
core.startGroup('Configuring credential for HTTPS authentication')
|
||||
await gitConfigHelper.configureToken(inputs.gitToken)
|
||||
await gitConfigHelper.configureToken(inputs.branchToken)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
@@ -174,6 +180,11 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||
)
|
||||
core.endGroup()
|
||||
|
||||
// Action outputs
|
||||
const outputs = new Map<string, string>()
|
||||
outputs.set('pull-request-branch', inputs.branch)
|
||||
outputs.set('pull-request-operation', 'none')
|
||||
|
||||
// Create or update the pull request branch
|
||||
core.startGroup('Create or update the pull request branch')
|
||||
const result = await createOrUpdateBranch(
|
||||
@@ -185,6 +196,9 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||
inputs.signoff,
|
||||
inputs.addPaths
|
||||
)
|
||||
outputs.set('pull-request-head-sha', result.headSha)
|
||||
// Set the base. It would have been '' if not specified as an input
|
||||
inputs.base = result.base
|
||||
core.endGroup()
|
||||
|
||||
if (['created', 'updated'].includes(result.action)) {
|
||||
@@ -192,40 +206,55 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||
core.startGroup(
|
||||
`Pushing pull request branch to '${branchRemoteName}/${inputs.branch}'`
|
||||
)
|
||||
await git.push([
|
||||
'--force-with-lease',
|
||||
branchRemoteName,
|
||||
`${inputs.branch}:refs/heads/${inputs.branch}`
|
||||
])
|
||||
if (inputs.signCommits) {
|
||||
// Create signed commits via the GitHub API
|
||||
const stashed = await git.stashPush(['--include-untracked'])
|
||||
await git.checkout(inputs.branch)
|
||||
const pushSignedCommitsResult = await ghBranch.pushSignedCommits(
|
||||
result.branchCommits,
|
||||
result.baseCommit,
|
||||
repoPath,
|
||||
branchRepository,
|
||||
inputs.branch
|
||||
)
|
||||
outputs.set('pull-request-head-sha', pushSignedCommitsResult.sha)
|
||||
outputs.set(
|
||||
'pull-request-commits-verified',
|
||||
pushSignedCommitsResult.verified.toString()
|
||||
)
|
||||
await git.checkout('-')
|
||||
if (stashed) {
|
||||
await git.stashPop()
|
||||
}
|
||||
} else {
|
||||
await git.push([
|
||||
'--force-with-lease',
|
||||
branchRemoteName,
|
||||
`${inputs.branch}:refs/heads/${inputs.branch}`
|
||||
])
|
||||
}
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
// 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
|
||||
core.startGroup('Create or update the pull request')
|
||||
const pull = await githubHelper.createOrUpdatePullRequest(
|
||||
const pull = await ghPull.createOrUpdatePullRequest(
|
||||
inputs,
|
||||
baseRemote.repository,
|
||||
branchRepository
|
||||
)
|
||||
core.endGroup()
|
||||
|
||||
// Set outputs
|
||||
core.startGroup('Setting outputs')
|
||||
core.setOutput('pull-request-number', pull.number)
|
||||
core.setOutput('pull-request-url', pull.html_url)
|
||||
outputs.set('pull-request-number', pull.number.toString())
|
||||
outputs.set('pull-request-url', pull.html_url)
|
||||
if (pull.created) {
|
||||
core.setOutput('pull-request-operation', 'created')
|
||||
outputs.set('pull-request-operation', 'created')
|
||||
} else if (result.action == 'updated') {
|
||||
core.setOutput('pull-request-operation', 'updated')
|
||||
outputs.set('pull-request-operation', 'updated')
|
||||
// The pull request was updated AND the branch was updated.
|
||||
// Convert back to draft if 'draft: always-true' is set.
|
||||
if (inputs.draft.always && pull.draft !== undefined && !pull.draft) {
|
||||
await ghPull.convertToDraft(pull.node_id)
|
||||
}
|
||||
}
|
||||
core.setOutput('pull-request-head-sha', result.headSha)
|
||||
core.setOutput('pull-request-branch', inputs.branch)
|
||||
// Deprecated
|
||||
core.exportVariable('PULL_REQUEST_NUMBER', pull.number)
|
||||
core.endGroup()
|
||||
} else {
|
||||
// There is no longer a diff with the base
|
||||
@@ -242,13 +271,45 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||
branchRemoteName,
|
||||
`refs/heads/${inputs.branch}`
|
||||
])
|
||||
// Set outputs
|
||||
core.startGroup('Setting outputs')
|
||||
core.setOutput('pull-request-operation', 'closed')
|
||||
core.endGroup()
|
||||
outputs.set('pull-request-operation', 'closed')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
core.startGroup('Setting outputs')
|
||||
// If the head commit is signed, get its verification status if we don't already know it.
|
||||
// This can happen if the branch wasn't updated (action = 'not-updated'), or GPG commit signing is in use.
|
||||
if (
|
||||
!outputs.has('pull-request-commits-verified') &&
|
||||
result.branchCommits.length > 0 &&
|
||||
result.branchCommits[result.branchCommits.length - 1].signed
|
||||
) {
|
||||
// Using the local head commit SHA because in this case commits have not been pushed via the API.
|
||||
core.info(`Checking verification status of head commit ${result.headSha}`)
|
||||
try {
|
||||
const headCommit = await ghBranch.getCommit(
|
||||
result.headSha,
|
||||
branchRepository
|
||||
)
|
||||
outputs.set(
|
||||
'pull-request-commits-verified',
|
||||
headCommit.verified.toString()
|
||||
)
|
||||
} catch (error) {
|
||||
core.warning('Failed to check verification status of head commit.')
|
||||
core.debug(utils.getErrorMessage(error))
|
||||
}
|
||||
}
|
||||
if (!outputs.has('pull-request-commits-verified')) {
|
||||
outputs.set('pull-request-commits-verified', 'false')
|
||||
}
|
||||
|
||||
// Set outputs
|
||||
for (const [key, value] of outputs) {
|
||||
core.info(`${key} = ${value}`)
|
||||
core.setOutput(key, value)
|
||||
}
|
||||
core.endGroup()
|
||||
} catch (error) {
|
||||
core.setFailed(utils.getErrorMessage(error))
|
||||
} finally {
|
||||
|
Reference in New Issue
Block a user