Source code for meta_package_manager.managers.nix

# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

from __future__ import annotations

import re

from extra_platforms import LINUX_LIKE, MACOS

from ..capabilities import search_capabilities, version_not_implemented
from ..manager import PackageManager

TYPE_CHECKING = False
if TYPE_CHECKING:
    from collections.abc import Iterator

    from ..package import Package


[docs] class Nix(PackageManager): """Nix, the functional package manager. .. note:: All operations use the imperative ``nix-env`` interface, which manages a per-user package profile. Declarative approaches (NixOS modules, home-manager) are not covered. Channel refresh (``sync``) and store garbage collection (``cleanup``) shell out to the sibling ``nix-channel`` and ``nix-collect-garbage`` binaries installed alongside ``nix-env``, not to ``nix-env`` itself. """ homepage_url = "https://nixos.org" platforms = LINUX_LIKE, MACOS requirement = ">=2.0.0" cli_names = ("nix-env",) version_regexes = (r"nix-env \(Nix\) (?P<version>\S+)",) """ .. code-block:: shell-session $ nix-env --version nix-env (Nix) 2.18.1 """ _OUTDATED_REGEXP = re.compile( r"^(?P<name_version>\S+)\s+<\s+(?P<latest_version>\S+)", re.MULTILINE, ) @property def installed(self) -> Iterator[Package]: """Fetch installed packages. .. code-block:: shell-session $ nix-env --query --installed hello-2.12.1 nix-2.18.1 python3-3.11.6 """ output = self.run_cli("--query", "--installed") for line in output.splitlines(): if split := self.split_name_version(line.strip()): package_id, version = split yield self.package(id=package_id, installed_version=version) @property def outdated(self) -> Iterator[Package]: """Fetch outdated packages. .. code-block:: shell-session $ nix-env --query --upgradeable --compare-versions hello-2.12.1 < 2.13.0 python3-3.11.6 < 3.12.0 """ output = self.run_cli( "--query", "--upgradeable", "--compare-versions", ) for match in self._OUTDATED_REGEXP.finditer(output): if split := self.split_name_version(match.group("name_version")): package_id, version = split yield self.package( id=package_id, installed_version=version, latest_version=match.group("latest_version"), )
[docs] @search_capabilities(extended_support=False, exact_support=False) def search(self, query: str, extended: bool, exact: bool) -> Iterator[Package]: """Fetch matching packages. .. caution:: Search does not support extended or exact matching. So we return the best subset of results and let :py:meth:`meta_package_manager.manager.PackageManager.refiltered_search` refine them. .. caution:: ``nix-env --query --available`` evaluates the full nixpkgs set and can be slow on first invocation. .. code-block:: shell-session $ nix-env --query --available --attr-path --description 'hello' nixpkgs.hello hello-2.12.1 A program that produces a friendly greeting """ output = self.run_cli( "--query", "--available", "--attr-path", "--description", query, ) for line in output.splitlines(): # Columns are padded with variable whitespace (2+ spaces). parts = re.split(r"\s{2,}", line.strip(), maxsplit=2) if len(parts) < 2: continue name_version = parts[1] description = parts[2] if len(parts) > 2 else None if split := self.split_name_version(name_version): package_id, version = split yield self.package( id=package_id, description=description, latest_version=version, )
[docs] @version_not_implemented def install(self, package_id: str, version: str | None = None) -> str: """Install one package. .. code-block:: shell-session $ nix-env --install hello """ return self.run_cli("--install", package_id)
[docs] def upgrade_all_cli(self) -> tuple[str, ...]: """Generates the CLI to upgrade all packages. .. code-block:: shell-session $ nix-env --upgrade """ return self.build_cli("--upgrade")
[docs] @version_not_implemented def upgrade_one_cli( self, package_id: str, version: str | None = None, ) -> tuple[str, ...]: """Generates the CLI to upgrade one package. .. code-block:: shell-session $ nix-env --upgrade hello """ return self.build_cli("--upgrade", package_id)
[docs] def remove(self, package_id: str) -> str: """Remove one package. .. code-block:: shell-session $ nix-env --uninstall hello """ return self.run_cli("--uninstall", package_id)
[docs] def sync(self) -> None: """Update Nix channel metadata. .. code-block:: shell-session $ nix-channel --update """ self.run_cli( "--update", override_cli_path=self.sibling_cli("nix-channel", same_dir=True), )
[docs] def cleanup(self) -> None: """Remove old generations and garbage-collect the Nix store. .. code-block:: shell-session $ nix-collect-garbage --delete-old """ self.run_cli( "--delete-old", override_cli_path=self.sibling_cli("nix-collect-garbage", same_dir=True), )