Explorar el Código

fix(core): Handle empty release list in check_for_new_releases

Guard max(releases) against an empty iterable to prevent a ValueError
when the release-check endpoint returns only prereleases, dev releases,
or entries lacking a tag_name.

Fixes #22202
Martin Hauser hace 5 días
padre
commit
bcb9a83c46
Se han modificado 2 ficheros con 24 adiciones y 1 borrados
  1. 4 1
      netbox/core/jobs.py
  2. 20 0
      netbox/core/tests/test_jobs.py

+ 4 - 1
netbox/core/jobs.py

@@ -222,8 +222,11 @@ class SystemHousekeepingJob(JobRunner):
             if 'tag_name' not in release or release.get('devrelease') or release.get('prerelease'):
                 continue
             releases.append((version.parse(release['tag_name']), release.get('html_url')))
-        latest_release = max(releases)
         self.logger.debug(f"Found {len(response.json())} releases; {len(releases)} usable")
+        if not releases:
+            self.logger.info("No usable releases found; skipping")
+            return
+        latest_release = max(releases)
         self.logger.info(f"Latest release: {latest_release[0]}")
 
         # Cache the most recent release

+ 20 - 0
netbox/core/tests/test_jobs.py

@@ -298,6 +298,26 @@ class CheckForNewReleasesTestCase(HousekeepingRunnerMixin, TestCase):
 
         cache.set.assert_not_called()
 
+    @override_settings(ISOLATED_DEPLOYMENT=False, RELEASE_CHECK_URL='https://api.example/')
+    def test_check_for_new_releases_handles_no_stable_releases(self):
+        # All entries are filtered out (prereleases, devreleases, or missing tag_name);
+        # check_for_new_releases() must not crash on the resulting empty list.
+        response = MagicMock()
+        response.json.return_value = [
+            {'tag_name': 'v4.7.0-rc1', 'html_url': 'https://example/rc', 'prerelease': True},
+            {'tag_name': 'v4.7.0-dev', 'html_url': 'https://example/dev', 'devrelease': True},
+            {'html_url': 'https://example/no-tag'},
+        ]
+
+        with (
+            patch('core.jobs.requests.get', return_value=response),
+            patch('core.jobs.cache.set') as cache_set,
+            patch('core.jobs.resolve_proxies', return_value={}),
+        ):
+            self._runner().check_for_new_releases()
+
+        cache_set.assert_not_called()
+
     @override_settings(ISOLATED_DEPLOYMENT=False, RELEASE_CHECK_URL='https://api.example/')
     def test_check_for_new_releases_caches_latest_stable_release(self):
         response = MagicMock()