diff --git a/__tests__/git.test.ts b/__tests__/git.test.ts index cc10b236..06e2e823 100644 --- a/__tests__/git.test.ts +++ b/__tests__/git.test.ts @@ -424,5 +424,30 @@ describe('git', () => { ) } }) + + it('should execute commands if force is false and retry until limit is exceeded', async () => { + Object.assign(action, { + hostname: 'github.com', + silent: false, + folder: 'assets', + branch: 'branch', + force: false, + token: '123', + repositoryName: 'JamesIves/montezuma', + pusher: { + name: 'asd', + email: 'as@cat' + }, + isTest: TestFlag.HAS_CHANGED_FILES + }) + + try { + await deploy(action) + } catch (error) { + expect(error instanceof Error && error.message).toBe( + 'The deploy step encountered an error: Attempt limit exceeded ❌' + ) + } + }) }) }) diff --git a/src/constants.ts b/src/constants.ts index 55905164..6505b778 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -10,7 +10,8 @@ export enum TestFlag { HAS_CHANGED_FILES = 1 << 1, // Assume changes to commit. HAS_REMOTE_BRANCH = 1 << 2, // Assume remote repository has existing commits. UNABLE_TO_REMOVE_ORIGIN = 1 << 3, // Assume we can't remove origin. - UNABLE_TO_UNSET_GIT_CONFIG = 1 << 4 // Assume we can't remove previously set git configs. + UNABLE_TO_UNSET_GIT_CONFIG = 1 << 4, // Assume we can't remove previously set git configs. + HAS_REJECTED_COMMIT = 1 << 5 // Assume commit rejection. } /* For more information please refer to the README: https://github.com/JamesIves/github-pages-deploy-action */ diff --git a/src/git.ts b/src/git.ts index 33565fb7..02207af8 100644 --- a/src/git.ts +++ b/src/git.ts @@ -240,27 +240,30 @@ export async function deploy(action: ActionInterface): Promise { action.silent ) } else { + const ATTEMPT_LIMIT = 3 // Attempt to push our changes, but fetch + rebase if there were // other changes added in the meantime - const ATTEMPT_LIMIT = 3 let attempt = 0 + // Keep track of whether the most recent attempt was rejected let rejected = false + do { attempt++ + if (attempt > ATTEMPT_LIMIT) throw new Error(`Attempt limit exceeded`) // Handle rejection for the previous attempt first such that, on // the final attempt, time is not wasted rebasing it when it will // not be pushed if (rejected) { - info(`Fetching upstream ${action.branch}...`) + info(`Fetching upstream ${action.branch}…`) await execute( `git fetch ${action.repositoryPath} ${action.branch}:${action.branch}`, `${action.workspace}/${temporaryDeploymentDirectory}`, action.silent ) - info(`Rebasing this deployment onto ${action.branch}...`) + info(`Rebasing this deployment onto ${action.branch}…`) await execute( `git rebase ${action.branch} ${temporaryDeploymentBranch}`, `${action.workspace}/${temporaryDeploymentDirectory}`, @@ -268,7 +271,8 @@ export async function deploy(action: ActionInterface): Promise { ) } - info(`Pushing changes... (attempt ${attempt} of ${ATTEMPT_LIMIT})`) + info(`Pushing changes… (attempt ${attempt} of ${ATTEMPT_LIMIT})`) + const pushResult = await execute( `git push --porcelain ${action.repositoryPath} ${temporaryDeploymentBranch}:${action.branch}`, `${action.workspace}/${temporaryDeploymentDirectory}`, @@ -277,8 +281,10 @@ export async function deploy(action: ActionInterface): Promise { ) rejected = + Boolean(action.isTest) || pushResult.stdout.includes(`[rejected]`) || pushResult.stdout.includes(`[remote rejected]`) + if (rejected) info('Updates were rejected') // If the push failed for any reason other than being rejected,