diff --git a/README.md b/README.md index bfecf464d..a47dab9f8 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,11 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ # Otherwise, uses the default branch. ref: '' + # Whether to checkout the default repository branch if specified ref does not + # exist. If this is set to true, then fetch-depth should be 0 + # Default: true + default-ref-on-error: '' + # Personal access token (PAT) used to fetch the repository. The PAT is configured # with the local git config, which enables your scripts to run authenticated git # commands. The post-job step removes the PAT. diff --git a/__test__/git-auth-helper.test.ts b/__test__/git-auth-helper.test.ts index 411faed05..e8ea8865c 100644 --- a/__test__/git-auth-helper.test.ts +++ b/__test__/git-auth-helper.test.ts @@ -813,6 +813,8 @@ async function setup(testName: string): Promise { nestedSubmodules: false, persistCredentials: true, ref: 'refs/heads/main', + defaultRefOnError: true, + defaultBranch: 'main', repositoryName: 'my-repo', repositoryOwner: 'my-org', repositoryPath: '', diff --git a/action.yml b/action.yml index 5aa90a738..ad48198f2 100644 --- a/action.yml +++ b/action.yml @@ -9,6 +9,11 @@ inputs: The branch, tag or SHA to checkout. When checking out the repository that triggered a workflow, this defaults to the reference or SHA for that event. Otherwise, uses the default branch. + default-ref-on-error: + description: > + Whether to checkout the default repository branch if specified ref does not exist. + If this is set to true, then fetch-depth should be 0 + default: true token: description: > Personal access token (PAT) used to fetch the repository. The PAT is configured diff --git a/dist/index.js b/dist/index.js index ddf2b3d89..5b474f562 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1226,6 +1226,17 @@ function getSource(settings) { core.startGroup('Setting up auth'); yield authHelper.configureAuth(); core.endGroup(); + if (settings.defaultRefOnError && settings.defaultRefOnError === true) { + // Configure default branch + core.startGroup('Setting up default branch'); + if (settings.sshKey) { + settings.defaultBranch = yield git.getDefaultBranch(repositoryUrl); + } + else { + settings.defaultBranch = yield githubApiHelper.getDefaultBranch(settings.authToken, settings.repositoryOwner, settings.repositoryName); + } + core.endGroup(); + } // Determine the default branch if (!settings.ref && !settings.commit) { core.startGroup('Determining the default branch'); @@ -1250,7 +1261,8 @@ function getSource(settings) { else if (settings.sparseCheckout) { fetchOptions.filter = 'blob:none'; } - if (settings.fetchDepth <= 0) { + if (settings.fetchDepth <= 0 || + (settings.defaultRefOnError && settings.defaultRefOnError === true)) { // Fetch all branches and tags let refSpec = refHelper.getRefSpecForAllHistory(settings.ref, settings.commit); yield git.fetch(refSpec, fetchOptions); @@ -1270,7 +1282,19 @@ function getSource(settings) { core.endGroup(); // Checkout info core.startGroup('Determining the checkout info'); - const checkoutInfo = yield refHelper.getCheckoutInfo(git, settings.ref, settings.commit); + let checkoutInfo; + if (settings.defaultRefOnError && settings.defaultRefOnError === true) { + try { + checkoutInfo = yield refHelper.getCheckoutInfo(git, settings.ref, settings.commit); + } + catch (error) { + core.info('Could not determine the checkout info. Trying the default repo branch'); + checkoutInfo = yield refHelper.getCheckoutInfo(git, settings.defaultBranch, settings.commit); + } + } + else { + checkoutInfo = yield refHelper.getCheckoutInfo(git, settings.ref, settings.commit); + } core.endGroup(); // LFS fetch // Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time). @@ -1724,6 +1748,10 @@ function getInputs() { } core.debug(`ref = '${result.ref}'`); core.debug(`commit = '${result.commit}'`); + // Default ref on error + result.defaultRefOnError = + (core.getInput('default-ref-on-error') || 'true').toUpperCase() === 'TRUE'; + core.debug(`default-ref-on-error = '${result.defaultRefOnError}'`); // Clean result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE'; core.debug(`clean = ${result.clean}`); diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts index 5c98e9f73..a17480245 100644 --- a/src/git-source-provider.ts +++ b/src/git-source-provider.ts @@ -130,6 +130,21 @@ export async function getSource(settings: IGitSourceSettings): Promise { await authHelper.configureAuth() core.endGroup() + if (settings.defaultRefOnError && settings.defaultRefOnError === true) { + // Configure default branch + core.startGroup('Setting up default branch') + if (settings.sshKey) { + settings.defaultBranch = await git.getDefaultBranch(repositoryUrl) + } else { + settings.defaultBranch = await githubApiHelper.getDefaultBranch( + settings.authToken, + settings.repositoryOwner, + settings.repositoryName + ) + } + core.endGroup() + } + // Determine the default branch if (!settings.ref && !settings.commit) { core.startGroup('Determining the default branch') @@ -166,7 +181,10 @@ export async function getSource(settings: IGitSourceSettings): Promise { fetchOptions.filter = 'blob:none' } - if (settings.fetchDepth <= 0) { + if ( + settings.fetchDepth <= 0 || + (settings.defaultRefOnError && settings.defaultRefOnError === true) + ) { // Fetch all branches and tags let refSpec = refHelper.getRefSpecForAllHistory( settings.ref, @@ -190,11 +208,31 @@ export async function getSource(settings: IGitSourceSettings): Promise { // Checkout info core.startGroup('Determining the checkout info') - const checkoutInfo = await refHelper.getCheckoutInfo( - git, - settings.ref, - settings.commit - ) + let checkoutInfo: refHelper.ICheckoutInfo + if (settings.defaultRefOnError && settings.defaultRefOnError === true) { + try { + checkoutInfo = await refHelper.getCheckoutInfo( + git, + settings.ref, + settings.commit + ) + } catch (error) { + core.info( + 'Could not determine the checkout info. Trying the default repo branch' + ) + checkoutInfo = await refHelper.getCheckoutInfo( + git, + settings.defaultBranch, + settings.commit + ) + } + } else { + checkoutInfo = await refHelper.getCheckoutInfo( + git, + settings.ref, + settings.commit + ) + } core.endGroup() // LFS fetch diff --git a/src/git-source-settings.ts b/src/git-source-settings.ts index 629350b2c..f94209b78 100644 --- a/src/git-source-settings.ts +++ b/src/git-source-settings.ts @@ -19,6 +19,16 @@ export interface IGitSourceSettings { */ ref: string + /** + * Whether to checkout the default repository branch if specified ref does not exist. + */ + defaultRefOnError: boolean + + /** + * The target ref to fetch if it exists + */ + defaultBranch: string + /** * The commit to checkout */ diff --git a/src/input-helper.ts b/src/input-helper.ts index e546c196d..cc854b077 100644 --- a/src/input-helper.ts +++ b/src/input-helper.ts @@ -78,6 +78,11 @@ export async function getInputs(): Promise { core.debug(`ref = '${result.ref}'`) core.debug(`commit = '${result.commit}'`) + // Default ref on error + result.defaultRefOnError = + (core.getInput('default-ref-on-error') || 'true').toUpperCase() === 'TRUE' + core.debug(`default-ref-on-error = '${result.defaultRefOnError}'`) + // Clean result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE' core.debug(`clean = ${result.clean}`)