Repository: renovatebot/renovate
Pull request #35150: fix(pep621): make name for uv index optional
Author: login: nikstuckenbrock
Labels: name: auto:no-done-comments
description: Don't say "Done" or "Please review" - request a review instead
Created: 2025-04-02T13:12:50Z | Merged: 2025-04-28T14:25:28Z

## Changes

```
name: z.string(),
```

```
name: z.string().optional(),
```

## Context

Regarding to the official `uv` [documentation](https://docs.astral.sh/uv/configuration/indexes/#defining-an-index) a name for a new defined index is optional: `# Optional name for the index.`. Therefor I've modified the zod schema for the uv index accordingly.

## Documentation (please check one with an [x])

- [ ] I have updated the documentation, or
- [x] No documentation update is required

## How I've tested my work (please select one)

I have verified these changes via:

- [x] Code inspection only, or
- [ ] Newly added/modified unit tests, or
- [ ] No unit tests but ran on a real repository, or
- [ ] Both unit tests + ran on a real repository


Diff:
diff --git a/lib/modules/manager/pep621/processors/uv.spec.ts b/lib/modules/manager/pep621/processors/uv.spec.ts
index 0a2f249ffa3..633f5f43897 100644
--- a/lib/modules/manager/pep621/processors/uv.spec.ts
+++ b/lib/modules/manager/pep621/processors/uv.spec.ts
@@ -226,6 +226,48 @@ describe('modules/manager/pep621/processors/uv', () => {
     ]);
   });
 
+  it('index with optional name', () => {
+    const pyproject = {
+      tool: {
+        uv: {
+          index: [
+            {
+              url: 'https://foo.com/simple',
+              default: true,
+              explicit: false,
+            },
+          ],
+        },
+      },
+    };
+
+    const dependencies = [
+      {
+        depName: 'dep1',
+        packageName: 'dep1',
+      },
+      {
+        depName: 'dep2',
+        packageName: 'dep2',
+      },
+    ];
+
+    const result = processor.process(pyproject, dependencies);
+
+    expect(result).toEqual([
+      {
+        depName: 'dep1',
+        registryUrls: ['https://foo.com/simple'],
+        packageName: 'dep1',
+      },
+      {
+        depName: 'dep2',
+        registryUrls: ['https://foo.com/simple'],
+        packageName: 'dep2',
+      },
+    ]);
+  });
+
   it('override implicit default index', () => {
     const pyproject = {
       tool: {
@@ -530,6 +572,12 @@ describe('modules/manager/pep621/processors/uv', () => {
           datasource: PypiDatasource.id,
           registryUrls: ['https://pinned.com/simple'],
         },
+        {
+          packageName: 'dep7',
+          depType: depTypes.dependencies,
+          datasource: PypiDatasource.id,
+          registryUrls: ['https://unnamed.com/simple'],
+        },
       ];
       const result = await processor.updateArtifacts(
         {
@@ -551,6 +599,11 @@ describe('modules/manager/pep621/processors/uv', () => {
                   default: false,
                   explicit: true,
                 },
+                {
+                  url: 'https://unnamed.com/simple',
+                  default: false,
+                  explicit: true,
+                },
               ],
             },
           },
@@ -567,7 +620,7 @@ describe('modules/manager/pep621/processors/uv', () => {
       ]);
       expect(execSnapshots).toMatchObject([
         {
-          cmd: 'uv lock --upgrade-package dep1 --upgrade-package dep2 --upgrade-package dep3 --upgrade-package dep4 --upgrade-package dep5 --upgrade-package dep6',
+          cmd: 'uv lock --upgrade-package dep1 --upgrade-package dep2 --upgrade-package dep3 --upgrade-package dep4 --upgrade-package dep5 --upgrade-package dep6 --upgrade-package dep7',
           options: {
             env: {
               GIT_CONFIG_COUNT: '6',
diff --git a/lib/modules/manager/pep621/processors/uv.ts b/lib/modules/manager/pep621/processors/uv.ts
index 13cc27204c0..5f0b160d1fd 100644
--- a/lib/modules/manager/pep621/processors/uv.ts
+++ b/lib/modules/manager/pep621/processors/uv.ts
@@ -344,6 +344,11 @@ async function getUvIndexCredentials(
       continue;
     }
 
+    // If no name is provided for the index, authentication information must be passed through alternative methods
+    if (!name) {
+      continue;
+    }
+
     const { username, password } = await getUsernamePassword(parsedUrl);
 
     const NAME = name.toUpperCase().replace(/[^A-Z0-9]/g, '_');
diff --git a/lib/modules/manager/pep621/schema.ts b/lib/modules/manager/pep621/schema.ts
index 0521b1d4b5b..29df1c9fd0a 100644
--- a/lib/modules/manager/pep621/schema.ts
+++ b/lib/modules/manager/pep621/schema.ts
@@ -80,7 +80,7 @@ const UvSchema = z.object({
   index: z
     .array(
       z.object({
-        name: z.string(),
+        name: z.string().optional(),
         url: z.string(),
         default: z.boolean().default(false),
         explicit: z.boolean().default(false),


Reviews:
user: login: rarkins
body: This `name` field is used in these three places:
- https://github.com/renovatebot/renovate/blob/f17caf4c1da65d63c359727cd24fcef770d9b8ff/lib/modules/manager/pep621/processors/uv.ts#L42
- https://github.com/renovatebot/renovate/blob/f17caf4c1da65d63c359727cd24fcef770d9b8ff/lib/modules/manager/pep621/processors/uv.ts#L69
-https://github.com/renovatebot/renovate/blob/f17caf4c1da65d63c359727cd24fcef770d9b8ff/lib/modules/manager/pep621/processors/uv.ts#L349-L357

We need to assess the impact on this field being undefined.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T04:55:45Z
user: login: viceice
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-03T05:01:19Z
user: login: rarkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T12:31:20Z
user: login: nikstuckenbrock
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T12:54:10Z
user: login: rarkins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T13:07:50Z
user: login: nikstuckenbrock
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T13:21:07Z
user: login: rarkins
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T13:25:42Z
user: login: viceice
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-28T14:18:51Z

Review comments:
diff_hunk: @@ -344,6 +344,11 @@ async function getUvIndexCredentials(
       continue;
     }
 
+    // If no name is provided for the index, authentication information must be passed through alternative methods
+    if (!name) {
+      continue;
+    }
path: lib/modules/manager/pep621/processors/uv.ts
user: login: rarkins
body: I don't understand this part. I don't see `name` used further, so what prevents its use?
created_at: 2025-04-28T12:30:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -344,6 +344,11 @@ async function getUvIndexCredentials(
       continue;
     }
 
+    // If no name is provided for the index, authentication information must be passed through alternative methods
+    if (!name) {
+      continue;
+    }
path: lib/modules/manager/pep621/processors/uv.ts
user: login: nikstuckenbrock
body: @rarkins If you take a look at line 354 in the same file you'll see that the name is refrenced and `.toUpperCase().replace(/[^A-Z0-9]/g, '_')` gets applied to it. If it is `undefined` this will lead to `Cannot read properties of undefined (reading 'toUpperCase')`. Therefor I've added the check.
created_at: 2025-04-28T12:54:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -80,7 +80,7 @@ const UvSchema = z.object({
   index: z
     .array(
       z.object({
-        name: z.string(),
+        name: z.string().optional(),
path: lib/modules/manager/pep621/schema.ts
user: login: rarkins
body: Does this have any impact on the extract part?
created_at: 2025-04-28T13:07:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -80,7 +80,7 @@ const UvSchema = z.object({
   index: z
     .array(
       z.object({
-        name: z.string(),
+        name: z.string().optional(),
path: lib/modules/manager/pep621/schema.ts
user: login: nikstuckenbrock
body: No, because the extraction and uprage process is done by `uv` itself which should be able to handle it's own specification. Therefor handling errors related to this are in the scope of `uv` not `renovate`.
created_at: 2025-04-28T13:21:07Z
author_association: CONTRIBUTOR

PR comments:
user: login: nikstuckenbrock
created_at: 2025-04-03T06:51:19Z
body: Hey @viceice @rarkins,

thanks for the quick response. In my opinion the first two references shouldn't be a problem. For the `getUvIndexCredentials` to work a name for the index is required. Therefor if no name exists I would assume skipping it is the most stable option. Let me know what you think. :)
author_association: CONTRIBUTOR
user: login: rarkins
created_at: 2025-04-03T06:58:12Z
body: > thanks for the quick response. In my opinion the first two references shouldn't be a problem. For the `getUvIndexCredentials` to work a name for the index is required. Therefor if no name exists I would assume skipping it is the most stable option. Let me know what you think. :)

How about the other usage? It seems to be logic relating to "is this the default index or not?" so what's the impact if one or both of them are undefined? If both undefined then they _match_ as default.
author_association: CONTRIBUTOR
user: login: nikstuckenbrock
created_at: 2025-04-03T07:24:22Z
body: > > thanks for the quick response. In my opinion the first two references shouldn't be a problem. For the `getUvIndexCredentials` to work a name for the index is required. Therefor if no name exists I would assume skipping it is the most stable option. Let me know what you think. :)
> 
> How about the other usage? It seems to be logic relating to "is this the default index or not?" so what's the impact if one or both of them are undefined? If both undefined then they _match_ as default.

Well, in this case all indizes missing a name are considered default and `uv` for example will try to check if it finds the package in one of the registries one after another. In my case I'm using GitLab and auth using a `.netrc` file containing a `CI_JOB_TOKEN` for my pipeline which has access to all the private package registries. But since this is default `uv` behaviour I don't see a problem here?
author_association: CONTRIBUTOR
user: login: nikstuckenbrock
created_at: 2025-04-03T09:09:00Z
body: @viceice Can you tell me were to add a test so the codecov step does not fail anymore?
author_association: CONTRIBUTOR
user: login: rarkins
created_at: 2025-04-03T09:13:01Z
body: Yes the failure appears to be because of coverage only
author_association: CONTRIBUTOR
user: login: nikstuckenbrock
created_at: 2025-04-09T08:20:28Z
body: > Yes the failure appears to be because of coverage only

@rarkins i think we have misunderstood each other here. my question is at what point do i have to add the tests so that the coverage step runs through?
author_association: CONTRIBUTOR
user: login: rarkins
created_at: 2025-04-09T11:32:06Z
body: If you look in https://github.com/renovatebot/renovate/pull/35150/files you should see an annotation of the line which is missed
author_association: CONTRIBUTOR
user: login: nikstuckenbrock
created_at: 2025-04-28T12:27:07Z
body: @rarkins @viceice Coverage should be good now.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-28T12:29:35Z
body: Hi there,

You are using `done` comments which cause a lot of noise/notifications. Instead, please use GitHub's web interface to request another review. Please read our [contributing guidelines](https://github.com/renovatebot/renovate/blob/main/.github/contributing.md#resolve-review-comments-instead-of-commenting) to reduce noise.

Good luck,

The Renovate team
author_association: CONTRIBUTOR
user: login: renovate-release
created_at: 2025-04-28T14:45:37Z
body: :tada: This PR is included in version 39.262.1 :tada:

The release is available on:
- [GitHub release](https://github.com/renovatebot/renovate/releases/tag/39.262.1)
- [npm package (@latest dist-tag)](https://www.npmjs.com/package/renovate/v/39.262.1)
- `39.262.1`

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: NONE