From 15dd2fcf02bf0471b53ae5411191f7804d3b7113 Mon Sep 17 00:00:00 2001 From: Tobias <tobias.gabriel@sap.com> Date: Wed, 20 Oct 2021 13:16:49 +0200 Subject: [PATCH] feat(git): insteadOf environment variables for authentication (#11077) Co-authored-by: Michael Kriese <michael.kriese@visualon.de> --- .../usage/getting-started/private-packages.md | 4 +- .../__snapshots__/artifacts.spec.ts.snap | 5 +- lib/manager/gomod/artifacts.ts | 21 ++-- lib/util/git/auth.spec.ts | 106 ++++++++++++++++++ lib/util/git/auth.ts | 40 +++++++ 5 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 lib/util/git/auth.spec.ts create mode 100644 lib/util/git/auth.ts diff --git a/docs/usage/getting-started/private-packages.md b/docs/usage/getting-started/private-packages.md index ff8614e966..404bfb5851 100644 --- a/docs/usage/getting-started/private-packages.md +++ b/docs/usage/getting-started/private-packages.md @@ -123,8 +123,8 @@ Any `hostRules` with `hostType=packagist` are also included. ### gomod -If a `github.com` token is found in `hostRules`, then it is written out to local git config prior to running `go` commands. -The command run is `git config --global url."https://${token}@github.com/".insteadOf "https://github.com/"`. +If a `github.com` token is found in `hostRules`, then it is written out to local [GIT*CONFIG*](https://git-scm.com/docs/git-config#Documentation/git-config.txt-GITCONFIGCOUNT) variables prior to running `go` commands. +The environment variables used are: `GIT_CONFIG_KEY_0=url.https://${token}@github.com/.insteadOf GIT_CONFIG_VALUE_0=https://github.com/ GIT_CONFIG_COUNT=1`. ### npm diff --git a/lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap b/lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap index 021acb2565..a446d705f8 100644 --- a/lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap +++ b/lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap @@ -239,12 +239,15 @@ Array [ }, }, Object { - "cmd": "docker run --rm --name=renovate_go --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/renovate/cache\\":\\"/tmp/renovate/cache\\" -e GOPATH -e GOPROXY -e GOPRIVATE -e GONOPROXY -e GONOSUMDB -e GOFLAGS -e CGO_ENABLED -w \\"/tmp/github/some/repo\\" renovate/go:latest bash -l -c \\"git config --global url.\\\\\\"https://some-token@github.com/\\\\\\".insteadOf \\\\\\"https://github.com/\\\\\\" && go get -d ./...\\"", + "cmd": "docker run --rm --name=renovate_go --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/renovate/cache\\":\\"/tmp/renovate/cache\\" -e GOPATH -e GOPROXY -e GOPRIVATE -e GONOPROXY -e GONOSUMDB -e GOFLAGS -e CGO_ENABLED -e GIT_CONFIG_KEY_0 -e GIT_CONFIG_VALUE_0 -e GIT_CONFIG_COUNT -w \\"/tmp/github/some/repo\\" renovate/go:latest bash -l -c \\"go get -d ./...\\"", "options": Object { "cwd": "/tmp/github/some/repo", "encoding": "utf-8", "env": Object { "CGO_ENABLED": "1", + "GIT_CONFIG_COUNT": "1", + "GIT_CONFIG_KEY_0": "url.https://some-token@github.com/.insteadOf", + "GIT_CONFIG_VALUE_0": "https://github.com/", "GOFLAGS": "-modcacherw", "GONOPROXY": "noproxy.example.com/*", "GONOSUMDB": "1", diff --git a/lib/manager/gomod/artifacts.ts b/lib/manager/gomod/artifacts.ts index 9b6f08de39..d770c9edcc 100644 --- a/lib/manager/gomod/artifacts.ts +++ b/lib/manager/gomod/artifacts.ts @@ -1,5 +1,4 @@ import is from '@sindresorhus/is'; -import { quote } from 'shlex'; import { dirname, join } from 'upath'; import { getGlobalConfig } from '../../config/global'; import { PlatformId } from '../../constants'; @@ -8,6 +7,7 @@ import { logger } from '../../logger'; import { ExecOptions, exec } from '../../util/exec'; import { ensureCacheDir, readLocalFile, writeLocalFile } from '../../util/fs'; import { getRepoStatus } from '../../util/git'; +import { getGitAuthenticatedEnvironmentVariables } from '../../util/git/auth'; import { find } from '../../util/host-rules'; import { regEx } from '../../util/regex'; import { isValid } from '../../versioning/semver'; @@ -18,19 +18,22 @@ import type { UpdateArtifactsResult, } from '../types'; -function getPreCommands(): string[] | null { +function getGitEnvironmentVariables(): NodeJS.ProcessEnv { + let environmentVariables: NodeJS.ProcessEnv = {}; + const credentials = find({ hostType: PlatformId.Github, url: 'https://api.github.com/', }); - let preCommands = null; + if (credentials?.token) { - const token = quote(credentials.token); - preCommands = [ - `git config --global url.\"https://${token}@github.com/\".insteadOf \"https://github.com/\"`, // eslint-disable-line no-useless-escape - ]; + environmentVariables = getGitAuthenticatedEnvironmentVariables( + 'https://github.com/', + credentials.token + ); } - return preCommands; + + return environmentVariables; } function getUpdateImportPathCmds( @@ -127,12 +130,12 @@ export async function updateArtifacts({ GONOSUMDB: process.env.GONOSUMDB, GOFLAGS: useModcacherw(config.constraints?.go) ? '-modcacherw' : null, CGO_ENABLED: getGlobalConfig().binarySource === 'docker' ? '0' : null, + ...getGitEnvironmentVariables(), }, docker: { image: 'go', tagConstraint: config.constraints?.go, tagScheme: 'npm', - preCommands: getPreCommands(), }, }; diff --git a/lib/util/git/auth.spec.ts b/lib/util/git/auth.spec.ts new file mode 100644 index 0000000000..7e54bf89aa --- /dev/null +++ b/lib/util/git/auth.spec.ts @@ -0,0 +1,106 @@ +import { getGitAuthenticatedEnvironmentVariables } from './auth'; + +describe('util/git/auth', () => { + afterEach(() => { + delete process.env.GIT_CONFIG_COUNT; + }); + describe('getGitAuthenticatedEnvironmentVariables()', () => { + it('returns url with token', () => { + expect( + getGitAuthenticatedEnvironmentVariables( + 'https://github.com/', + 'token1234' + ) + ).toStrictEqual({ + GIT_CONFIG_KEY_0: 'url.https://token1234@github.com/.insteadOf', + GIT_CONFIG_VALUE_0: 'https://github.com/', + GIT_CONFIG_COUNT: '1', + }); + }); + + it('returns url with correctly encoded token', () => { + expect( + getGitAuthenticatedEnvironmentVariables( + 'https://github.com/', + 'token:1234' + ) + ).toStrictEqual({ + GIT_CONFIG_KEY_0: 'url.https://token%3A1234@github.com/.insteadOf', + GIT_CONFIG_VALUE_0: 'https://github.com/', + GIT_CONFIG_COUNT: '1', + }); + }); + + it('returns url with token and already existing GIT_CONFIG_COUNT from parameter', () => { + expect( + getGitAuthenticatedEnvironmentVariables( + 'https://github.com/', + 'token1234', + { GIT_CONFIG_COUNT: '1' } + ) + ).toStrictEqual({ + GIT_CONFIG_KEY_1: 'url.https://token1234@github.com/.insteadOf', + GIT_CONFIG_VALUE_1: 'https://github.com/', + GIT_CONFIG_COUNT: '2', + }); + }); + + it('returns url with token and already existing GIT_CONFIG_COUNT from parameter over environment', () => { + process.env.GIT_CONFIG_COUNT = '54'; + expect( + getGitAuthenticatedEnvironmentVariables( + 'https://github.com/', + 'token1234', + { GIT_CONFIG_COUNT: '1' } + ) + ).toStrictEqual({ + GIT_CONFIG_KEY_1: 'url.https://token1234@github.com/.insteadOf', + GIT_CONFIG_VALUE_1: 'https://github.com/', + GIT_CONFIG_COUNT: '2', + }); + }); + + it('returns url with token and already existing GIT_CONFIG_COUNT from environment', () => { + process.env.GIT_CONFIG_COUNT = '1'; + expect( + getGitAuthenticatedEnvironmentVariables( + 'https://github.com/', + 'token1234' + ) + ).toStrictEqual({ + GIT_CONFIG_KEY_1: 'url.https://token1234@github.com/.insteadOf', + GIT_CONFIG_VALUE_1: 'https://github.com/', + GIT_CONFIG_COUNT: '2', + }); + }); + + it('returns url with token and passthrough existing variables', () => { + expect( + getGitAuthenticatedEnvironmentVariables( + 'https://github.com/', + 'token1234', + { RANDOM_VARIABLE: 'random' } + ) + ).toStrictEqual({ + GIT_CONFIG_KEY_0: 'url.https://token1234@github.com/.insteadOf', + GIT_CONFIG_VALUE_0: 'https://github.com/', + GIT_CONFIG_COUNT: '1', + RANDOM_VARIABLE: 'random', + }); + }); + + it('return url with token with invalid GIT_CONFIG_COUNT from environment', () => { + process.env.GIT_CONFIG_COUNT = 'notvalid'; + expect( + getGitAuthenticatedEnvironmentVariables( + 'https://github.com/', + 'token1234' + ) + ).toStrictEqual({ + GIT_CONFIG_KEY_0: 'url.https://token1234@github.com/.insteadOf', + GIT_CONFIG_VALUE_0: 'https://github.com/', + GIT_CONFIG_COUNT: '1', + }); + }); + }); +}); diff --git a/lib/util/git/auth.ts b/lib/util/git/auth.ts new file mode 100644 index 0000000000..3a6c56b218 --- /dev/null +++ b/lib/util/git/auth.ts @@ -0,0 +1,40 @@ +import { logger } from '../../logger'; +import { getHttpUrl } from './url'; + +/* + Add authorization to a Git Url and returns the updated environment variables +*/ +export function getGitAuthenticatedEnvironmentVariables( + gitUrl: string, + token: string, + environmentVariables?: NodeJS.ProcessEnv +): NodeJS.ProcessEnv { + // check if the environmentVariables already contain a GIT_CONFIG_COUNT or if the process has one + const gitConfigCountEnvVariable = + environmentVariables?.GIT_CONFIG_COUNT || process.env.GIT_CONFIG_COUNT; + let gitConfigCount = 0; + if (gitConfigCountEnvVariable) { + // passthrough the gitConfigCountEnvVariable environment variable as start value of the index count + gitConfigCount = parseInt(gitConfigCountEnvVariable, 10); + if (Number.isNaN(gitConfigCount)) { + logger.warn( + `Found GIT_CONFIG_COUNT env variable, but couldn't parse the value to an integer: ${process.env.GIT_CONFIG_COUNT}. Ignoring it.` + ); + gitConfigCount = 0; + } + } + + const gitUrlWithToken = getHttpUrl(gitUrl, encodeURIComponent(token)); + + // create a shallow copy of the environmentVariables as base so we don't modify the input parameter object + // add the two new config key and value to the returnEnvironmentVariables object + // increase the CONFIG_COUNT by one and add it to the object + const returnEnvironmentVariables = { + ...environmentVariables, + [`GIT_CONFIG_KEY_${gitConfigCount}`]: `url.${gitUrlWithToken}.insteadOf`, + [`GIT_CONFIG_VALUE_${gitConfigCount}`]: gitUrl, + GIT_CONFIG_COUNT: (gitConfigCount + 1).toString(), + }; + + return returnEnvironmentVariables; +} -- GitLab