Skip to content
Snippets Groups Projects
Unverified Commit 1ca7d263 authored by Tor Arvid Lund's avatar Tor Arvid Lund Committed by GitHub
Browse files

feat: export UV_EXTRA_INDEX_URL before doing 'uv lock' (#31840)


Co-authored-by: default avatarRhys Arkins <rhys@arkins.net>
parent 839ade15
No related branches found
No related tags found
No related merge requests found
import { join } from 'upath'; import { join } from 'upath';
import { mockExecAll } from '../../../../../test/exec-util'; import { mockExecAll } from '../../../../../test/exec-util';
import { fs, mockedFunction } from '../../../../../test/util'; import { fs, hostRules, mockedFunction } from '../../../../../test/util';
import { GlobalConfig } from '../../../../config/global'; import { GlobalConfig } from '../../../../config/global';
import type { RepoGlobalConfig } from '../../../../config/types'; import type { RepoGlobalConfig } from '../../../../config/types';
import { getPkgReleases as _getPkgReleases } from '../../../datasource'; import { getPkgReleases as _getPkgReleases } from '../../../datasource';
...@@ -268,6 +268,69 @@ describe('modules/manager/pep621/processors/uv', () => { ...@@ -268,6 +268,69 @@ describe('modules/manager/pep621/processors/uv', () => {
]); ]);
}); });
it('performs update on private package registry', async () => {
const execSnapshots = mockExecAll();
GlobalConfig.set(adminConfig);
hostRules.add({
matchHost: 'https://example.com',
username: 'user',
password: 'pass',
});
fs.getSiblingFileName.mockReturnValueOnce('uv.lock');
fs.readLocalFile.mockResolvedValueOnce('test content');
fs.readLocalFile.mockResolvedValueOnce('changed test content');
// python
getPkgReleases.mockResolvedValueOnce({
releases: [{ version: '3.11.1' }, { version: '3.11.2' }],
});
// uv
getPkgReleases.mockResolvedValueOnce({
releases: [{ version: '0.2.35' }, { version: '0.2.28' }],
});
const updatedDeps = [
{
packageName: 'dep1',
depType: depTypes.dependencies,
registryUrls: ['https://foobar.com'],
},
{
packageName: 'dep2',
depType: depTypes.dependencies,
registryUrls: ['https://example.com'],
},
];
const result = await processor.updateArtifacts(
{
packageFileName: 'pyproject.toml',
newPackageFileContent: '',
config: {},
updatedDeps,
},
{},
);
expect(result).toEqual([
{
file: {
contents: 'changed test content',
path: 'uv.lock',
type: 'addition',
},
},
]);
expect(execSnapshots).toMatchObject([
{
cmd: 'uv lock --upgrade-package dep1 --upgrade-package dep2',
options: {
env: {
UV_EXTRA_INDEX_URL:
'https://foobar.com/ https://user:pass@example.com/',
},
},
},
]);
});
it('return update on lockfileMaintenance', async () => { it('return update on lockfileMaintenance', async () => {
const execSnapshots = mockExecAll(); const execSnapshots = mockExecAll();
GlobalConfig.set(adminConfig); GlobalConfig.set(adminConfig);
......
...@@ -2,10 +2,14 @@ import is from '@sindresorhus/is'; ...@@ -2,10 +2,14 @@ import is from '@sindresorhus/is';
import { quote } from 'shlex'; import { quote } from 'shlex';
import { TEMPORARY_ERROR } from '../../../../constants/error-messages'; import { TEMPORARY_ERROR } from '../../../../constants/error-messages';
import { logger } from '../../../../logger'; import { logger } from '../../../../logger';
import type { HostRule } from '../../../../types';
import { exec } from '../../../../util/exec'; import { exec } from '../../../../util/exec';
import type { ExecOptions, ToolConstraint } from '../../../../util/exec/types'; import type { ExecOptions, ToolConstraint } from '../../../../util/exec/types';
import { getSiblingFileName, readLocalFile } from '../../../../util/fs'; import { getSiblingFileName, readLocalFile } from '../../../../util/fs';
import { find } from '../../../../util/host-rules';
import { Result } from '../../../../util/result'; import { Result } from '../../../../util/result';
import { parseUrl } from '../../../../util/url';
import { PypiDatasource } from '../../../datasource/pypi';
import type { import type {
PackageDependency, PackageDependency,
UpdateArtifact, UpdateArtifact,
...@@ -115,8 +119,12 @@ export class UvProcessor implements PyProjectProcessor { ...@@ -115,8 +119,12 @@ export class UvProcessor implements PyProjectProcessor {
constraint: config.constraints?.uv, constraint: config.constraints?.uv,
}; };
const extraEnv = {
...getUvExtraIndexUrl(updateArtifact.updatedDeps),
};
const execOptions: ExecOptions = { const execOptions: ExecOptions = {
cwdFile: packageFileName, cwdFile: packageFileName,
extraEnv,
docker: {}, docker: {},
userConfiguredEnv: config.env, userConfiguredEnv: config.env,
toolConstraints: [pythonConstraint, uvConstraint], toolConstraints: [pythonConstraint, uvConstraint],
...@@ -191,3 +199,33 @@ function generateCMD(updatedDeps: Upgrade[]): string { ...@@ -191,3 +199,33 @@ function generateCMD(updatedDeps: Upgrade[]): string {
return `${uvUpdateCMD} ${deps.map((dep) => `--upgrade-package ${quote(dep)}`).join(' ')}`; return `${uvUpdateCMD} ${deps.map((dep) => `--upgrade-package ${quote(dep)}`).join(' ')}`;
} }
function getMatchingHostRule(url: string | undefined): HostRule {
return find({ hostType: PypiDatasource.id, url });
}
function getUvExtraIndexUrl(deps: Upgrade[]): NodeJS.ProcessEnv {
const registryUrls = new Set(deps.map((dep) => dep.registryUrls).flat());
const extraIndexUrls: string[] = [];
for (const registryUrl of registryUrls) {
const parsedUrl = parseUrl(registryUrl);
if (!parsedUrl) {
continue;
}
const rule = getMatchingHostRule(parsedUrl.toString());
if (rule.username) {
parsedUrl.username = rule.username;
}
if (rule.password) {
parsedUrl.password = rule.password;
}
extraIndexUrls.push(parsedUrl.toString());
}
return {
UV_EXTRA_INDEX_URL: extraIndexUrls.join(' '),
};
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment