From 01ee73c28a3c4a0e85ac41b460e57cdb8f06c78b Mon Sep 17 00:00:00 2001 From: Tobias <tobias.gabriel@sap.com> Date: Tue, 18 Jul 2023 18:17:04 +0200 Subject: [PATCH] feat(manager/git-submodule): add authentication support (#22061) Co-authored-by: Rhys Arkins <rhys@arkins.net> --- .../manager/git-submodules/extract.spec.ts | 23 +++++++++++++++++ lib/modules/manager/git-submodules/extract.ts | 16 ++++++++---- lib/modules/manager/git-submodules/readme.md | 21 +++++++++++++--- .../manager/git-submodules/update.spec.ts | 25 +++++++++++++++++++ lib/modules/manager/git-submodules/update.ts | 10 +++++++- 5 files changed, 85 insertions(+), 10 deletions(-) diff --git a/lib/modules/manager/git-submodules/extract.spec.ts b/lib/modules/manager/git-submodules/extract.spec.ts index 6850f30f19..c6e53cc5ea 100644 --- a/lib/modules/manager/git-submodules/extract.spec.ts +++ b/lib/modules/manager/git-submodules/extract.spec.ts @@ -57,6 +57,29 @@ describe('modules/manager/git-submodules/extract', () => { expect(res?.deps[0].currentValue).toBe('main'); }); + it('default branch is detected with using git environment variables when no branch is specified', async () => { + gitMock.listRemote.mockResolvedValueOnce( + 'ref: refs/heads/main HEAD\n5701164b9f5edba1f6ca114c491a564ffb55a964 HEAD' + ); + hostRules.add({ + hostType: 'github', + matchHost: 'github.com', + token: 'abc123', + }); + const res = await extractPackageFile('', '.gitmodules.2', {}); + expect(res?.deps).toHaveLength(1); + expect(res?.deps[0].currentValue).toBe('main'); + expect(gitMock.env).toHaveBeenCalledWith({ + GIT_CONFIG_COUNT: '3', + GIT_CONFIG_KEY_0: 'url.https://ssh:abc123@github.com/.insteadOf', + GIT_CONFIG_KEY_1: 'url.https://git:abc123@github.com/.insteadOf', + GIT_CONFIG_KEY_2: 'url.https://abc123@github.com/.insteadOf', + GIT_CONFIG_VALUE_0: 'ssh://git@github.com/', + GIT_CONFIG_VALUE_1: 'git@github.com:', + GIT_CONFIG_VALUE_2: 'https://github.com/', + }); + }); + it('default to master if no branch can be detected', async () => { const res = await extractPackageFile('', '.gitmodules.2', {}); expect(res?.deps).toHaveLength(1); diff --git a/lib/modules/manager/git-submodules/extract.ts b/lib/modules/manager/git-submodules/extract.ts index 468a89a747..b38aef1677 100644 --- a/lib/modules/manager/git-submodules/extract.ts +++ b/lib/modules/manager/git-submodules/extract.ts @@ -4,6 +4,7 @@ import upath from 'upath'; import { GlobalConfig } from '../../../config/global'; import { logger } from '../../../logger'; import { detectPlatform } from '../../../util/common'; +import { getGitEnvironmentVariables } from '../../../util/git/auth'; import { simpleGitConfig } from '../../../util/git/config'; import { getHttpUrl, getRemoteUrlWithToken } from '../../../util/git/url'; import { regEx } from '../../../util/regex'; @@ -37,11 +38,16 @@ async function getUrl( const headRefRe = regEx(/ref: refs\/heads\/(?<branch>\w+)\s/); async function getDefaultBranch(subModuleUrl: string): Promise<string> { - const val = await Git(simpleGitConfig()).listRemote([ - '--symref', - subModuleUrl, - 'HEAD', - ]); + const gitSubmoduleAuthEnvironmentVariables = getGitEnvironmentVariables(); + const gitEnv = { + // pass all existing env variables + ...process.env, + // add all known git Variables + ...gitSubmoduleAuthEnvironmentVariables, + }; + const val = await Git(simpleGitConfig()) + .env(gitEnv) + .listRemote(['--symref', subModuleUrl, 'HEAD']); return headRefRe.exec(val)?.groups?.branch ?? 'master'; } diff --git a/lib/modules/manager/git-submodules/readme.md b/lib/modules/manager/git-submodules/readme.md index 680c780f15..a6194f456c 100644 --- a/lib/modules/manager/git-submodules/readme.md +++ b/lib/modules/manager/git-submodules/readme.md @@ -1,7 +1,4 @@ -Keeps publicly accessible Git submodules updated within a repository. - -Renovate does not support updating Git submodules that are hosted on a private repository. -Subscribe to [issue #10149 on GitHub](https://github.com/renovatebot/renovate/issues/10149) to keep track of our progress towards supporting private Git submodules. +Keeps Git submodules updated within a repository. You can customize the per-submodule checks of the git-submodules manager like this: @@ -13,3 +10,19 @@ You can customize the per-submodule checks of the git-submodules manager like th } } ``` + +### Private Modules Authentication + +Before running the `git` commands to update the submodules, Renovate exports `git` [`insteadOf`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-urlltbasegtinsteadOf) directives in environment variables. + +The following logic is executed prior to "submodules" updating: + +The token from the `hostRules` entry matching `hostType=github` and `matchHost=api.github.com` is added as the default authentication for `github.com`. +For those running against `github.com`, this token will be the default platform token. + +Next, all `hostRules` with both a token and `matchHost` will be fetched, except for any github.com one from above. + +Rules from this list are converted to environment variable directives if they match _any_ of the following characteristics: + +- No `hostType` is defined, or +- `hostType` is a platform (`github`, `gitlab`, `azure`, etc.) diff --git a/lib/modules/manager/git-submodules/update.spec.ts b/lib/modules/manager/git-submodules/update.spec.ts index 6b5111ed5c..2b77fd3803 100644 --- a/lib/modules/manager/git-submodules/update.spec.ts +++ b/lib/modules/manager/git-submodules/update.spec.ts @@ -62,5 +62,30 @@ describe('modules/manager/git-submodules/update', () => { }); expect(update).toBe(''); }); + + it('returns content on update and uses git environment variables', async () => { + gitMock.submoduleUpdate.mockResolvedValue(''); + gitMock.checkout.mockResolvedValue(''); + hostRules.add({ + hostType: 'github', + matchHost: 'github.com', + token: 'abc123', + }); + + const update = await updateDependency({ + fileContent: '', + upgrade, + }); + expect(update).toBe(''); + expect(gitMock.env).toHaveBeenCalledWith({ + GIT_CONFIG_COUNT: '3', + GIT_CONFIG_KEY_0: 'url.https://ssh:abc123@github.com/.insteadOf', + GIT_CONFIG_KEY_1: 'url.https://git:abc123@github.com/.insteadOf', + GIT_CONFIG_KEY_2: 'url.https://abc123@github.com/.insteadOf', + GIT_CONFIG_VALUE_0: 'ssh://git@github.com/', + GIT_CONFIG_VALUE_1: 'git@github.com:', + GIT_CONFIG_VALUE_2: 'https://github.com/', + }); + }); }); }); diff --git a/lib/modules/manager/git-submodules/update.ts b/lib/modules/manager/git-submodules/update.ts index 54df033376..75eae26982 100644 --- a/lib/modules/manager/git-submodules/update.ts +++ b/lib/modules/manager/git-submodules/update.ts @@ -2,6 +2,7 @@ import Git from 'simple-git'; import upath from 'upath'; import { GlobalConfig } from '../../../config/global'; import { logger } from '../../../logger'; +import { getGitEnvironmentVariables } from '../../../util/git/auth'; import type { UpdateDependencyConfig } from '../types'; export default async function updateDependency({ @@ -9,7 +10,14 @@ export default async function updateDependency({ upgrade, }: UpdateDependencyConfig): Promise<string | null> { const localDir = GlobalConfig.get('localDir'); - const git = Git(localDir); + const gitSubmoduleAuthEnvironmentVariables = getGitEnvironmentVariables(); + const gitEnv = { + // pass all existing env variables + ...process.env, + // add all known git Variables + ...gitSubmoduleAuthEnvironmentVariables, + }; + const git = Git(localDir).env(gitEnv); const submoduleGit = Git(upath.join(localDir, upgrade.depName)); try { -- GitLab