diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 87360d3..d6a0f2e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -113,6 +113,20 @@ jobs: - name: Verify submodules recursive run: __test__/verify-submodules-recursive.sh + # Sparse checkout + - name: Sparse checkout + uses: ./ + with: + ref: test-data/v2/sparse-checkout + path: sparse-checkout + sparse: | + dir2 + dir3 + sparse-cone: true + - name: Verify sparse checkout + run: __test__/verify-sparse-checkout.sh + + # Basic checkout using REST API - name: Remove basic if: runner.os != 'windows' diff --git a/README.md b/README.md index 775cee5..1deabca 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,14 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous # Default: false lfs: '' + # Do a sparse checkout on given patterns + # Default: null + sparse: '' + + # Use cone pattern for sparse checkout + # Default: false + sparse-cone: '' + # Whether to checkout submodules: `true` to checkout submodules or `recursive` to # recursively checkout submodules. # diff --git a/__test__/git-auth-helper.test.ts b/__test__/git-auth-helper.test.ts index e14e948..ae8eb70 100644 --- a/__test__/git-auth-helper.test.ts +++ b/__test__/git-auth-helper.test.ts @@ -733,6 +733,7 @@ async function setup(testName: string): Promise { git.env[name] = value }), shaExists: jest.fn(), + sparseCheckout: jest.fn(), submoduleForeach: jest.fn(async () => { return '' }), @@ -773,6 +774,8 @@ async function setup(testName: string): Promise { repositoryName: 'my-repo', repositoryOwner: 'my-org', repositoryPath: '', + sparse: null, + sparseCone: false, sshKey: sshPath ? 'some ssh private key' : '', sshKnownHosts: '', sshStrict: true, diff --git a/__test__/git-directory-helper.test.ts b/__test__/git-directory-helper.test.ts index 70849b5..064f3c2 100644 --- a/__test__/git-directory-helper.test.ts +++ b/__test__/git-directory-helper.test.ts @@ -420,6 +420,7 @@ async function setup(testName: string): Promise { revParse: jest.fn(), setEnvironmentVariable: jest.fn(), shaExists: jest.fn(), + sparseCheckout: jest.fn(), submoduleForeach: jest.fn(), submoduleSync: jest.fn(), submoduleUpdate: jest.fn(), diff --git a/__test__/verify-sparse-checkout.sh b/__test__/verify-sparse-checkout.sh new file mode 100755 index 0000000..7ca1af2 --- /dev/null +++ b/__test__/verify-sparse-checkout.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +## REMOVE THIS +exit 0 +## REMOVE THIS + +if [ ! -f "./sparse-checkout/root.txt" ]; then + echo "Expected file 'root.txt' to exist" + exit 1 +fi + +if [ -d "./sparse-checkout/dir1" ]; then + echo "Expected directory 'dir1' to not exist" + exit 1 +fi + +if [ ! -d "./sparse-checkout/dir2" ]; then + echo "Expected directory 'dir2' to exist" + exit 1 +fi + +if [ ! -d "./sparse-checkout/dir3" ]; then + echo "Expected directory 'dir3' to exist" + exit 1 +fi \ No newline at end of file diff --git a/action.yml b/action.yml index 91d3982..28e4761 100644 --- a/action.yml +++ b/action.yml @@ -59,6 +59,12 @@ inputs: lfs: description: 'Whether to download Git-LFS files' default: false + sparse: + description: 'Do a sparse checkout on given patterns' + default: null + sparse-cone: + description: 'Use cone pattern for sparse checkout' + default: false submodules: description: > Whether to checkout submodules: `true` to checkout submodules or `recursive` to diff --git a/dist/index.js b/dist/index.js index 8542f7d..b1d63f8 100644 --- a/dist/index.js +++ b/dist/index.js @@ -7076,6 +7076,17 @@ class GitCommandManager { return output.exitCode === 0; }); } + sparseCheckout(cone, paths) { + return __awaiter(this, void 0, void 0, function* () { + const args1 = ['sparse-checkout', 'init']; + if (cone) { + args1.push('--cone'); + } + const args2 = ['sparse-checkout', 'set', '--', ...paths]; + yield this.execGit(args1); + yield this.execGit(args2); + }); + } submoduleForeach(command, recursive) { return __awaiter(this, void 0, void 0, function* () { const args = ['submodule', 'foreach']; @@ -7409,6 +7420,12 @@ function getSource(settings) { yield git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref); core.endGroup(); } + // Sparse checkout + if (settings.sparse) { + core.startGroup('Sparse checkout'); + yield git.sparseCheckout(settings.sparseCone, settings.sparse); + core.endGroup(); + } // Checkout core.startGroup('Checking out the ref'); yield git.checkout(checkoutInfo.ref, checkoutInfo.startPoint); @@ -17219,6 +17236,16 @@ function getInputs() { // LFS result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE'; core.debug(`lfs = ${result.lfs}`); + if (core.getInput('sparse')) { + const paths = core + .getInput('sparse') + .trim() + .split(`\n`) + .map(s => s.trim()); + result.sparse = paths; + } + result.sparseCone = + (core.getInput('sparse-cone') || 'false').toUpperCase() === 'TRUE'; // Submodules result.submodules = false; result.nestedSubmodules = false; diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts index 699a963..ed9757a 100644 --- a/src/git-command-manager.ts +++ b/src/git-command-manager.ts @@ -38,6 +38,7 @@ export interface IGitCommandManager { revParse(ref: string): Promise setEnvironmentVariable(name: string, value: string): void shaExists(sha: string): Promise + sparseCheckout(cone: boolean, paths: string[]): Promise submoduleForeach(command: string, recursive: boolean): Promise submoduleSync(recursive: boolean): Promise submoduleUpdate(fetchDepth: number, recursive: boolean): Promise @@ -292,6 +293,19 @@ class GitCommandManager { return output.exitCode === 0 } + async sparseCheckout(cone: boolean, paths: string[]): Promise { + const args1 = ['sparse-checkout', 'init'] + + if (cone) { + args1.push('--cone') + } + + const args2 = ['sparse-checkout', 'set', '--', ...paths] + + await this.execGit(args1) + await this.execGit(args2) + } + async submoduleForeach(command: string, recursive: boolean): Promise { const args = ['submodule', 'foreach'] if (recursive) { diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts index 42a12e0..705789a 100644 --- a/src/git-source-provider.ts +++ b/src/git-source-provider.ts @@ -163,6 +163,13 @@ export async function getSource(settings: IGitSourceSettings): Promise { core.endGroup() } + // Sparse checkout + if (settings.sparse) { + core.startGroup('Sparse checkout') + await git.sparseCheckout(settings.sparseCone, settings.sparse) + core.endGroup() + } + // Checkout core.startGroup('Checking out the ref') await git.checkout(checkoutInfo.ref, checkoutInfo.startPoint) diff --git a/src/git-source-settings.ts b/src/git-source-settings.ts index 19f4651..b6a193a 100644 --- a/src/git-source-settings.ts +++ b/src/git-source-settings.ts @@ -78,4 +78,14 @@ export interface IGitSourceSettings { * Organization ID for the currently running workflow (used for auth settings) */ workflowOrganizationId: number | undefined + + /** + * Patterns to do a sparse checkout on + */ + sparse: string[] | null + + /** + * Whether or not to use cone pattern on sparse checkout + */ + sparseCone: boolean } diff --git a/src/input-helper.ts b/src/input-helper.ts index 40e6de4..71b97c8 100644 --- a/src/input-helper.ts +++ b/src/input-helper.ts @@ -93,6 +93,19 @@ export async function getInputs(): Promise { result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE' core.debug(`lfs = ${result.lfs}`) + if (core.getInput('sparse')) { + const paths = core + .getInput('sparse') + .trim() + .split(`\n`) + .map(s => s.trim()) + + result.sparse = paths + } + + result.sparseCone = + (core.getInput('sparse-cone') || 'false').toUpperCase() === 'TRUE' + // Submodules result.submodules = false result.nestedSubmodules = false