From 053b5011459e9892435f7535e4adb5f47817a3c0 Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Sat, 18 Jul 2020 15:33:46 +0900 Subject: [PATCH] Redesign from request-to-parent to push-to-fork --- __test__/create-or-update-branch.int.test.ts | 483 ++++++++++++++++--- __test__/entrypoint.sh | 26 +- __test__/git-auth-helper.int.test.ts | 2 +- __test__/utils.unit.test.ts | 8 + action.yml | 5 +- dist/index.js | 87 ++-- src/create-or-update-branch.ts | 23 +- src/create-pull-request.ts | 55 ++- src/github-helper.ts | 23 +- src/main.ts | 2 +- src/utils.ts | 6 + 11 files changed, 563 insertions(+), 157 deletions(-) diff --git a/__test__/create-or-update-branch.int.test.ts b/__test__/create-or-update-branch.int.test.ts index fa49e51..335d20c 100644 --- a/__test__/create-or-update-branch.int.test.ts +++ b/__test__/create-or-update-branch.int.test.ts @@ -4,7 +4,8 @@ import {GitCommandManager} from '../lib/git-command-manager' import * as path from 'path' import {v4 as uuidv4} from 'uuid' -const REPO_PATH = '/git/test-repo' +const REPO_PATH = '/git/local/test-base' +const REMOTE_NAME = 'origin' const TRACKED_FILE = 'tracked-file.txt' const UNTRACKED_FILE = 'untracked-file.txt' @@ -17,6 +18,9 @@ const INIT_COMMIT_MESSAGE = 'Add file to be a tracked file for tests' const BRANCH = 'tests/create-pull-request/patch' const BASE = DEFAULT_BRANCH +const FORK_REMOTE_URL = 'git://127.0.0.1/test-fork.git' +const FORK_REMOTE_NAME = 'fork' + async function createFile(filename: string, content?: string): Promise { const _content = content ? content : uuidv4() const filepath = path.join(REPO_PATH, filename) @@ -103,7 +107,11 @@ describe('create-or-update-branch tests', () => { await createFile(TRACKED_FILE) await git.exec(['add', '-A']) await git.commit(['-m', 'This commit should not appear in pr branches']) - await git.push(['--force', 'origin', `HEAD:refs/heads/${NOT_BASE_BRANCH}`]) + await git.push([ + '--force', + REMOTE_NAME, + `HEAD:refs/heads/${NOT_BASE_BRANCH}` + ]) // Create a new default branch for the test run with a tracked file await git.checkout('master') @@ -111,8 +119,15 @@ describe('create-or-update-branch tests', () => { await createFile(TRACKED_FILE) await git.exec(['add', '-A']) await git.commit(['-m', INIT_COMMIT_MESSAGE]) - await git.push(['--force', 'origin', `HEAD:refs/heads/${DEFAULT_BRANCH}`]) + await git.push([ + '--force', + REMOTE_NAME, + `HEAD:refs/heads/${DEFAULT_BRANCH}` + ]) initCommitHash = await git.revParse('HEAD') + + // Add a remote for the fork + await git.exec(['remote', 'add', FORK_REMOTE_NAME, FORK_REMOTE_URL]) }) async function beforeTest(): Promise { @@ -120,18 +135,21 @@ describe('create-or-update-branch tests', () => { } async function afterTest(deleteRemote = true): Promise { - //await git.exec(['log', '-5', '--format=%H %s']) await git.checkout(DEFAULT_BRANCH) - // Delete PR branch try { + // Get the upstream branch if it exists + const result = await git.exec([ + 'for-each-ref', + `--format=%(upstream:short)`, + `refs/heads/${BRANCH}` + ]) + const upstreamBranch = result.stdout.trim() + // Delete the local branch await git.exec(['branch', '--delete', '--force', BRANCH]) - if (deleteRemote) { - await git.push([ - '--delete', - '--force', - 'origin', - `refs/heads/${BRANCH}` - ]) + // Delete the remote branch + if (deleteRemote && upstreamBranch) { + const remote = upstreamBranch.split('/')[0] + await git.push(['--delete', '--force', remote, `refs/heads/${BRANCH}`]) } } catch { /* empty */ @@ -147,7 +165,11 @@ describe('create-or-update-branch tests', () => { // Reset default branch if it was committed to during the test if ((await git.revParse('HEAD')) != initCommitHash) { await git.exec(['reset', '--hard', initCommitHash]) - await git.push(['--force', 'origin', `HEAD:refs/heads/${DEFAULT_BRANCH}`]) + await git.push([ + '--force', + REMOTE_NAME, + `HEAD:refs/heads/${DEFAULT_BRANCH}` + ]) } }) @@ -167,13 +189,19 @@ describe('create-or-update-branch tests', () => { } it('tests if a branch exists and can be fetched', async () => { - expect(await tryFetch(git, NOT_BASE_BRANCH)).toBeTruthy() - expect(await tryFetch(git, NOT_EXIST_BRANCH)).toBeFalsy() + expect(await tryFetch(git, REMOTE_NAME, NOT_BASE_BRANCH)).toBeTruthy() + expect(await tryFetch(git, REMOTE_NAME, NOT_EXIST_BRANCH)).toBeFalsy() }) it('tests no changes resulting in no new branch being created', async () => { const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('none') expect(await gitLogMatches([INIT_COMMIT_MESSAGE])).toBeTruthy() }) @@ -182,7 +210,13 @@ describe('create-or-update-branch tests', () => { // Create a tracked file change const trackedContent = await createFile(TRACKED_FILE) const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(trackedContent) expect( @@ -192,7 +226,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -202,7 +236,13 @@ describe('create-or-update-branch tests', () => { // Create a tracked file change const _trackedContent = await createFile(TRACKED_FILE) const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() expect(await getFileContent(TRACKED_FILE)).toEqual(_trackedContent) @@ -215,7 +255,13 @@ describe('create-or-update-branch tests', () => { // Create an untracked file change const untrackedContent = await createFile(UNTRACKED_FILE) const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(UNTRACKED_FILE)).toEqual(untrackedContent) expect( @@ -225,7 +271,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -235,7 +281,13 @@ describe('create-or-update-branch tests', () => { // Create an untracked file change const _untrackedContent = await createFile(UNTRACKED_FILE) const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() expect(await getFileContent(UNTRACKED_FILE)).toEqual(_untrackedContent) @@ -250,7 +302,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -261,7 +319,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -271,7 +329,13 @@ describe('create-or-update-branch tests', () => { // Create identical tracked and untracked file changes await createChanges(changes.tracked, changes.untracked) const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('none') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -284,7 +348,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -295,7 +365,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -304,12 +374,22 @@ describe('create-or-update-branch tests', () => { // Create commits on the base const commits = await createCommits(git) - await git.push(['--force', 'origin', `HEAD:refs/heads/${DEFAULT_BRANCH}`]) + await git.push([ + '--force', + REMOTE_NAME, + `HEAD:refs/heads/${DEFAULT_BRANCH}` + ]) // Create tracked and untracked file changes const _changes = await createChanges() const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked) @@ -332,7 +412,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -343,7 +429,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -352,7 +438,13 @@ describe('create-or-update-branch tests', () => { // Running with no update effectively reverts the branch back to match the base const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeFalsy() expect(await getFileContent(TRACKED_FILE)).toEqual(defaultTrackedContent) @@ -365,7 +457,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -376,7 +474,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -385,7 +483,11 @@ describe('create-or-update-branch tests', () => { // Create commits on the base const commits = await createCommits(git) - await git.push(['--force', 'origin', `HEAD:refs/heads/${DEFAULT_BRANCH}`]) + await git.push([ + '--force', + REMOTE_NAME, + `HEAD:refs/heads/${DEFAULT_BRANCH}` + ]) // Create the same tracked and untracked file changes that were made to the base const _changes = await createChanges( @@ -393,7 +495,13 @@ describe('create-or-update-branch tests', () => { commits.changes.untracked ) const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeFalsy() expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked) @@ -407,7 +515,13 @@ describe('create-or-update-branch tests', () => { // Create commits on the working base const commits = await createCommits(git) const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(commits.changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual( @@ -420,7 +534,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -430,7 +544,13 @@ describe('create-or-update-branch tests', () => { // Create commits on the working base const _commits = await createCommits(git) const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() expect(await getFileContent(TRACKED_FILE)).toEqual(_commits.changes.tracked) @@ -448,7 +568,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -463,7 +589,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -475,7 +601,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const _changes = await createChanges() const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked) @@ -495,7 +627,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, '', BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -510,7 +648,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -519,14 +657,24 @@ describe('create-or-update-branch tests', () => { // Create commits on the base const commitsOnBase = await createCommits(git) - await git.push(['--force', 'origin', `HEAD:refs/heads/${DEFAULT_BRANCH}`]) + await git.push([ + '--force', + REMOTE_NAME, + `HEAD:refs/heads/${DEFAULT_BRANCH}` + ]) // Create commits on the working base const _commits = await createCommits(git) // Create tracked and untracked file changes const _changes = await createChanges() const _commitMessage = uuidv4() - const _result = await createOrUpdateBranch(git, _commitMessage, '', BRANCH) + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + REMOTE_NAME + ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked) @@ -541,6 +689,53 @@ describe('create-or-update-branch tests', () => { ).toBeTruthy() }) + it('tests create and update using a different remote from the base', async () => { + // Create tracked and untracked file changes + const changes = await createChanges() + const commitMessage = uuidv4() + const result = await createOrUpdateBranch( + git, + commitMessage, + '', + BRANCH, + FORK_REMOTE_NAME + ) + 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', + FORK_REMOTE_NAME, + `HEAD:refs/heads/${BRANCH}` + ]) + + await afterTest(false) + await beforeTest() + + // Create tracked and untracked file changes + const _changes = await createChanges() + const _commitMessage = uuidv4() + const _result = await createOrUpdateBranch( + git, + _commitMessage, + '', + BRANCH, + FORK_REMOTE_NAME + ) + 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, INIT_COMMIT_MESSAGE]) + ).toBeTruthy() + }) + // Working Base is Not Base (WBNB) it('tests no changes resulting in no new branch being created (WBNB)', async () => { @@ -548,7 +743,13 @@ describe('create-or-update-branch tests', () => { await git.checkout(NOT_BASE_BRANCH) const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('none') expect(await gitLogMatches([INIT_COMMIT_MESSAGE])).toBeTruthy() }) @@ -560,7 +761,13 @@ describe('create-or-update-branch tests', () => { // Create a tracked file change const trackedContent = await createFile(TRACKED_FILE) const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(trackedContent) expect( @@ -570,7 +777,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -587,7 +794,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() @@ -604,7 +812,13 @@ describe('create-or-update-branch tests', () => { // Create an untracked file change const untrackedContent = await createFile(UNTRACKED_FILE) const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(UNTRACKED_FILE)).toEqual(untrackedContent) expect( @@ -614,7 +828,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -631,7 +845,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() @@ -650,7 +865,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -661,7 +882,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -678,7 +899,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('none') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) @@ -695,7 +917,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -706,7 +934,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -715,7 +943,11 @@ describe('create-or-update-branch tests', () => { // Create commits on the base const commits = await createCommits(git) - await git.push(['--force', 'origin', `HEAD:refs/heads/${DEFAULT_BRANCH}`]) + 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) @@ -727,7 +959,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() @@ -754,7 +987,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -765,7 +1004,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -781,7 +1020,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeFalsy() @@ -800,7 +1040,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -811,7 +1057,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -820,7 +1066,11 @@ describe('create-or-update-branch tests', () => { // Create commits on the base const commits = await createCommits(git) - await git.push(['--force', 'origin', `HEAD:refs/heads/${DEFAULT_BRANCH}`]) + 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) @@ -835,7 +1085,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeFalsy() @@ -853,7 +1104,13 @@ describe('create-or-update-branch tests', () => { // Create commits on the working base const commits = await createCommits(git) const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(commits.changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual( @@ -866,7 +1123,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -883,7 +1140,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() @@ -905,7 +1163,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -920,7 +1184,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -939,7 +1203,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() @@ -963,7 +1228,13 @@ describe('create-or-update-branch tests', () => { // Create tracked and untracked file changes const changes = await createChanges() const commitMessage = uuidv4() - const result = await createOrUpdateBranch(git, commitMessage, BASE, BRANCH) + const result = await createOrUpdateBranch( + git, + commitMessage, + BASE, + BRANCH, + REMOTE_NAME + ) expect(result.action).toEqual('created') expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked) expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked) @@ -978,7 +1249,7 @@ describe('create-or-update-branch tests', () => { // Push pull request branch to remote await git.push([ '--force-with-lease', - 'origin', + REMOTE_NAME, `HEAD:refs/heads/${BRANCH}` ]) @@ -987,7 +1258,11 @@ describe('create-or-update-branch tests', () => { // Create commits on the base const commitsOnBase = await createCommits(git) - await git.push(['--force', 'origin', `HEAD:refs/heads/${DEFAULT_BRANCH}`]) + 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) @@ -1001,7 +1276,8 @@ describe('create-or-update-branch tests', () => { git, _commitMessage, BASE, - BRANCH + BRANCH, + REMOTE_NAME ) expect(_result.action).toEqual('updated') expect(_result.hasDiffWithBase).toBeTruthy() @@ -1016,4 +1292,57 @@ describe('create-or-update-branch tests', () => { ]) ).toBeTruthy() }) + + it('tests create and update using a different remote from the base (WBNB)', async () => { + // 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, + FORK_REMOTE_NAME + ) + 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', + FORK_REMOTE_NAME, + `HEAD:refs/heads/${BRANCH}` + ]) + + await afterTest(false) + await beforeTest() + + // 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, + FORK_REMOTE_NAME + ) + 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, INIT_COMMIT_MESSAGE]) + ).toBeTruthy() + }) }) diff --git a/__test__/entrypoint.sh b/__test__/entrypoint.sh index 1cd0560..dd13fe9 100755 --- a/__test__/entrypoint.sh +++ b/__test__/entrypoint.sh @@ -4,29 +4,35 @@ set -euo pipefail # Save the working directory WORKINGDIR=$PWD -# Serve remote repo -mkdir /git -git init --bare /git/test-repo.git -git daemon --verbose --enable=receive-pack --base-path=/git --export-all /git/test-repo.git &>/dev/null & +# Create and serve a remote repo +mkdir -p /git/remote +git init --bare /git/remote/test-base.git +git daemon --verbose --enable=receive-pack --base-path=/git/remote --export-all /git/remote &>/dev/null & # Give the daemon time to start sleep 2 -# Clone and make an initial commit -git clone git://127.0.0.1/test-repo.git /git/test-repo -cd /git/test-repo +# Create a local clone and make an initial commit +mkdir -p /git/local +git clone git://127.0.0.1/test-base.git /git/local/test-base +cd /git/local/test-base git config --global user.email "you@example.com" git config --global user.name "Your Name" -echo "#test-repo" > README.md +echo "#test-base" > README.md git add . git commit -m "initial commit" git push -u +git log -1 --pretty=oneline git config --global --unset user.email git config --global --unset user.name - -# Display config git config -l +# Clone a server-side fork of the base repo +cd $WORKINGDIR +git clone --mirror git://127.0.0.1/test-base.git /git/remote/test-fork.git +cd /git/remote/test-fork.git +git log -1 --pretty=oneline + # Restore the working directory cd $WORKINGDIR diff --git a/__test__/git-auth-helper.int.test.ts b/__test__/git-auth-helper.int.test.ts index 69d5c90..0c888e1 100644 --- a/__test__/git-auth-helper.int.test.ts +++ b/__test__/git-auth-helper.int.test.ts @@ -1,7 +1,7 @@ import {GitCommandManager} from '../lib/git-command-manager' import {GitAuthHelper} from '../lib/git-auth-helper' -const REPO_PATH = '/git/test-repo' +const REPO_PATH = '/git/local/test-base' const extraheaderConfigKey = 'http.https://github.com/.extraheader' diff --git a/__test__/utils.unit.test.ts b/__test__/utils.unit.test.ts index 1c2f0a9..094b0c3 100644 --- a/__test__/utils.unit.test.ts +++ b/__test__/utils.unit.test.ts @@ -65,6 +65,14 @@ describe('utils tests', () => { } }) + test('getRemoteUrl successfully returns remote URLs', async () => { + const url1 = utils.getRemoteUrl('HTTPS', 'peter-evans/create-pull-request') + expect(url1).toEqual('https://github.com/peter-evans/create-pull-request') + + const url2 = utils.getRemoteUrl('SSH', 'peter-evans/create-pull-request') + expect(url2).toEqual('git@github.com:peter-evans/create-pull-request.git') + }) + test('secondsSinceEpoch returns the number of seconds since the Epoch', async () => { const seconds = `${utils.secondsSinceEpoch()}` expect(seconds.length).toEqual(10) diff --git a/action.yml b/action.yml index b9922af..11dc68e 100644 --- a/action.yml +++ b/action.yml @@ -34,9 +34,8 @@ inputs: description: 'Create a draft pull request' branch: description: 'The pull request branch name.' - request-to-parent: - description: 'Create the pull request in the parent repository of the checked out fork.' - default: false + push-to-fork: + description: 'A fork of the checked out base repository to which the pull request branch will be pushed.' base: description: 'The pull request base branch.' branch-suffix: diff --git a/dist/index.js b/dist/index.js index c503cd7..3a80c8d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1012,10 +1012,10 @@ exports.createOrUpdateBranch = exports.tryFetch = void 0; const core = __importStar(__webpack_require__(470)); const uuid_1 = __webpack_require__(62); const CHERRYPICK_EMPTY = 'The previous cherry-pick is now empty, possibly due to conflict resolution.'; -function tryFetch(git, branch) { +function tryFetch(git, remote, branch) { return __awaiter(this, void 0, void 0, function* () { try { - yield git.fetch([`${branch}:refs/remotes/origin/${branch}`]); + yield git.fetch([`${branch}:refs/remotes/${remote}/${branch}`], remote); return true; } catch (_a) { @@ -1057,12 +1057,13 @@ function splitLines(multilineString) { .map(s => s.trim()) .filter(x => x !== ''); } -function createOrUpdateBranch(git, commitMessage, baseInput, branch) { +function createOrUpdateBranch(git, commitMessage, base, branch, branchRemoteName) { return __awaiter(this, void 0, void 0, function* () { // Get the working base. This may or may not be the actual base. const workingBase = yield git.symbolicRef('HEAD', ['--short']); // If the base is not specified it is assumed to be the working base. - const base = baseInput ? baseInput : workingBase; + base = base ? base : workingBase; + const baseRemote = 'origin'; // Set the default return values const result = { action: 'none', @@ -1080,12 +1081,12 @@ function createOrUpdateBranch(git, commitMessage, baseInput, branch) { } // Perform fetch and reset the working base // Commits made during the workflow will be removed - yield git.fetch([`${workingBase}:${workingBase}`], 'origin', ['--force']); + yield git.fetch([`${workingBase}:${workingBase}`], baseRemote, ['--force']); // If the working base is not the base, rebase the temp branch commits if (workingBase != base) { core.info(`Rebasing commits made to branch '${workingBase}' on to base branch '${base}'`); // Checkout the actual base - yield git.fetch([`${base}:${base}`], 'origin', ['--force']); + yield git.fetch([`${base}:${base}`], baseRemote, ['--force']); yield git.checkout(base); // Cherrypick commits from the temporary branch starting from the working base const commits = yield git.revList([`${workingBase}..${tempBranch}`, '.'], ['--reverse']); @@ -1098,10 +1099,10 @@ function createOrUpdateBranch(git, commitMessage, baseInput, branch) { // Reset the temp branch to the working index yield git.checkout(tempBranch, 'HEAD'); // Reset the base - yield git.fetch([`${base}:${base}`], 'origin', ['--force']); + yield git.fetch([`${base}:${base}`], baseRemote, ['--force']); } // Try to fetch the pull request branch - if (!(yield tryFetch(git, branch))) { + if (!(yield tryFetch(git, branchRemoteName, branch))) { // The pull request branch does not exist core.info(`Pull request branch '${branch}' does not exist yet.`); // Create the pull request branch @@ -1118,7 +1119,7 @@ function createOrUpdateBranch(git, commitMessage, baseInput, branch) { } else { // The pull request branch exists - core.info(`Pull request branch '${branch}' already exists as remote branch 'origin/${branch}'`); + core.info(`Pull request branch '${branch}' already exists as remote branch '${branchRemoteName}/${branch}'`); // Checkout the pull request branch yield git.checkout(branch); if (yield hasDiff(git, branch, tempBranch)) { @@ -1131,7 +1132,7 @@ function createOrUpdateBranch(git, commitMessage, baseInput, branch) { // Check if the pull request branch has been updated // If the branch was reset or updated it will be ahead // It may be behind if a reset now results in no diff with the base - if (!(yield isEven(git, `origin/${branch}`, branch))) { + if (!(yield isEven(git, `${branchRemoteName}/${branch}`, branch))) { result.action = 'updated'; core.info(`Updated branch '${branch}'`); } @@ -1304,7 +1305,7 @@ function run() { milestone: Number(core.getInput('milestone')), draft: core.getInput('draft') === 'true', branch: core.getInput('branch'), - requestToParent: core.getInput('request-to-parent') === 'true', + pushToFork: core.getInput('push-to-fork'), base: core.getInput('base'), branchSuffix: core.getInput('branch-suffix') }; @@ -6916,7 +6917,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseDisplayNameEmail = exports.randomString = exports.secondsSinceEpoch = exports.getRemoteDetail = exports.getRepoPath = exports.getStringAsArray = exports.getInputAsArray = void 0; +exports.parseDisplayNameEmail = exports.randomString = exports.secondsSinceEpoch = exports.getRemoteUrl = exports.getRemoteDetail = exports.getRepoPath = exports.getStringAsArray = exports.getInputAsArray = void 0; const core = __importStar(__webpack_require__(470)); const path = __importStar(__webpack_require__(622)); function getInputAsArray(name, options) { @@ -6966,6 +6967,12 @@ function getRemoteDetail(remoteUrl) { throw new Error(`The format of '${remoteUrl}' is not a valid GitHub repository URL`); } exports.getRemoteDetail = getRemoteDetail; +function getRemoteUrl(protocol, repository) { + return protocol == 'HTTPS' + ? `https://github.com/${repository}` + : `git@github.com:${repository}.git`; +} +exports.getRemoteUrl = getRemoteUrl; function secondsSinceEpoch() { const now = new Date(); return Math.round(now.getTime() / 1000); @@ -8139,16 +8146,19 @@ class GitHubHelper { return pull.number; }); } - createOrUpdatePullRequest(inputs, headRepository) { + getRepositoryParent(headRepository) { return __awaiter(this, void 0, void 0, function* () { const { data: headRepo } = yield this.octokit.repos.get(Object.assign({}, this.parseRepository(headRepository))); - if (inputs.requestToParent && !headRepo.parent) { - throw new Error(`The checked out repository is not a fork. Input 'request-to-parent' should be set to 'false'.`); + if (!headRepo.parent) { + throw new Error(`Repository '${headRepository}' is not a fork. Unable to continue.`); } - const baseRepository = inputs.requestToParent - ? headRepo.parent.full_name - : headRepository; - const headBranch = `${headRepo.owner.login}:${inputs.branch}`; + return headRepo.parent.full_name; + }); + } + createOrUpdatePullRequest(inputs, baseRepository, headRepository) { + return __awaiter(this, void 0, void 0, function* () { + const [headOwner] = headRepository.split('/'); + const headBranch = `${headOwner}:${inputs.branch}`; // Create or update the pull request const pullNumber = yield this.createOrUpdate(inputs, baseRepository, headBranch); // Set outputs @@ -10581,14 +10591,30 @@ function createPullRequest(inputs) { inputs.title = inputs.title ? inputs.title : DEFAULT_TITLE; inputs.body = inputs.body ? inputs.body : DEFAULT_BODY; inputs.branch = inputs.branch ? inputs.branch : DEFAULT_BRANCH; - // Determine the GitHub repository from git config - // This will be the target repository for the pull request branch - core.startGroup('Determining the checked out repository'); + // Init the GitHub client + const githubHelper = new github_helper_1.GitHubHelper(inputs.token); + core.startGroup('Determining the base and head repositories'); + // Determine the base repository from git config const remoteUrl = yield git.tryGetRemoteUrl(); - const remote = utils.getRemoteDetail(remoteUrl); + 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 = yield 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); + yield git.exec(['remote', 'add', 'fork', remoteUrl]); + } core.endGroup(); - core.info(`Pull request branch target repository set to ${remote.repository}`); - if (remote.protocol == 'HTTPS') { + core.info(`Pull request branch target repository set to ${branchRepository}`); + if (baseRemote.protocol == 'HTTPS') { core.startGroup('Configuring credential for HTTPS authentication'); yield gitAuthHelper.configureToken(inputs.token); core.endGroup(); @@ -10655,14 +10681,14 @@ function createPullRequest(inputs) { core.endGroup(); // Create or update the pull request branch core.startGroup('Create or update the pull request branch'); - const result = yield create_or_update_branch_1.createOrUpdateBranch(git, inputs.commitMessage, inputs.base, inputs.branch); + const result = yield create_or_update_branch_1.createOrUpdateBranch(git, inputs.commitMessage, inputs.base, inputs.branch, branchRemoteName); core.endGroup(); if (['created', 'updated'].includes(result.action)) { // The branch was created or updated - core.startGroup(`Pushing pull request branch to 'origin/${inputs.branch}'`); + core.startGroup(`Pushing pull request branch to '${branchRemoteName}/${inputs.branch}'`); yield git.push([ '--force-with-lease', - 'origin', + branchRemoteName, `HEAD:refs/heads/${inputs.branch}` ]); core.endGroup(); @@ -10670,8 +10696,7 @@ function createPullRequest(inputs) { inputs.base = result.base; if (result.hasDiffWithBase) { // Create or update the pull request - const githubHelper = new github_helper_1.GitHubHelper(inputs.token); - yield githubHelper.createOrUpdatePullRequest(inputs, remote.repository); + yield githubHelper.createOrUpdatePullRequest(inputs, baseRemote.repository, branchRepository); } else { // If there is no longer a diff with the base delete the branch @@ -10680,7 +10705,7 @@ function createPullRequest(inputs) { yield git.push([ '--delete', '--force', - 'origin', + branchRemoteName, `refs/heads/${inputs.branch}` ]); } diff --git a/src/create-or-update-branch.ts b/src/create-or-update-branch.ts index 03fb852..9803e69 100644 --- a/src/create-or-update-branch.ts +++ b/src/create-or-update-branch.ts @@ -7,10 +7,11 @@ const CHERRYPICK_EMPTY = export async function tryFetch( git: GitCommandManager, + remote: string, branch: string ): Promise { try { - await git.fetch([`${branch}:refs/remotes/origin/${branch}`]) + await git.fetch([`${branch}:refs/remotes/${remote}/${branch}`], remote) return true } catch { return false @@ -74,13 +75,15 @@ function splitLines(multilineString: string): string[] { export async function createOrUpdateBranch( git: GitCommandManager, commitMessage: string, - baseInput: string, - branch: string + base: string, + branch: string, + branchRemoteName: string ): Promise { // Get the working base. This may or may not be the actual base. const workingBase = await git.symbolicRef('HEAD', ['--short']) // If the base is not specified it is assumed to be the working base. - const base = baseInput ? baseInput : workingBase + base = base ? base : workingBase + const baseRemote = 'origin' // Set the default return values const result: CreateOrUpdateBranchResult = { @@ -101,7 +104,7 @@ export async function createOrUpdateBranch( // Perform fetch and reset the working base // Commits made during the workflow will be removed - await git.fetch([`${workingBase}:${workingBase}`], 'origin', ['--force']) + await git.fetch([`${workingBase}:${workingBase}`], baseRemote, ['--force']) // If the working base is not the base, rebase the temp branch commits if (workingBase != base) { @@ -109,7 +112,7 @@ export async function createOrUpdateBranch( `Rebasing commits made to branch '${workingBase}' on to base branch '${base}'` ) // Checkout the actual base - await git.fetch([`${base}:${base}`], 'origin', ['--force']) + await git.fetch([`${base}:${base}`], baseRemote, ['--force']) await git.checkout(base) // Cherrypick commits from the temporary branch starting from the working base const commits = await git.revList( @@ -128,11 +131,11 @@ export async function createOrUpdateBranch( // Reset the temp branch to the working index await git.checkout(tempBranch, 'HEAD') // Reset the base - await git.fetch([`${base}:${base}`], 'origin', ['--force']) + await git.fetch([`${base}:${base}`], baseRemote, ['--force']) } // Try to fetch the pull request branch - if (!(await tryFetch(git, branch))) { + if (!(await tryFetch(git, branchRemoteName, branch))) { // The pull request branch does not exist core.info(`Pull request branch '${branch}' does not exist yet.`) // Create the pull request branch @@ -150,7 +153,7 @@ export async function createOrUpdateBranch( } else { // The pull request branch exists core.info( - `Pull request branch '${branch}' already exists as remote branch 'origin/${branch}'` + `Pull request branch '${branch}' already exists as remote branch '${branchRemoteName}/${branch}'` ) // Checkout the pull request branch await git.checkout(branch) @@ -166,7 +169,7 @@ export async function createOrUpdateBranch( // Check if the pull request branch has been updated // If the branch was reset or updated it will be ahead // It may be behind if a reset now results in no diff with the base - if (!(await isEven(git, `origin/${branch}`, branch))) { + if (!(await isEven(git, `${branchRemoteName}/${branch}`, branch))) { result.action = 'updated' core.info(`Updated branch '${branch}'`) } else { diff --git a/src/create-pull-request.ts b/src/create-pull-request.ts index 588c4d1..fdf74b6 100644 --- a/src/create-pull-request.ts +++ b/src/create-pull-request.ts @@ -27,7 +27,7 @@ export interface Inputs { milestone: number draft: boolean branch: string - requestToParent: boolean + pushToFork: string base: string branchSuffix: string } @@ -54,17 +54,42 @@ export async function createPullRequest(inputs: Inputs): Promise { inputs.body = inputs.body ? inputs.body : DEFAULT_BODY inputs.branch = inputs.branch ? inputs.branch : DEFAULT_BRANCH - // Determine the GitHub repository from git config - // This will be the target repository for the pull request branch - core.startGroup('Determining the checked out repository') + // 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 const remoteUrl = await git.tryGetRemoteUrl() - const remote = utils.getRemoteDetail(remoteUrl) + 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]) + } core.endGroup() core.info( - `Pull request branch target repository set to ${remote.repository}` + `Pull request branch target repository set to ${branchRepository}` ) - if (remote.protocol == 'HTTPS') { + // Configure auth + if (baseRemote.protocol == 'HTTPS') { core.startGroup('Configuring credential for HTTPS authentication') await gitAuthHelper.configureToken(inputs.token) core.endGroup() @@ -158,18 +183,19 @@ export async function createPullRequest(inputs: Inputs): Promise { git, inputs.commitMessage, inputs.base, - inputs.branch + inputs.branch, + branchRemoteName ) core.endGroup() if (['created', 'updated'].includes(result.action)) { // The branch was created or updated core.startGroup( - `Pushing pull request branch to 'origin/${inputs.branch}'` + `Pushing pull request branch to '${branchRemoteName}/${inputs.branch}'` ) await git.push([ '--force-with-lease', - 'origin', + branchRemoteName, `HEAD:refs/heads/${inputs.branch}` ]) core.endGroup() @@ -179,8 +205,11 @@ export async function createPullRequest(inputs: Inputs): Promise { if (result.hasDiffWithBase) { // Create or update the pull request - const githubHelper = new GitHubHelper(inputs.token) - await githubHelper.createOrUpdatePullRequest(inputs, remote.repository) + await githubHelper.createOrUpdatePullRequest( + inputs, + baseRemote.repository, + branchRepository + ) } else { // If there is no longer a diff with the base delete the branch core.info( @@ -190,7 +219,7 @@ export async function createPullRequest(inputs: Inputs): Promise { await git.push([ '--delete', '--force', - 'origin', + branchRemoteName, `refs/heads/${inputs.branch}` ]) } diff --git a/src/github-helper.ts b/src/github-helper.ts index 3476022..de80b4f 100644 --- a/src/github-helper.ts +++ b/src/github-helper.ts @@ -77,24 +77,25 @@ export class GitHubHelper { return pull.number } - async createOrUpdatePullRequest( - inputs: Inputs, - headRepository: string - ): Promise { + async getRepositoryParent(headRepository: string): Promise { const {data: headRepo} = await this.octokit.repos.get({ ...this.parseRepository(headRepository) }) - - if (inputs.requestToParent && !headRepo.parent) { + if (!headRepo.parent) { throw new Error( - `The checked out repository is not a fork. Input 'request-to-parent' should be set to 'false'.` + `Repository '${headRepository}' is not a fork. Unable to continue.` ) } - const baseRepository = inputs.requestToParent - ? headRepo.parent.full_name - : headRepository + return headRepo.parent.full_name + } - const headBranch = `${headRepo.owner.login}:${inputs.branch}` + async createOrUpdatePullRequest( + inputs: Inputs, + baseRepository: string, + headRepository: string + ): Promise { + const [headOwner] = headRepository.split('/') + const headBranch = `${headOwner}:${inputs.branch}` // Create or update the pull request const pullNumber = await this.createOrUpdate( diff --git a/src/main.ts b/src/main.ts index 0039668..3f89ae6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,7 +20,7 @@ async function run(): Promise { milestone: Number(core.getInput('milestone')), draft: core.getInput('draft') === 'true', branch: core.getInput('branch'), - requestToParent: core.getInput('request-to-parent') === 'true', + pushToFork: core.getInput('push-to-fork'), base: core.getInput('base'), branchSuffix: core.getInput('branch-suffix') } diff --git a/src/utils.ts b/src/utils.ts index a3b304c..b8f5f32 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -62,6 +62,12 @@ export function getRemoteDetail(remoteUrl: string): RemoteDetail { ) } +export function getRemoteUrl(protocol: string, repository: string): string { + return protocol == 'HTTPS' + ? `https://github.com/${repository}` + : `git@github.com:${repository}.git` +} + export function secondsSinceEpoch(): number { const now = new Date() return Math.round(now.getTime() / 1000)