diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1ef3c313e..36be628a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -154,6 +154,17 @@ jobs: submodules: true - name: Verify submodules true run: __test__/verify-submodules-true.sh + + # Submodules limited + - name: Checkout submodules limited + uses: ./ + with: + ref: test-data/v2/submodule-ssh-url + path: submodules-true + submodules: true + submodule-directories: submodule-level-1 + - name: Verify submodules true + run: __test__/verify-submodules-true.sh # Submodules recursive - name: Checkout submodules recursive diff --git a/README.md b/README.md index 9b6176d94..75200a32e 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,10 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ # Default: false submodules: '' + # A list of submodules to checkout. + # Default: null + submodule-directories: '' + # Add repository path as safe.directory for Git global config by running `git # config --global --add safe.directory ` # Default: true diff --git a/__test__/git-auth-helper.test.ts b/__test__/git-auth-helper.test.ts index 7633704cc..6666dca85 100644 --- a/__test__/git-auth-helper.test.ts +++ b/__test__/git-auth-helper.test.ts @@ -813,6 +813,7 @@ async function setup(testName: string): Promise { lfs: false, submodules: false, nestedSubmodules: false, + submoduleDirectories: [], persistCredentials: true, ref: 'refs/heads/main', repositoryName: 'my-repo', diff --git a/__test__/input-helper.test.ts b/__test__/input-helper.test.ts index 9514cb42d..75eb546a6 100644 --- a/__test__/input-helper.test.ts +++ b/__test__/input-helper.test.ts @@ -21,6 +21,13 @@ describe('input-helper tests', () => { jest.spyOn(core, 'getInput').mockImplementation((name: string) => { return inputs[name] }) + // Mock getMultilineInput + jest.spyOn(core, 'getMultilineInput').mockImplementation((name: string) => { + const input: string[] = (inputs[name] || '') + .split('\n') + .filter(x => x !== '') + return input.map(inp => inp.trim()) + }) // Mock error/warning/info/debug jest.spyOn(core, 'error').mockImplementation(jest.fn()) @@ -87,6 +94,7 @@ describe('input-helper tests', () => { expect(settings.showProgress).toBe(true) expect(settings.lfs).toBe(false) expect(settings.ref).toBe('refs/heads/some-ref') + expect(settings.submoduleDirectories).toStrictEqual([]) expect(settings.repositoryName).toBe('some-repo') expect(settings.repositoryOwner).toBe('some-owner') expect(settings.repositoryPath).toBe(gitHubWorkspace) @@ -144,4 +152,13 @@ describe('input-helper tests', () => { const settings: IGitSourceSettings = await inputHelper.getInputs() expect(settings.workflowOrganizationId).toBe(123456) }) + it('sets submoduleDirectories', async () => { + inputs['submodule-directories'] = 'submodule1\nsubmodule2' + const settings: IGitSourceSettings = await inputHelper.getInputs() + expect(settings.submoduleDirectories).toStrictEqual([ + 'submodule1', + 'submodule2' + ]) + expect(settings.submodules).toBe(true) + }) }) diff --git a/action.yml b/action.yml index 75d5ae2d8..e9304629f 100644 --- a/action.yml +++ b/action.yml @@ -92,6 +92,10 @@ inputs: When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are converted to HTTPS. default: false + submodule-directories: + description: > + A list of submodules to checkout. + default: null set-safe-directory: description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory ` default: true diff --git a/dist/index.js b/dist/index.js index 9d959a9ee..abd0a7554 100644 --- a/dist/index.js +++ b/dist/index.js @@ -795,10 +795,10 @@ class GitCommandManager { yield this.execGit(args); }); } - submoduleUpdate(fetchDepth, recursive) { + submoduleUpdate(fetchDepth, recursive, submoduleDirectories) { return __awaiter(this, void 0, void 0, function* () { const args = ['-c', 'protocol.version=2']; - args.push('submodule', 'update', '--init', '--force'); + args.push('submodule', 'update', '--init', '--force', ...submoduleDirectories); if (fetchDepth > 0) { args.push(`--depth=${fetchDepth}`); } @@ -1342,7 +1342,7 @@ function getSource(settings) { // Checkout submodules core.startGroup('Fetching submodules'); yield git.submoduleSync(settings.nestedSubmodules); - yield git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules); + yield git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules, settings.submoduleDirectories); yield git.submoduleForeach('git config --local gc.auto 0', settings.nestedSubmodules); core.endGroup(); // Persist credentials @@ -1805,6 +1805,7 @@ function getInputs() { // Submodules result.submodules = false; result.nestedSubmodules = false; + result.submoduleDirectories = []; const submodulesString = (core.getInput('submodules') || '').toUpperCase(); if (submodulesString == 'RECURSIVE') { result.submodules = true; @@ -1813,8 +1814,15 @@ function getInputs() { else if (submodulesString == 'TRUE') { result.submodules = true; } + const submoduleDirectories = core.getMultilineInput('submodule-directories'); + if (submoduleDirectories.length > 0) { + result.submoduleDirectories = submoduleDirectories; + if (!result.submodules) + result.submodules = true; + } core.debug(`submodules = ${result.submodules}`); core.debug(`recursive submodules = ${result.nestedSubmodules}`); + core.debug(`submodule directories = ${result.submoduleDirectories}`); // Auth token result.authToken = core.getInput('token', { required: true }); // SSH diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts index 8e42a387f..bc73118a1 100644 --- a/src/git-command-manager.ts +++ b/src/git-command-manager.ts @@ -54,7 +54,11 @@ export interface IGitCommandManager { shaExists(sha: string): Promise submoduleForeach(command: string, recursive: boolean): Promise submoduleSync(recursive: boolean): Promise - submoduleUpdate(fetchDepth: number, recursive: boolean): Promise + submoduleUpdate( + fetchDepth: number, + recursive: boolean, + submoduleDirectories: string[] + ): Promise submoduleStatus(): Promise tagExists(pattern: string): Promise tryClean(): Promise @@ -409,9 +413,19 @@ class GitCommandManager { await this.execGit(args) } - async submoduleUpdate(fetchDepth: number, recursive: boolean): Promise { + async submoduleUpdate( + fetchDepth: number, + recursive: boolean, + submoduleDirectories: string[] + ): Promise { const args = ['-c', 'protocol.version=2'] - args.push('submodule', 'update', '--init', '--force') + args.push( + 'submodule', + 'update', + '--init', + '--force', + ...submoduleDirectories + ) if (fetchDepth > 0) { args.push(`--depth=${fetchDepth}`) } diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts index f723d94f4..122e1316f 100644 --- a/src/git-source-provider.ts +++ b/src/git-source-provider.ts @@ -242,7 +242,11 @@ export async function getSource(settings: IGitSourceSettings): Promise { // Checkout submodules core.startGroup('Fetching submodules') await git.submoduleSync(settings.nestedSubmodules) - await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules) + await git.submoduleUpdate( + settings.fetchDepth, + settings.nestedSubmodules, + settings.submoduleDirectories + ) await git.submoduleForeach( 'git config --local gc.auto 0', settings.nestedSubmodules diff --git a/src/git-source-settings.ts b/src/git-source-settings.ts index 4e41ac302..518e0bfc3 100644 --- a/src/git-source-settings.ts +++ b/src/git-source-settings.ts @@ -74,6 +74,11 @@ export interface IGitSourceSettings { */ nestedSubmodules: boolean + /** + * Indicates which submodule paths to checkout + */ + submoduleDirectories: string[] + /** * The auth token to use when fetching the repository */ diff --git a/src/input-helper.ts b/src/input-helper.ts index 059232f5c..5b2ff6c92 100644 --- a/src/input-helper.ts +++ b/src/input-helper.ts @@ -125,6 +125,7 @@ export async function getInputs(): Promise { // Submodules result.submodules = false result.nestedSubmodules = false + result.submoduleDirectories = [] const submodulesString = (core.getInput('submodules') || '').toUpperCase() if (submodulesString == 'RECURSIVE') { result.submodules = true @@ -132,9 +133,16 @@ export async function getInputs(): Promise { } else if (submodulesString == 'TRUE') { result.submodules = true } + + const submoduleDirectories = core.getMultilineInput('submodule-directories') + if (submoduleDirectories.length > 0) { + result.submoduleDirectories = submoduleDirectories + if (!result.submodules) result.submodules = true + } + core.debug(`submodules = ${result.submodules}`) core.debug(`recursive submodules = ${result.nestedSubmodules}`) - + core.debug(`submodule directories = ${result.submoduleDirectories}`) // Auth token result.authToken = core.getInput('token', {required: true})