From d72c4f89cac3d8a09138adf217ab94ce22ef2a54 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 26 May 2026 11:13:55 -0500 Subject: [PATCH 1/7] nixpkgs-plugin-update: allow semver build tags --- .../nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py index ac1944ef35e1..d56433eba1a7 100644 --- a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py +++ b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py @@ -40,7 +40,7 @@ AUTO_BRANCH = "" VERSION_DATE_PATTERN = re.compile(r"(\d{4}-\d{2}-\d{2})$") VERSION_TAG_PATTERN = re.compile(r"^(.+?)-unstable-") NON_RELEASE_TAG_PREFIXES = ("pre-",) -RELEASE_VERSION_PATTERN = re.compile(r"^[^\d]*(\d[\w.@-]*)$") +RELEASE_VERSION_PATTERN = re.compile(r"^[^\d]*(\d[\w.@+-]*)$") LOG_LEVELS = { logging.getLevelName(level): level From fed14bd766dfdf22c3886fd127d4591772643df4 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 26 May 2026 11:14:00 -0500 Subject: [PATCH 2/7] nixpkgs-plugin-update: skip prerelease tags --- .../src/nixpkgs_plugin_update/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py index d56433eba1a7..8b7ce537933a 100644 --- a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py +++ b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py @@ -127,8 +127,18 @@ def select_latest_tag( def first_release_tag(tags: list[str]) -> str | None: for tag in tags: - if normalize_release_version(tag) is not None: - return tag + normalized_tag = normalize_release_version(tag) + if normalized_tag is None: + continue + + try: + version = parse_version(normalized_tag) + if version.is_prerelease or version.is_devrelease: + continue + except InvalidVersion: + pass + + return tag return None From e0e838b060bd3ea93ab776d4d9289ea7e474c2d4 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 26 May 2026 11:14:09 -0500 Subject: [PATCH 3/7] nixpkgs-plugin-update: fall back on empty refs Treat empty GitHub GraphQL tag refs as inconclusive. Use the existing tag feed/git fallback path instead of treating an empty API result as authoritative no-tags state. --- .../nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py index 8b7ce537933a..2c6225ede083 100644 --- a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py +++ b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py @@ -429,7 +429,7 @@ class RepoGitHub(Repo): recent_tags = [node["name"] for node in repo["refs"]["nodes"]] if not recent_tags: - return None + return self._get_latest_tag_from_fallbacks() latest_tag = first_release_tag(recent_tags) return latest_tag if latest_tag is not None else recent_tags[0] From 2cb4e23a3d479baee1318f39cd49412aea74b6a4 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 26 May 2026 11:14:16 -0500 Subject: [PATCH 4/7] nixpkgs-plugin-update: keep newer tag base When current unstable plugin is newer than latest release tag, keep current last_tag as generated unstable version base. --- .../src/nixpkgs_plugin_update/__init__.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py index 2c6225ede083..a42ba867ee2c 100644 --- a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py +++ b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py @@ -644,6 +644,29 @@ def make_unstable_version(date: datetime, last_tag: str | None) -> str: return f"{tag_part}-unstable-{date_str}" +def newer_version_tag(first_tag: str | None, second_tag: str | None) -> str | None: + if first_tag is None: + return second_tag + if second_tag is None: + return first_tag + + first_version = normalize_release_version(first_tag) + second_version = normalize_release_version(second_tag) + if first_version is None: + return second_tag + if second_version is None: + return first_tag + + try: + return ( + first_tag + if parse_version(first_version) >= parse_version(second_version) + else second_tag + ) + except InvalidVersion: + return first_tag if first_version >= second_version else second_tag + + def get_commit_target( repo: Repo, branch: str, @@ -683,6 +706,7 @@ def select_plugin_target( and current_plugin.tag is None and current_plugin.date.date() > release_date.date() ): + latest_tag = newer_version_tag(current_plugin.last_tag, latest_tag) return get_commit_target(plugin_desc.repo, "HEAD", latest_tag) return release_commit, release_date, release_version, latest_tag From 97c5fd50e9d4e17d0c4e808d3426d78abb3c1f3f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 26 May 2026 11:14:32 -0500 Subject: [PATCH 5/7] nixpkgs-plugin-update: avoid duplicate submodule checks --- .../src/nixpkgs_plugin_update/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py index a42ba867ee2c..6afe8aed52e3 100644 --- a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py +++ b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py @@ -237,7 +237,7 @@ class Repo: loaded = json.loads(data) return loaded - def prefetch(self, ref: str) -> str: + def prefetch(self, ref: str, has_submodules: bool | None = None) -> str: log.info("Prefetching %s", self.uri) loaded = self._prefetch(ref) return loaded["sha256"] @@ -459,8 +459,11 @@ class RepoGitHub(Repo): new_repo = RepoGitHub(owner=new_owner, repo=new_name, branch=self._branch) self.redirect = new_repo - def prefetch(self, commit: str) -> str: - if self.has_submodules(): + def prefetch(self, commit: str, has_submodules: bool | None = None) -> str: + if has_submodules is None: + has_submodules = self.has_submodules() + + if has_submodules: sha256 = super().prefetch(commit) else: sha256 = self.prefetch_github(commit) @@ -1208,9 +1211,9 @@ def prefetch_plugin( has_submodules = p.repo.has_submodules() log.debug(f"prefetch {p.name}") sha256 = ( - p.repo.prefetch(f"{GIT_TAGS_PREFIX}{source_tag}") + p.repo.prefetch(f"{GIT_TAGS_PREFIX}{source_tag}", has_submodules=has_submodules) if source_tag - else p.repo.prefetch(commit) + else p.repo.prefetch(commit, has_submodules=has_submodules) ) license_spdx_id = ( current_plugin.license From 2d413a5e38c08b59e031e3cffe59ee463b63ac32 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 26 May 2026 11:14:38 -0500 Subject: [PATCH 6/7] nixpkgs-plugin-update: preserve slash tags --- .../src/nixpkgs_plugin_update/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py index 6afe8aed52e3..4b205624417d 100644 --- a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py +++ b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py @@ -25,7 +25,7 @@ from multiprocessing.dummy import Pool from pathlib import Path from tempfile import NamedTemporaryFile from typing import Any, Callable -from urllib.parse import urljoin, urlparse, urlsplit +from urllib.parse import unquote, urljoin, urlparse, urlsplit import git from packaging.version import InvalidVersion, parse as parse_version @@ -143,6 +143,16 @@ def first_release_tag(tags: list[str]) -> str | None: return None +def tag_from_github_atom_href(href: str) -> str | None: + path = urlparse(href).path + marker = "/releases/tag/" + if marker in path: + return unquote(path.split(marker, 1)[1]) + + tag_name = Path(path).name + return unquote(tag_name) if tag_name else None + + class Repo: def __init__(self, uri: str, branch: str) -> None: self.uri = uri @@ -359,7 +369,7 @@ class RepoGitHub(Repo): if not href: continue - tag_name = Path(urlparse(href).path).name + tag_name = tag_from_github_atom_href(href) if tag_name: tags.append(tag_name) From 9d5face41c3611acfea5694ff9af15dc6cef8ec5 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Tue, 26 May 2026 11:14:46 -0500 Subject: [PATCH 7/7] nixpkgs-plugin-update: keep current plugins on fetch failure Warn and reuse existing plugin data when fetch fails, so one transient failure does not drop entries from generated output. --- .../src/nixpkgs_plugin_update/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py index 4b205624417d..612fa0823bdf 100644 --- a/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py +++ b/pkgs/development/python-modules/nixpkgs-plugin-update/nixpkgs-plugin-update/src/nixpkgs_plugin_update/__init__.py @@ -974,9 +974,7 @@ class Editor: cache.store() print(f"{len(results)} of {len(current_plugins)} were checked") - # Do only partial update of out file - if len(results) != len(current_plugins): - results = self.merge_results(current_plugins, results) + results = self.merge_results(current_plugins, results) plugins, redirects = check_results(results) # Track version changes for commit message generation @@ -1016,9 +1014,11 @@ class Editor: if isinstance(plugin, Plugin) and hasattr(plugin, "normalized_name"): result[plugin.normalized_name] = (plugin_desc, plugin, redirect) elif isinstance(plugin, Exception): - # For exceptions, we can't determine the normalized_name - # Just log the error and continue - log.error(f"Error fetching plugin {plugin_desc.name}: {plugin!r}") + log.warning( + "Keeping current plugin data after error fetching %s: %r", + plugin_desc.name, + plugin, + ) else: # For unexpected types, log the issue log.error(