diff --git a/lib/modules/manager/pep621/processors/pdm.spec.ts b/lib/modules/manager/pep621/processors/pdm.spec.ts index 05ad3d73dbfaf9..7d7f23252ad9c9 100644 --- a/lib/modules/manager/pep621/processors/pdm.spec.ts +++ b/lib/modules/manager/pep621/processors/pdm.spec.ts @@ -4,6 +4,7 @@ import { fs, mockedFunction } from '../../../../../test/util'; import { GlobalConfig } from '../../../../config/global'; import type { RepoGlobalConfig } from '../../../../config/types'; import { logger } from '../../../../logger'; +import * as hostRules from '../../../../util/host-rules'; import { getPkgReleases as _getPkgReleases } from '../../../datasource'; import type { UpdateArtifactsConfig } from '../../types'; import { depTypes } from '../utils'; @@ -290,5 +291,64 @@ describe('modules/manager/pep621/processors/pdm', () => { }, ]); }); + + it('sets Git environment variables', async () => { + hostRules.add({ + matchHost: 'https://example.com', + username: 'user', + password: 'pass', + }); + const execSnapshots = mockExecAll(); + GlobalConfig.set(adminConfig); + fs.getSiblingFileName.mockReturnValueOnce('pdm.lock'); + fs.readLocalFile.mockResolvedValueOnce('test content'); + fs.readLocalFile.mockResolvedValueOnce('changed test content'); + // python + getPkgReleases.mockResolvedValueOnce({ + releases: [{ version: '3.11.1' }, { version: '3.11.2' }], + }); + // pdm + getPkgReleases.mockResolvedValueOnce({ + releases: [{ version: 'v2.6.1' }, { version: 'v2.5.0' }], + }); + + const result = await processor.updateArtifacts( + { + packageFileName: 'folder/pyproject.toml', + newPackageFileContent: '', + config: { + updateType: 'lockFileMaintenance', + }, + updatedDeps: [], + }, + {}, + ); + expect(result).toEqual([ + { + file: { + contents: 'changed test content', + path: 'pdm.lock', + type: 'addition', + }, + }, + ]); + expect(execSnapshots).toMatchObject([ + { + cmd: 'pdm update --no-sync --update-eager', + options: { + cwd: '/tmp/github/some/repo/folder', + env: { + GIT_CONFIG_COUNT: '3', + GIT_CONFIG_KEY_0: 'url.https://user:pass@example.com/.insteadOf', + GIT_CONFIG_KEY_1: 'url.https://user:pass@example.com/.insteadOf', + GIT_CONFIG_KEY_2: 'url.https://user:pass@example.com/.insteadOf', + GIT_CONFIG_VALUE_0: 'ssh://git@example.com/', + GIT_CONFIG_VALUE_1: 'git@example.com:', + GIT_CONFIG_VALUE_2: 'https://example.com/', + }, + }, + }, + ]); + }); }); }); diff --git a/lib/modules/manager/pep621/processors/pdm.ts b/lib/modules/manager/pep621/processors/pdm.ts index d991c78cc3a9c0..a78097e74deeb0 100644 --- a/lib/modules/manager/pep621/processors/pdm.ts +++ b/lib/modules/manager/pep621/processors/pdm.ts @@ -5,6 +5,7 @@ import { logger } from '../../../../logger'; import { exec } from '../../../../util/exec'; import type { ExecOptions, ToolConstraint } from '../../../../util/exec/types'; import { getSiblingFileName, readLocalFile } from '../../../../util/fs'; +import { getGitEnvironmentVariables } from '../../../../util/git/auth'; import { Result } from '../../../../util/result'; import { PypiDatasource } from '../../../datasource/pypi'; import type { @@ -116,8 +117,12 @@ export class PdmProcessor implements PyProjectProcessor { constraint: config.constraints?.pdm, }; + const extraEnv = { + ...getGitEnvironmentVariables(['pep621']), + }; const execOptions: ExecOptions = { cwdFile: packageFileName, + extraEnv, docker: {}, userConfiguredEnv: config.env, toolConstraints: [pythonConstraint, pdmConstraint], diff --git a/lib/modules/manager/pep621/processors/uv.spec.ts b/lib/modules/manager/pep621/processors/uv.spec.ts index e570a1178076cf..90d7595422cac9 100644 --- a/lib/modules/manager/pep621/processors/uv.spec.ts +++ b/lib/modules/manager/pep621/processors/uv.spec.ts @@ -356,6 +356,13 @@ describe('modules/manager/pep621/processors/uv', () => { cmd: 'uv lock --upgrade-package dep1 --upgrade-package dep2 --upgrade-package dep3 --upgrade-package dep4', options: { env: { + GIT_CONFIG_COUNT: '3', + GIT_CONFIG_KEY_0: 'url.https://user:pass@example.com/.insteadOf', + GIT_CONFIG_KEY_1: 'url.https://user:pass@example.com/.insteadOf', + GIT_CONFIG_KEY_2: 'url.https://user:pass@example.com/.insteadOf', + GIT_CONFIG_VALUE_0: 'ssh://git@example.com/', + GIT_CONFIG_VALUE_1: 'git@example.com:', + GIT_CONFIG_VALUE_2: 'https://example.com/', UV_EXTRA_INDEX_URL: 'https://foobar.com/ https://user:pass@example.com/', }, diff --git a/lib/modules/manager/pep621/processors/uv.ts b/lib/modules/manager/pep621/processors/uv.ts index 4bc86b3fe8b38e..10ea7624481493 100644 --- a/lib/modules/manager/pep621/processors/uv.ts +++ b/lib/modules/manager/pep621/processors/uv.ts @@ -6,6 +6,7 @@ import type { HostRule } from '../../../../types'; import { exec } from '../../../../util/exec'; import type { ExecOptions, ToolConstraint } from '../../../../util/exec/types'; import { getSiblingFileName, readLocalFile } from '../../../../util/fs'; +import { getGitEnvironmentVariables } from '../../../../util/git/auth'; import { find } from '../../../../util/host-rules'; import { Result } from '../../../../util/result'; import { parseUrl } from '../../../../util/url'; @@ -129,6 +130,7 @@ export class UvProcessor implements PyProjectProcessor { }; const extraEnv = { + ...getGitEnvironmentVariables(['pep621']), ...getUvExtraIndexUrl(updateArtifact.updatedDeps), }; const execOptions: ExecOptions = { diff --git a/lib/modules/manager/pep621/readme.md b/lib/modules/manager/pep621/readme.md index db3ac79b0c7268..0084470bf51d5f 100644 --- a/lib/modules/manager/pep621/readme.md +++ b/lib/modules/manager/pep621/readme.md @@ -16,3 +16,20 @@ Available `depType`s: - `tool.uv.dev-dependencies` - `tool.uv.sources` - `tool.hatch.envs.` + +### Private Modules Authentication + +Before running the `pdm` or `uv` commands to update the `pdm.lock` or `uv.lock` respectively, Renovate exports `git` [`insteadOf`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-urlltbasegtinsteadOf) directives in environment variables. + +Renovate uses this logic before it updates any "artifacts": + +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 or username/password 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 these characteristics: + +- No `hostType` is defined, or +- `hostType` is `pep621`, or +- `hostType` is a platform (`github`, `gitlab`, `azure`, etc.)