From 4ca76e40b550ea75980a139015c3b3479478a932 Mon Sep 17 00:00:00 2001
From: Mark Reuter <13319190+reutermj@users.noreply.github.com>
Date: Thu, 30 Jan 2025 04:11:53 -0800
Subject: [PATCH] feat(bazel-module): support `bazel_dep` dependencies without
 the `version` parameter (#33496)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
---
 docs/usage/bazel.md                           |  26 ++
 .../manager/bazel-module/context.spec.ts      |  18 +
 .../manager/bazel-module/extract.spec.ts      | 383 +++++++++++++-----
 .../manager/bazel-module/rules.spec.ts        |  63 ++-
 lib/modules/manager/bazel-module/rules.ts     |   5 +-
 5 files changed, 375 insertions(+), 120 deletions(-)

diff --git a/docs/usage/bazel.md b/docs/usage/bazel.md
index 3ef033f168..14474a8e9e 100644
--- a/docs/usage/bazel.md
+++ b/docs/usage/bazel.md
@@ -80,6 +80,7 @@ If Renovate finds a newer version, it updates `0.15.0` to match that version.
 #### `git_override`
 
 If Renovate finds a [`git_override`](https://bazel.build/rules/lib/globals/module#git_override), it ignores the related `bazel_dep` entry and instead evaluates the `commit` value at the specified `remote`.
+When using `git_override`, the `version` parameter on the `bazel_dep` is optional.
 
 ```python
 bazel_dep(name = "cgrindel_bazel_starlib", version = "0.15.0")
@@ -89,6 +90,13 @@ git_override(
     commit = "fb47f0e9f7c376a7700fc9fe3319231ae57880df",
     remote = "https://github.com/cgrindel/bazel-starlib.git",
 )
+
+bazel_dep(name = "rules_foo")
+git_override(
+    module_name = "rules_foo",
+    remote = "https://github.com/foo/rules_foo.git",
+    commit = "8a1e9abe415eda7cd7f2a744fdac7499ce42cdca",
+)
 ```
 
 If the primary branch has a newer commit than in the list, Renovate updates the `commit` value.
@@ -101,6 +109,7 @@ Renovate only evaluates _two_ attributes from this declaration: `version` and `r
 If a `version` is specified, it overrides the version in the `bazel_dep`.
 In the following example, Renovate notices that the version is pinned to `1.2.3`.
 This results in `rules_foo` being ignored for update evaluation.
+When using `single_version_override`, the `version` parameter on the `bazel_dep` is optional.
 
 ```python
 bazel_dep(name = "rules_foo", version = "1.2.4")
@@ -109,6 +118,13 @@ single_version_override(
   module_name = "rules_foo",
   version = "1.2.3",
 )
+
+bazel_dep(name = "rules_bar")
+
+single_version_override(
+  module_name = "rules_bar",
+  version = "1.2.3",
+)
 ```
 
 If a `registry` is specified, Renovate uses the specified registry URL to check for a new version.
@@ -128,6 +144,7 @@ single_version_override(
 
 If Renovate finds an [`archive_override`](https://bazel.build/rules/lib/globals/module#archive_override) or a [`local_path_override`](https://bazel.build/rules/lib/globals/module#local_path_override), it ignores the related `bazel_dep`.
 Because these declarations lack versionable attributes, Renovate does not update them.
+When using `archive_override` and `local_path_override`, the `version` parameter on the `bazel_dep` is optional.
 
 ```python
 bazel_dep(name = "rules_foo", version = "1.2.3")
@@ -138,6 +155,15 @@ archive_override(
     "https://example.com/archive.tar.gz",
   ],
 )
+
+bazel_dep(name = "rules_bar")
+
+archive_override(
+  module_name = "rules_bar",
+  urls = [
+    "https://example.com/archive.tar.gz",
+  ],
+)
 ```
 
 #### `multiple_version_override`
diff --git a/lib/modules/manager/bazel-module/context.spec.ts b/lib/modules/manager/bazel-module/context.spec.ts
index f2dc8d7b99..a134bb69fe 100644
--- a/lib/modules/manager/bazel-module/context.spec.ts
+++ b/lib/modules/manager/bazel-module/context.spec.ts
@@ -24,6 +24,24 @@ describe('modules/manager/bazel-module/context', () => {
       ]);
     });
 
+    it('construct simple bazel_dep with no version', () => {
+      const ctx = new Ctx()
+        .startRule('bazel_dep')
+        .startAttribute('name')
+        .addString('rules_foo')
+        .endRule();
+
+      expect(ctx.results).toEqual([
+        fragments.record(
+          {
+            rule: fragments.string('bazel_dep'),
+            name: fragments.string('rules_foo'),
+          },
+          true,
+        ),
+      ]);
+    });
+
     it('construct a rule with array arg', () => {
       const ctx = new Ctx()
         .startRule('foo_library')
diff --git a/lib/modules/manager/bazel-module/extract.spec.ts b/lib/modules/manager/bazel-module/extract.spec.ts
index 53e5ca4784..58e7652cd5 100644
--- a/lib/modules/manager/bazel-module/extract.spec.ts
+++ b/lib/modules/manager/bazel-module/extract.spec.ts
@@ -66,20 +66,52 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toHaveLength(3);
-      expect(result.deps).toEqual(
-        expect.arrayContaining([
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: BazelDatasource.id,
+            depType: 'bazel_dep',
+            depName: 'rules_foo',
+            currentValue: '1.2.3',
+            skipReason: 'git-dependency',
+          },
+          {
+            depType: 'git_override',
+            depName: 'rules_foo',
+            currentDigest: '850cb49c8649e463b80ef7984e7c744279746170',
+            datasource: GithubTagsDatasource.id,
+            packageName: 'example/rules_foo',
+          },
           {
             datasource: BazelDatasource.id,
             depType: 'bazel_dep',
             depName: 'rules_bar',
             currentValue: '1.0.0',
           },
+        ],
+      });
+    });
+
+    it('returns bazel_dep with no version and git_override', async () => {
+      const input = codeBlock`
+        bazel_dep(name = "rules_foo")
+        git_override(
+            module_name = "rules_foo",
+            commit = "850cb49c8649e463b80ef7984e7c744279746170",
+            remote = "https://github.com/example/rules_foo.git",
+        )
+        `;
+      const result = await extractPackageFile(input, 'MODULE.bazel');
+      if (!result) {
+        throw new Error('Expected a result.');
+      }
+      expect(result).toEqual({
+        deps: [
           {
             datasource: BazelDatasource.id,
             depType: 'bazel_dep',
             depName: 'rules_foo',
-            currentValue: '1.2.3',
             skipReason: 'git-dependency',
           },
           {
@@ -89,8 +121,8 @@ describe('modules/manager/bazel-module/extract', () => {
             currentDigest: '850cb49c8649e463b80ef7984e7c744279746170',
             packageName: 'example/rules_foo',
           },
-        ]),
-      );
+        ],
+      });
     });
 
     it('returns dependencies and custom registry URLs when specified in a bazelrc', async () => {
@@ -130,9 +162,9 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toHaveLength(2);
-      expect(result.deps).toEqual(
-        expect.arrayContaining([
+
+      expect(result).toEqual({
+        deps: [
           {
             datasource: BazelDatasource.id,
             depType: 'bazel_dep',
@@ -145,8 +177,40 @@ describe('modules/manager/bazel-module/extract', () => {
             depName: 'rules_foo',
             skipReason: 'unsupported-datasource',
           },
-        ]),
-      );
+        ],
+      });
+    });
+
+    it('returns bazel_dep with no version and archive_override dependencies', async () => {
+      const input = codeBlock`
+        bazel_dep(name = "rules_foo")
+        archive_override(
+          module_name = "rules_foo",
+          urls = [
+            "https://example.com/archive.tar.gz",
+          ],
+        )
+      `;
+      const result = await extractPackageFile(input, 'MODULE.bazel');
+      if (!result) {
+        throw new Error('Expected a result.');
+      }
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: BazelDatasource.id,
+            depType: 'bazel_dep',
+            depName: 'rules_foo',
+            skipReason: 'file-dependency',
+          },
+          {
+            depType: 'archive_override',
+            depName: 'rules_foo',
+            skipReason: 'unsupported-datasource',
+          },
+        ],
+      });
     });
 
     it('returns bazel_dep and local_path_override dependencies', async () => {
@@ -161,9 +225,9 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toHaveLength(2);
-      expect(result.deps).toEqual(
-        expect.arrayContaining([
+
+      expect(result).toEqual({
+        deps: [
           {
             datasource: BazelDatasource.id,
             depType: 'bazel_dep',
@@ -176,8 +240,38 @@ describe('modules/manager/bazel-module/extract', () => {
             depName: 'rules_foo',
             skipReason: 'unsupported-datasource',
           },
-        ]),
-      );
+        ],
+      });
+    });
+
+    it('returns bazel_dep with no version and local_path_override dependencies', async () => {
+      const input = codeBlock`
+        bazel_dep(name = "rules_foo")
+        local_path_override(
+          module_name = "rules_foo",
+          urls = "/path/to/repo",
+        )
+      `;
+      const result = await extractPackageFile(input, 'MODULE.bazel');
+      if (!result) {
+        throw new Error('Expected a result.');
+      }
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: BazelDatasource.id,
+            depType: 'bazel_dep',
+            depName: 'rules_foo',
+            skipReason: 'local-dependency',
+          },
+          {
+            depType: 'local_path_override',
+            depName: 'rules_foo',
+            skipReason: 'unsupported-datasource',
+          },
+        ],
+      });
     });
 
     it('returns bazel_dep and single_version_override dependencies if a version is specified', async () => {
@@ -185,7 +279,7 @@ describe('modules/manager/bazel-module/extract', () => {
         bazel_dep(name = "rules_foo", version = "1.2.3")
         single_version_override(
           module_name = "rules_foo",
-          version = "1.2.3",
+          version = "1.2.5",
           registry = "https://example.com/custom_registry",
         )
       `;
@@ -193,9 +287,9 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toHaveLength(2);
-      expect(result.deps).toEqual(
-        expect.arrayContaining([
+
+      expect(result).toEqual({
+        deps: [
           {
             datasource: BazelDatasource.id,
             depType: 'bazel_dep',
@@ -207,12 +301,46 @@ describe('modules/manager/bazel-module/extract', () => {
           {
             depType: 'single_version_override',
             depName: 'rules_foo',
-            currentValue: '1.2.3',
             skipReason: 'ignored',
+            currentValue: '1.2.5',
             registryUrls: ['https://example.com/custom_registry'],
           },
-        ]),
-      );
+        ],
+      });
+    });
+
+    it('returns bazel_dep with no version and single_version_override dependencies if a version is specified', async () => {
+      const input = codeBlock`
+        bazel_dep(name = "rules_foo")
+        single_version_override(
+          module_name = "rules_foo",
+          version = "1.2.3",
+          registry = "https://example.com/custom_registry",
+        )
+      `;
+      const result = await extractPackageFile(input, 'MODULE.bazel');
+      if (!result) {
+        throw new Error('Expected a result.');
+      }
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: BazelDatasource.id,
+            depType: 'bazel_dep',
+            depName: 'rules_foo',
+            skipReason: 'is-pinned',
+            registryUrls: ['https://example.com/custom_registry'],
+          },
+          {
+            depType: 'single_version_override',
+            depName: 'rules_foo',
+            skipReason: 'ignored',
+            currentValue: '1.2.3',
+            registryUrls: ['https://example.com/custom_registry'],
+          },
+        ],
+      });
     });
 
     it('returns bazel_dep dependency if single_version_override does not have a version', async () => {
@@ -227,15 +355,40 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toEqual([
-        {
-          datasource: BazelDatasource.id,
-          depType: 'bazel_dep',
-          depName: 'rules_foo',
-          currentValue: '1.2.3',
-          registryUrls: ['https://example.com/custom_registry'],
-        },
-      ]);
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: BazelDatasource.id,
+            depType: 'bazel_dep',
+            depName: 'rules_foo',
+            currentValue: '1.2.3',
+            registryUrls: ['https://example.com/custom_registry'],
+          },
+        ],
+      });
+    });
+
+    it('returns bazel_dep with no version dependency if single_version_override does not have a version', async () => {
+      const input = codeBlock`
+        bazel_dep(name = "rules_foo")
+        single_version_override(
+          module_name = "rules_foo",
+          registry = "https://example.com/custom_registry",
+        )
+      `;
+      const result = await extractPackageFile(input, 'MODULE.bazel');
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: BazelDatasource.id,
+            depType: 'bazel_dep',
+            depName: 'rules_foo',
+            skipReason: 'unspecified-version',
+            registryUrls: ['https://example.com/custom_registry'],
+          },
+        ],
+      });
     });
 
     it('returns maven.install and maven.artifact dependencies', async () => {
@@ -263,32 +416,35 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toEqual([
-        {
-          datasource: MavenDatasource.id,
-          depType: 'maven_install',
-          depName: 'junit:junit',
-          currentValue: '4.13.2',
-          registryUrls: ['https://repo1.maven.org/maven2/'],
-          versioning: 'gradle',
-        },
-        {
-          datasource: MavenDatasource.id,
-          depType: 'maven_install',
-          depName: 'com.google.guava:guava',
-          currentValue: '31.1-jre',
-          registryUrls: ['https://repo1.maven.org/maven2/'],
-          versioning: 'gradle',
-        },
-        {
-          datasource: MavenDatasource.id,
-          depType: 'maven_install',
-          depName: 'org.clojure:core.specs.alpha',
-          currentValue: '0.2.56',
-          registryUrls: ['https://repo1.maven.org/maven2/'],
-          versioning: 'gradle',
-        },
-      ]);
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: MavenDatasource.id,
+            versioning: 'gradle',
+            depName: 'junit:junit',
+            currentValue: '4.13.2',
+            depType: 'maven_install',
+            registryUrls: ['https://repo1.maven.org/maven2/'],
+          },
+          {
+            datasource: MavenDatasource.id,
+            versioning: 'gradle',
+            depName: 'com.google.guava:guava',
+            currentValue: '31.1-jre',
+            depType: 'maven_install',
+            registryUrls: ['https://repo1.maven.org/maven2/'],
+          },
+          {
+            datasource: MavenDatasource.id,
+            versioning: 'gradle',
+            depName: 'org.clojure:core.specs.alpha',
+            currentValue: '0.2.56',
+            depType: 'maven_install',
+            registryUrls: ['https://repo1.maven.org/maven2/'],
+          },
+        ],
+      });
     });
 
     it('returns oci.pull dependencies', async () => {
@@ -306,17 +462,20 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toEqual([
-        {
-          datasource: DockerDatasource.id,
-          depType: 'oci_pull',
-          depName: 'nginx_image',
-          packageName: 'index.docker.io/library/nginx',
-          currentValue: '1.27.1',
-          currentDigest:
-            'sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720',
-        },
-      ]);
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: DockerDatasource.id,
+            depType: 'oci_pull',
+            depName: 'nginx_image',
+            packageName: 'index.docker.io/library/nginx',
+            currentValue: '1.27.1',
+            currentDigest:
+              'sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720',
+          },
+        ],
+      });
     });
 
     it('returns oci.pull dependencies without tags', async () => {
@@ -333,16 +492,19 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toEqual([
-        {
-          datasource: DockerDatasource.id,
-          depType: 'oci_pull',
-          depName: 'nginx_image',
-          packageName: 'index.docker.io/library/nginx',
-          currentDigest:
-            'sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720',
-        },
-      ]);
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: DockerDatasource.id,
+            depType: 'oci_pull',
+            depName: 'nginx_image',
+            packageName: 'index.docker.io/library/nginx',
+            currentDigest:
+              'sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720',
+          },
+        ],
+      });
     });
 
     it('returns maven.install and bazel_dep dependencies together', async () => {
@@ -367,30 +529,33 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toEqual([
-        {
-          datasource: BazelDatasource.id,
-          depType: 'bazel_dep',
-          depName: 'bazel_jar_jar',
-          currentValue: '0.1.0',
-        },
-        {
-          datasource: MavenDatasource.id,
-          depType: 'maven_install',
-          depName: 'junit:junit',
-          currentValue: '4.13.2',
-          registryUrls: ['https://repo1.maven.org/maven2/'],
-          versioning: 'gradle',
-        },
-        {
-          datasource: MavenDatasource.id,
-          depType: 'maven_install',
-          depName: 'com.google.guava:guava',
-          currentValue: '31.1-jre',
-          registryUrls: ['https://repo1.maven.org/maven2/'],
-          versioning: 'gradle',
-        },
-      ]);
+
+      expect(result).toEqual({
+        deps: [
+          {
+            datasource: BazelDatasource.id,
+            depType: 'bazel_dep',
+            depName: 'bazel_jar_jar',
+            currentValue: '0.1.0',
+          },
+          {
+            datasource: MavenDatasource.id,
+            versioning: 'gradle',
+            depName: 'junit:junit',
+            currentValue: '4.13.2',
+            depType: 'maven_install',
+            registryUrls: ['https://repo1.maven.org/maven2/'],
+          },
+          {
+            datasource: MavenDatasource.id,
+            versioning: 'gradle',
+            depName: 'com.google.guava:guava',
+            currentValue: '31.1-jre',
+            depType: 'maven_install',
+            registryUrls: ['https://repo1.maven.org/maven2/'],
+          },
+        ],
+      });
     });
 
     it('returns git_repository dependencies', async () => {
@@ -405,18 +570,18 @@ describe('modules/manager/bazel-module/extract', () => {
       if (!result) {
         throw new Error('Expected a result.');
       }
-      expect(result.deps).toHaveLength(1);
-      expect(result.deps).toEqual(
-        expect.arrayContaining([
+
+      expect(result).toEqual({
+        deps: [
           {
-            datasource: GithubTagsDatasource.id,
             depType: 'git_repository',
             depName: 'rules_foo',
             currentDigest: '850cb49c8649e463b80ef7984e7c744279746170',
+            datasource: GithubTagsDatasource.id,
             packageName: 'example/rules_foo',
           },
-        ]),
-      );
+        ],
+      });
     });
   });
 });
diff --git a/lib/modules/manager/bazel-module/rules.spec.ts b/lib/modules/manager/bazel-module/rules.spec.ts
index c3a8b2a220..cd035d3a86 100644
--- a/lib/modules/manager/bazel-module/rules.spec.ts
+++ b/lib/modules/manager/bazel-module/rules.spec.ts
@@ -25,6 +25,13 @@ const bazelDepPkgDep: BasePackageDep = {
   depName: 'rules_foo',
   currentValue: '1.2.3',
 };
+const bazelDepPkgDepNoVersion: BasePackageDep = {
+  datasource: BazelDatasource.id,
+  depType: 'bazel_dep',
+  depName: 'rules_foo',
+  currentValue: undefined,
+  skipReason: 'unspecified-version',
+};
 const gitOverrideForGithubPkgDep: OverridePackageDep = {
   datasource: GithubTagsDatasource.id,
   depType: 'git_override',
@@ -94,6 +101,10 @@ describe('modules/manager/bazel-module/rules', () => {
       name: fragments.string('rules_foo'),
       version: fragments.string('1.2.3'),
     });
+    const bazelDepWithoutDevDepNoVersion = fragments.record({
+      rule: fragments.string('bazel_dep'),
+      name: fragments.string('rules_foo'),
+    });
     const gitOverrideWithGihubHost = fragments.record({
       rule: fragments.string('git_override'),
       module_name: fragments.string('rules_foo'),
@@ -131,6 +142,7 @@ describe('modules/manager/bazel-module/rules', () => {
     it.each`
       msg                                                    | a                                    | exp
       ${'bazel_dep'}                                         | ${bazelDepWithoutDevDep}             | ${bazelDepPkgDep}
+      ${'bazel_dep, no version'}                             | ${bazelDepWithoutDevDepNoVersion}    | ${bazelDepPkgDepNoVersion}
       ${'git_override, GitHub host'}                         | ${gitOverrideWithGihubHost}          | ${gitOverrideForGithubPkgDep}
       ${'git_override, unsupported host'}                    | ${gitOverrideWithUnsupportedHost}    | ${gitOverrideForUnsupportedPkgDep}
       ${'archive_override'}                                  | ${archiveOverride}                   | ${archiveOverridePkgDep}
@@ -169,10 +181,17 @@ describe('modules/manager/bazel-module/rules', () => {
 
   describe('.toPackageDependencies()', () => {
     const expectedBazelDepNoOverrides: PackageDependency[] = [bazelDepPkgDep];
+    const expectedBazelDepNoOverridesNoVersion: PackageDependency[] = [
+      bazelDepPkgDepNoVersion,
+    ];
     const expectedBazelDepAndGitOverride: PackageDependency[] = [
       deepmerge(bazelDepPkgDep, { skipReason: 'git-dependency' }),
       bazelModulePackageDepToPackageDependency(gitOverrideForGithubPkgDep),
     ];
+    const expectedBazelDepNoVersionAndGitOverride: PackageDependency[] = [
+      deepmerge(bazelDepPkgDepNoVersion, { skipReason: 'git-dependency' }),
+      bazelModulePackageDepToPackageDependency(gitOverrideForGithubPkgDep),
+    ];
     const expectedBazelDepAndSingleVersionOverride: PackageDependency[] = [
       deepmerge(bazelDepPkgDep, {
         skipReason: 'is-pinned',
@@ -180,30 +199,56 @@ describe('modules/manager/bazel-module/rules', () => {
       }),
       bazelModulePackageDepToPackageDependency(singleVersionOverridePkgDep),
     ];
+    const expectedBazelDepNoVersionAndSingleVersionOverride: PackageDependency[] =
+      [
+        deepmerge(bazelDepPkgDepNoVersion, {
+          skipReason: 'is-pinned',
+          registryUrls: [customRegistryUrl],
+        }),
+        bazelModulePackageDepToPackageDependency(singleVersionOverridePkgDep),
+      ];
     const expectedBazelDepAndArchiveOverride: PackageDependency[] = [
       deepmerge(bazelDepPkgDep, { skipReason: 'file-dependency' }),
       bazelModulePackageDepToPackageDependency(archiveOverridePkgDep),
     ];
+    const expectedBazelDepNoVersionAndArchiveOverride: PackageDependency[] = [
+      deepmerge(bazelDepPkgDepNoVersion, { skipReason: 'file-dependency' }),
+      bazelModulePackageDepToPackageDependency(archiveOverridePkgDep),
+    ];
     const expectedBazelDepAndLocalPathOverride: PackageDependency[] = [
       deepmerge(bazelDepPkgDep, { skipReason: 'local-dependency' }),
       bazelModulePackageDepToPackageDependency(localPathOverridePkgDep),
     ];
+    const expectedBazelDepNoVersionAndLocalPathOverride: PackageDependency[] = [
+      deepmerge(bazelDepPkgDepNoVersion, { skipReason: 'local-dependency' }),
+      bazelModulePackageDepToPackageDependency(localPathOverridePkgDep),
+    ];
     // If a registry is specified and a version is not specified for a
     // single_version_override, it is merely providing a registry URL for the bazel_dep.
     const expectedBazelDepWithRegistry: PackageDependency[] = [
       deepmerge(bazelDepPkgDep, { registryUrls: [customRegistryUrl] }),
     ];
+    const expectedBazelDepNoVersionWithRegistry: PackageDependency[] = [
+      deepmerge(bazelDepPkgDepNoVersion, { registryUrls: [customRegistryUrl] }),
+    ];
 
     it.each`
-      msg                                                        | a                                                                         | exp
-      ${'bazel_dep, no overrides'}                               | ${[bazelDepPkgDep]}                                                       | ${expectedBazelDepNoOverrides}
-      ${'bazel_dep & git_override'}                              | ${[bazelDepPkgDep, gitOverrideForGithubPkgDep]}                           | ${expectedBazelDepAndGitOverride}
-      ${'git_override, no bazel_dep'}                            | ${[gitOverrideForGithubPkgDep]}                                           | ${[]}
-      ${'bazel_dep & archive_override'}                          | ${[bazelDepPkgDep, archiveOverridePkgDep]}                                | ${expectedBazelDepAndArchiveOverride}
-      ${'bazel_dep & local_path_override'}                       | ${[bazelDepPkgDep, localPathOverridePkgDep]}                              | ${expectedBazelDepAndLocalPathOverride}
-      ${'single_version_override, with version and registry'}    | ${[bazelDepPkgDep, singleVersionOverridePkgDep]}                          | ${expectedBazelDepAndSingleVersionOverride}
-      ${'single_version_override, with registry'}                | ${[bazelDepPkgDep, singleVersionOverrideWithRegistryPkgDep]}              | ${expectedBazelDepWithRegistry}
-      ${'single_version_override, without version and registry'} | ${[bazelDepPkgDep, singleVersionOverrideWithoutVersionAndRegistryPkgDep]} | ${[bazelDepPkgDep]}
+      msg                                                                                | a                                                                                  | exp
+      ${'bazel_dep, no overrides'}                                                       | ${[bazelDepPkgDep]}                                                                | ${expectedBazelDepNoOverrides}
+      ${'bazel_dep, no overrides, no version'}                                           | ${[bazelDepPkgDepNoVersion]}                                                       | ${expectedBazelDepNoOverridesNoVersion}
+      ${'bazel_dep & git_override'}                                                      | ${[bazelDepPkgDep, gitOverrideForGithubPkgDep]}                                    | ${expectedBazelDepAndGitOverride}
+      ${'bazel_dep, no version & git_override'}                                          | ${[bazelDepPkgDepNoVersion, gitOverrideForGithubPkgDep]}                           | ${expectedBazelDepNoVersionAndGitOverride}
+      ${'git_override, no bazel_dep'}                                                    | ${[gitOverrideForGithubPkgDep]}                                                    | ${[]}
+      ${'bazel_dep & archive_override'}                                                  | ${[bazelDepPkgDep, archiveOverridePkgDep]}                                         | ${expectedBazelDepAndArchiveOverride}
+      ${'bazel_dep, no version & archive_override'}                                      | ${[bazelDepPkgDepNoVersion, archiveOverridePkgDep]}                                | ${expectedBazelDepNoVersionAndArchiveOverride}
+      ${'bazel_dep & local_path_override'}                                               | ${[bazelDepPkgDep, localPathOverridePkgDep]}                                       | ${expectedBazelDepAndLocalPathOverride}
+      ${'bazel_dep, no version & local_path_override'}                                   | ${[bazelDepPkgDepNoVersion, localPathOverridePkgDep]}                              | ${expectedBazelDepNoVersionAndLocalPathOverride}
+      ${'single_version_override, with version and registry'}                            | ${[bazelDepPkgDep, singleVersionOverridePkgDep]}                                   | ${expectedBazelDepAndSingleVersionOverride}
+      ${'bazel_dep, no version & single_version_override, with version and registry'}    | ${[bazelDepPkgDepNoVersion, singleVersionOverridePkgDep]}                          | ${expectedBazelDepNoVersionAndSingleVersionOverride}
+      ${'single_version_override, with registry'}                                        | ${[bazelDepPkgDep, singleVersionOverrideWithRegistryPkgDep]}                       | ${expectedBazelDepWithRegistry}
+      ${'bazel_dep, no version & single_version_override, with registry'}                | ${[bazelDepPkgDepNoVersion, singleVersionOverrideWithRegistryPkgDep]}              | ${expectedBazelDepNoVersionWithRegistry}
+      ${'single_version_override, without version and registry'}                         | ${[bazelDepPkgDep, singleVersionOverrideWithoutVersionAndRegistryPkgDep]}          | ${[bazelDepPkgDep]}
+      ${'bazel_dep, no version & single_version_override, without version and registry'} | ${[bazelDepPkgDepNoVersion, singleVersionOverrideWithoutVersionAndRegistryPkgDep]} | ${[bazelDepPkgDepNoVersion]}
     `('with $msg', ({ msg, a, exp }) => {
       const result = toPackageDependencies(a);
       expect(result).toEqual(exp);
diff --git a/lib/modules/manager/bazel-module/rules.ts b/lib/modules/manager/bazel-module/rules.ts
index 8f1bb2e626..ef0fe52fc6 100644
--- a/lib/modules/manager/bazel-module/rules.ts
+++ b/lib/modules/manager/bazel-module/rules.ts
@@ -69,14 +69,15 @@ const BazelDepToPackageDep = RecordFragmentSchema.extend({
       value: z.literal('bazel_dep'),
     }),
     name: StringFragmentSchema,
-    version: StringFragmentSchema,
+    version: StringFragmentSchema.optional(),
   }),
 }).transform(
   ({ children: { rule, name, version } }): BasePackageDep => ({
     datasource: BazelDatasource.id,
     depType: rule.value,
     depName: name.value,
-    currentValue: version.value,
+    currentValue: version?.value,
+    ...(version ? {} : { skipReason: 'unspecified-version' }),
   }),
 );
 
-- 
GitLab