mirror of
https://forgejo.stefka.eu/jiriks74/create-pull-request.git
synced 2025-01-18 16:01:06 +01:00
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:
parent
0c2a66fe4a
commit
4320041ed3
20 changed files with 32759 additions and 8594 deletions
|
@ -1,7 +1,8 @@
|
|||
import {
|
||||
createOrUpdateBranch,
|
||||
tryFetch,
|
||||
getWorkingBaseAndType
|
||||
getWorkingBaseAndType,
|
||||
buildBranchCommits
|
||||
} from '../lib/create-or-update-branch'
|
||||
import * as fs from 'fs'
|
||||
import {GitCommandManager} from '../lib/git-command-manager'
|
||||
|
@ -229,6 +230,77 @@ describe('create-or-update-branch tests', () => {
|
|||
expect(workingBaseType).toEqual('commit')
|
||||
})
|
||||
|
||||
it('tests buildBranchCommits with no diff', async () => {
|
||||
await git.checkout(BRANCH, BASE)
|
||||
const branchCommits = await buildBranchCommits(git, BASE, BRANCH)
|
||||
expect(branchCommits.length).toEqual(0)
|
||||
})
|
||||
|
||||
it('tests buildBranchCommits with addition and modification', async () => {
|
||||
await git.checkout(BRANCH, BASE)
|
||||
await createChanges()
|
||||
const UNTRACKED_EXE_FILE = 'a/script.sh'
|
||||
const filepath = path.join(REPO_PATH, UNTRACKED_EXE_FILE)
|
||||
await fs.promises.writeFile(filepath, '#!/usr/bin/env bash', {mode: 0o755})
|
||||
await git.exec(['add', '-A'])
|
||||
await git.commit(['-m', 'Test changes'])
|
||||
|
||||
const branchCommits = await buildBranchCommits(git, BASE, BRANCH)
|
||||
|
||||
expect(branchCommits.length).toEqual(1)
|
||||
expect(branchCommits[0].subject).toEqual('Test changes')
|
||||
expect(branchCommits[0].changes.length).toEqual(3)
|
||||
expect(branchCommits[0].changes).toEqual([
|
||||
{mode: '100755', path: UNTRACKED_EXE_FILE, status: 'A'},
|
||||
{mode: '100644', path: TRACKED_FILE, status: 'M'},
|
||||
{mode: '100644', path: UNTRACKED_FILE, status: 'A'}
|
||||
])
|
||||
})
|
||||
|
||||
it('tests buildBranchCommits with addition and deletion', async () => {
|
||||
await git.checkout(BRANCH, BASE)
|
||||
await createChanges()
|
||||
const TRACKED_FILE_NEW_PATH = 'c/tracked-file.txt'
|
||||
const filepath = path.join(REPO_PATH, TRACKED_FILE_NEW_PATH)
|
||||
await fs.promises.mkdir(path.dirname(filepath), {recursive: true})
|
||||
await fs.promises.rename(path.join(REPO_PATH, TRACKED_FILE), filepath)
|
||||
await git.exec(['add', '-A'])
|
||||
await git.commit(['-m', 'Test changes'])
|
||||
|
||||
const branchCommits = await buildBranchCommits(git, BASE, BRANCH)
|
||||
|
||||
expect(branchCommits.length).toEqual(1)
|
||||
expect(branchCommits[0].subject).toEqual('Test changes')
|
||||
expect(branchCommits[0].changes.length).toEqual(3)
|
||||
expect(branchCommits[0].changes).toEqual([
|
||||
{mode: '100644', path: TRACKED_FILE, status: 'D'},
|
||||
{mode: '100644', path: UNTRACKED_FILE, status: 'A'},
|
||||
{mode: '100644', path: TRACKED_FILE_NEW_PATH, status: 'A'}
|
||||
])
|
||||
})
|
||||
|
||||
it('tests buildBranchCommits with multiple commits', async () => {
|
||||
await git.checkout(BRANCH, BASE)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await createChanges()
|
||||
await git.exec(['add', '-A'])
|
||||
await git.commit(['-m', `Test changes ${i}`])
|
||||
}
|
||||
|
||||
const branchCommits = await buildBranchCommits(git, BASE, BRANCH)
|
||||
|
||||
expect(branchCommits.length).toEqual(3)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
expect(branchCommits[i].subject).toEqual(`Test changes ${i}`)
|
||||
expect(branchCommits[i].changes.length).toEqual(2)
|
||||
const untrackedFileStatus = i == 0 ? 'A' : 'M'
|
||||
expect(branchCommits[i].changes).toEqual([
|
||||
{mode: '100644', path: TRACKED_FILE, status: 'M'},
|
||||
{mode: '100644', path: UNTRACKED_FILE, status: untrackedFileStatus}
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
it('tests no changes resulting in no new branch being created', async () => {
|
||||
const commitMessage = uuidv4()
|
||||
const result = await createOrUpdateBranch(
|
||||
|
@ -585,6 +657,76 @@ describe('create-or-update-branch tests', () => {
|
|||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('tests create, commit with partial changes on the base, and update', async () => {
|
||||
// This is an edge case where the changes for a single commit are partially merged to the base
|
||||
|
||||
// Create tracked and untracked file changes
|
||||
const changes = await createChanges()
|
||||
const commitMessage = uuidv4()
|
||||
const result = await createOrUpdateBranch(
|
||||
git,
|
||||
commitMessage,
|
||||
'',
|
||||
BRANCH,
|
||||
REMOTE_NAME,
|
||||
false,
|
||||
ADD_PATHS_DEFAULT
|
||||
)
|
||||
await git.checkout(BRANCH)
|
||||
expect(result.action).toEqual('created')
|
||||
expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked)
|
||||
expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked)
|
||||
expect(
|
||||
await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE])
|
||||
).toBeTruthy()
|
||||
|
||||
// Push pull request branch to remote
|
||||
await git.push([
|
||||
'--force-with-lease',
|
||||
REMOTE_NAME,
|
||||
`HEAD:refs/heads/${BRANCH}`
|
||||
])
|
||||
|
||||
await afterTest(false)
|
||||
await beforeTest()
|
||||
|
||||
// Create a commit on the base with a partial merge of the changes
|
||||
await createFile(TRACKED_FILE, changes.tracked)
|
||||
const baseCommitMessage = uuidv4()
|
||||
await git.exec(['add', '-A'])
|
||||
await git.commit(['-m', baseCommitMessage])
|
||||
await git.push([
|
||||
'--force',
|
||||
REMOTE_NAME,
|
||||
`HEAD:refs/heads/${DEFAULT_BRANCH}`
|
||||
])
|
||||
|
||||
// Create the same tracked and untracked file changes
|
||||
const _changes = await createChanges(changes.tracked, changes.untracked)
|
||||
const _commitMessage = uuidv4()
|
||||
const _result = await createOrUpdateBranch(
|
||||
git,
|
||||
_commitMessage,
|
||||
'',
|
||||
BRANCH,
|
||||
REMOTE_NAME,
|
||||
false,
|
||||
ADD_PATHS_DEFAULT
|
||||
)
|
||||
await git.checkout(BRANCH)
|
||||
expect(_result.action).toEqual('updated')
|
||||
expect(_result.hasDiffWithBase).toBeTruthy()
|
||||
expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked)
|
||||
expect(await getFileContent(UNTRACKED_FILE)).toEqual(_changes.untracked)
|
||||
expect(
|
||||
await gitLogMatches([
|
||||
_commitMessage,
|
||||
baseCommitMessage,
|
||||
INIT_COMMIT_MESSAGE
|
||||
])
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('tests create, squash merge, and update with identical changes', async () => {
|
||||
// Branches that have been squash merged appear to have a diff with the base due to
|
||||
// different commits for the same changes. To prevent creating pull requests
|
||||
|
@ -1607,6 +1749,81 @@ describe('create-or-update-branch tests', () => {
|
|||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('tests create, commit with partial changes on the base, and update (WBNB)', async () => {
|
||||
// This is an edge case where the changes for a single commit are partially merged to the base
|
||||
|
||||
// Set the working base to a branch that is not the pull request base
|
||||
await git.checkout(NOT_BASE_BRANCH)
|
||||
|
||||
// Create tracked and untracked file changes
|
||||
const changes = await createChanges()
|
||||
const commitMessage = uuidv4()
|
||||
const result = await createOrUpdateBranch(
|
||||
git,
|
||||
commitMessage,
|
||||
BASE,
|
||||
BRANCH,
|
||||
REMOTE_NAME,
|
||||
false,
|
||||
ADD_PATHS_DEFAULT
|
||||
)
|
||||
await git.checkout(BRANCH)
|
||||
expect(result.action).toEqual('created')
|
||||
expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked)
|
||||
expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked)
|
||||
expect(
|
||||
await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE])
|
||||
).toBeTruthy()
|
||||
|
||||
// Push pull request branch to remote
|
||||
await git.push([
|
||||
'--force-with-lease',
|
||||
REMOTE_NAME,
|
||||
`HEAD:refs/heads/${BRANCH}`
|
||||
])
|
||||
|
||||
await afterTest(false)
|
||||
await beforeTest()
|
||||
|
||||
// Create a commit on the base with a partial merge of the changes
|
||||
await createFile(TRACKED_FILE, changes.tracked)
|
||||
const baseCommitMessage = uuidv4()
|
||||
await git.exec(['add', '-A'])
|
||||
await git.commit(['-m', baseCommitMessage])
|
||||
await git.push([
|
||||
'--force',
|
||||
REMOTE_NAME,
|
||||
`HEAD:refs/heads/${DEFAULT_BRANCH}`
|
||||
])
|
||||
|
||||
// Set the working base to a branch that is not the pull request base
|
||||
await git.checkout(NOT_BASE_BRANCH)
|
||||
|
||||
// Create the same tracked and untracked file changes
|
||||
const _changes = await createChanges(changes.tracked, changes.untracked)
|
||||
const _commitMessage = uuidv4()
|
||||
const _result = await createOrUpdateBranch(
|
||||
git,
|
||||
_commitMessage,
|
||||
BASE,
|
||||
BRANCH,
|
||||
REMOTE_NAME,
|
||||
false,
|
||||
ADD_PATHS_DEFAULT
|
||||
)
|
||||
await git.checkout(BRANCH)
|
||||
expect(_result.action).toEqual('updated')
|
||||
expect(_result.hasDiffWithBase).toBeTruthy()
|
||||
expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked)
|
||||
expect(await getFileContent(UNTRACKED_FILE)).toEqual(_changes.untracked)
|
||||
expect(
|
||||
await gitLogMatches([
|
||||
_commitMessage,
|
||||
baseCommitMessage // fetch depth of base is 1
|
||||
])
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('tests create, squash merge, and update with identical changes (WBNB)', async () => {
|
||||
// Branches that have been squash merged appear to have a diff with the base due to
|
||||
// different commits for the same changes. To prevent creating pull requests
|
||||
|
|
|
@ -13,7 +13,7 @@ git daemon --verbose --enable=receive-pack --base-path=/git/remote --export-all
|
|||
# Give the daemon time to start
|
||||
sleep 2
|
||||
|
||||
# Create a local clone and make an initial commit
|
||||
# Create a local clone and make initial commits
|
||||
mkdir -p /git/local/repos
|
||||
git clone git://127.0.0.1/repos/test-base.git /git/local/repos/test-base
|
||||
cd /git/local/repos/test-base
|
||||
|
@ -22,6 +22,11 @@ git config --global user.name "Your Name"
|
|||
echo "#test-base" > README.md
|
||||
git add .
|
||||
git commit -m "initial commit"
|
||||
git commit --allow-empty -m "empty commit for tests"
|
||||
echo "#test-base :sparkles:" > README.md
|
||||
git add .
|
||||
git commit -m "add sparkles" -m "Change description:
|
||||
- updates README.md to add sparkles to the title"
|
||||
git push -u
|
||||
git log -1 --pretty=oneline
|
||||
git config --global --unset user.email
|
||||
|
|
37
__test__/git-command-manager.int.test.ts
Normal file
37
__test__/git-command-manager.int.test.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import {GitCommandManager} from '../lib/git-command-manager'
|
||||
|
||||
const REPO_PATH = '/git/local/repos/test-base'
|
||||
|
||||
describe('git-command-manager integration tests', () => {
|
||||
let git: GitCommandManager
|
||||
|
||||
beforeAll(async () => {
|
||||
git = await GitCommandManager.create(REPO_PATH)
|
||||
await git.checkout('main')
|
||||
})
|
||||
|
||||
it('tests getCommit', async () => {
|
||||
const initialCommit = await git.getCommit('HEAD^^')
|
||||
const emptyCommit = await git.getCommit('HEAD^')
|
||||
const headCommit = await git.getCommit('HEAD')
|
||||
|
||||
expect(initialCommit.subject).toEqual('initial commit')
|
||||
expect(initialCommit.signed).toBeFalsy()
|
||||
expect(initialCommit.changes).toEqual([
|
||||
{mode: '100644', status: 'A', path: 'README.md'}
|
||||
])
|
||||
|
||||
expect(emptyCommit.subject).toEqual('empty commit for tests')
|
||||
expect(emptyCommit.tree).toEqual(initialCommit.tree) // empty commits have no tree and reference the parent's
|
||||
expect(emptyCommit.parents[0]).toEqual(initialCommit.sha)
|
||||
expect(emptyCommit.signed).toBeFalsy()
|
||||
expect(emptyCommit.changes).toEqual([])
|
||||
|
||||
expect(headCommit.subject).toEqual('add sparkles')
|
||||
expect(headCommit.parents[0]).toEqual(emptyCommit.sha)
|
||||
expect(headCommit.signed).toBeFalsy()
|
||||
expect(headCommit.changes).toEqual([
|
||||
{mode: '100644', status: 'M', path: 'README.md'}
|
||||
])
|
||||
})
|
||||
})
|
|
@ -7,7 +7,6 @@ const extraheaderConfigKey = 'http.https://127.0.0.1/.extraheader'
|
|||
|
||||
describe('git-config-helper integration tests', () => {
|
||||
let git: GitCommandManager
|
||||
let gitConfigHelper: GitConfigHelper
|
||||
|
||||
beforeAll(async () => {
|
||||
git = await GitCommandManager.create(REPO_PATH)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue