maintainers/scripts/update.nix: Fix reverse toposort with independent packages

When updating GNOME packages in reverse topological order using the following command:

    nix-shell maintainers/scripts/update.nix --arg predicate '(path: pkg: builtins.any (team: team.shortName == "GNOME") pkg.meta.teams or [])' --argstr order reverse-topological

the `update.py` script would crash with `ValueError` on calling `ordered.index("adwaita-fonts")`.

This happened because the `adwaita-fonts` does not depend on any other GNOME package, nor it is depended upon by one.
As a result, it had been discarded by the `reverse_edges` function.

Co-authored-by: Philip Taron <philip.taron@gmail.com>
This commit is contained in:
Jan Tojnar 2025-05-01 17:26:02 +02:00
parent 4b62180ead
commit 2e0c34649b

View file

@ -1,6 +1,6 @@
from graphlib import TopologicalSorter from graphlib import TopologicalSorter
from pathlib import Path from pathlib import Path
from typing import Any, Generator, Literal from typing import Any, Final, Generator, Literal
import argparse import argparse
import asyncio import asyncio
import contextlib import contextlib
@ -15,6 +15,11 @@ import tempfile
Order = Literal["arbitrary", "reverse-topological", "topological"] Order = Literal["arbitrary", "reverse-topological", "topological"]
FAKE_DEPENDENCY_FOR_INDEPENDENT_PACKAGES: Final[str] = (
"::fake_dependency_for_independent_packages"
)
class CalledProcessError(Exception): class CalledProcessError(Exception):
process: asyncio.subprocess.Process process: asyncio.subprocess.Process
stderr: bytes | None stderr: bytes | None
@ -116,10 +121,14 @@ def requisites_to_attrs(
def reverse_edges(graph: dict[str, set[str]]) -> dict[str, set[str]]: def reverse_edges(graph: dict[str, set[str]]) -> dict[str, set[str]]:
""" """
Flips the edges of a directed graph. Flips the edges of a directed graph.
Packages without any dependency relation in the updated set
will be added to `FAKE_DEPENDENCY_FOR_INDEPENDENT_PACKAGES` node.
""" """
reversed_graph: dict[str, set[str]] = {} reversed_graph: dict[str, set[str]] = {}
for dependent, dependencies in graph.items(): for dependent, dependencies in graph.items():
dependencies = dependencies or {FAKE_DEPENDENCY_FOR_INDEPENDENT_PACKAGES}
for dependency in dependencies: for dependency in dependencies:
reversed_graph.setdefault(dependency, set()).add(dependent) reversed_graph.setdefault(dependency, set()).add(dependent)
@ -413,6 +422,8 @@ async def populate_queue(
ready_packages = list(sorter.get_ready()) ready_packages = list(sorter.get_ready())
eprint(f"Enqueuing group of {len(ready_packages)} packages") eprint(f"Enqueuing group of {len(ready_packages)} packages")
for package in ready_packages: for package in ready_packages:
if package == FAKE_DEPENDENCY_FOR_INDEPENDENT_PACKAGES:
continue
await packages_to_update.put(attr_packages[package]) await packages_to_update.put(attr_packages[package])
await packages_to_update.join() await packages_to_update.join()
sorter.done(*ready_packages) sorter.done(*ready_packages)