diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1ef3c31..36be628 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 9b6176d..d376c49 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 use when `submodules` is `true`. + # 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 7633704..ff60bae 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: null, persistCredentials: true, ref: 'refs/heads/main', repositoryName: 'my-repo', diff --git a/__test__/input-helper.test.ts b/__test__/input-helper.test.ts index 9514cb4..35c1848 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).toBe(null) 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 75d5ae2..d18a526 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 use when `submodules` is `true`. + 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 9d959a9..aba7b84 100644 --- a/dist/index.js +++ b/dist/index.js @@ -795,17 +795,32 @@ 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'); - if (fetchDepth > 0) { - args.push(`--depth=${fetchDepth}`); + if (submoduleDirectories) { + for (const submodule of submoduleDirectories) { + const args = ['-c', 'protocol.version=2']; + args.push('submodule', 'update', '--init', '--force', submodule); + if (fetchDepth > 0) { + args.push(`--depth=${fetchDepth}`); + } + if (recursive) { + args.push('--recursive'); + } + yield this.execGit(args); + } } - if (recursive) { - args.push('--recursive'); + else { + const args = ['-c', 'protocol.version=2']; + args.push('submodule', 'update', '--init', '--force'); + if (fetchDepth > 0) { + args.push(`--depth=${fetchDepth}`); + } + if (recursive) { + args.push('--recursive'); + } + yield this.execGit(args); } - yield this.execGit(args); }); } submoduleStatus() { @@ -1342,7 +1357,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 +1820,7 @@ function getInputs() { // Submodules result.submodules = false; result.nestedSubmodules = false; + result.submoduleDirectories = null; const submodulesString = (core.getInput('submodules') || '').toUpperCase(); if (submodulesString == 'RECURSIVE') { result.submodules = true; @@ -1813,8 +1829,18 @@ 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; + } + else { + result.submoduleDirectories = null; + } 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 8e42a38..9c37043 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[] | null + ): Promise submoduleStatus(): Promise tagExists(pattern: string): Promise tryClean(): Promise @@ -409,18 +413,38 @@ class GitCommandManager { await this.execGit(args) } - async submoduleUpdate(fetchDepth: number, recursive: boolean): Promise { - const args = ['-c', 'protocol.version=2'] - args.push('submodule', 'update', '--init', '--force') - if (fetchDepth > 0) { - args.push(`--depth=${fetchDepth}`) - } + async submoduleUpdate( + fetchDepth: number, + recursive: boolean, + submoduleDirectories: string[] | null + ): Promise { + if (submoduleDirectories) { + for (const submodule of submoduleDirectories) { + const args = ['-c', 'protocol.version=2'] + args.push('submodule', 'update', '--init', '--force', submodule) + if (fetchDepth > 0) { + args.push(`--depth=${fetchDepth}`) + } - if (recursive) { - args.push('--recursive') - } + if (recursive) { + args.push('--recursive') + } - await this.execGit(args) + await this.execGit(args) + } + } else { + const args = ['-c', 'protocol.version=2'] + args.push('submodule', 'update', '--init', '--force') + if (fetchDepth > 0) { + args.push(`--depth=${fetchDepth}`) + } + + if (recursive) { + args.push('--recursive') + } + + await this.execGit(args) + } } async submoduleStatus(): Promise { diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts index f723d94..122e131 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 4e41ac3..1555489 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[] | null + /** * The auth token to use when fetching the repository */ diff --git a/src/input-helper.ts b/src/input-helper.ts index 059232f..f367ce0 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 = null const submodulesString = (core.getInput('submodules') || '').toUpperCase() if (submodulesString == 'RECURSIVE') { result.submodules = true @@ -132,9 +133,18 @@ 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 + } else { + result.submoduleDirectories = null + } + 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})