Source code for meta_package_manager.managers.volta

# 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, WINDOWS

from ..manager import PackageManager

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

    from ..package import Package


[docs] class Volta(PackageManager): """Volta manages JavaScript command-line tools, installing each package from the npm registry into its own isolated image pinned to a specific Node runtime. Tools installed with `volta install` never land in npm's global prefix: Volta keeps them under its own `VOLTA_HOME` layout and exposes their binaries through shims on `PATH`. The `npm` executable found on a Volta-equipped host is itself such a shim, and `npm --global` operations do not see Volta-managed tools, hence this dedicated backend (requested in [#1995](https://github.com/kdeldycke/meta-package-manager/issues/1995)). Volta has no command listing outdated packages, so `outdated` (and with it the synthesized `upgrade --all`) is unsupported: upgrades are targeted, by reinstalling the latest release of a named package. """ unmaintained = True unmaintained_message = ( "Volta's maintainers [declared the project unmaintained on 2025-11-14]" "(https://github.com/volta-cli/volta/issues/2080) and recommend migrating " "to [`mise`](https://mise.jdx.dev). The final release, [`2.0.2`]" "(https://github.com/volta-cli/volta/releases/tag/v2.0.2), dates back to " "2024-12-05." ) homepage_url = "https://volta.sh" platforms = LINUX_LIKE, MACOS, WINDOWS requirement = ">=1.0.2" """[`1.0.2`](https://github.com/volta-cli/volta/blob/main/RELEASES.md#version-102) ships the fixes making `volta list` report correct information ([volta-cli/volta#778](https://github.com/volta-cli/volta/issues/778) and [volta-cli/volta#926](https://github.com/volta-cli/volta/issues/926)), the listing this backend parses. The plain output format itself is stable from `1.0` through the final `2.0.2` release. """ cli_search_path = ("~/.volta/bin",) """`VOLTA_HOME/bin`, where the official install script places the `volta` binary on Linux and macOS. Windows installers register the binary on `PATH` themselves. """ _LIST_REGEXP = re.compile( r"^package (?P<package_id>\S+)@(?P<installed_version>\S+) /.* \(default\)$", ) """Retains only the `package <name>@<version> / <binaries> / <platform>` lines flagged `(default)`, skipping the `runtime` and `package-manager` lines. The greedy `package_id` group backtracks to the last `@` of the spec, so scoped names (`@scope/name@1.0.0`) split correctly. """ @property def installed(self) -> Iterator[Package]: """Fetch installed packages. `volta list all --format plain` prints one line per tool. Only `package` lines carrying the `(default)` marker are retained: they are the globally-installed npm packages Volta manages through `volta install` and `volta uninstall`. `runtime` (Node) and `package-manager` (npm, pnpm, Yarn) lines are pinned toolchain components Volta cannot uninstall, so they are kept out of the inventory. So is a package shadowed by the working directory's own JavaScript project: it renders with `project` placeholders instead of versions, so run `mpm` outside a project tree for the full global inventory. The sample below is derived from the format's [unit tests in Volta's `plain.rs`](https://github.com/volta-cli/volta/blob/v2.0.2/src/command/list/plain.rs) and the trace of [#1995](https://github.com/kdeldycke/meta-package-manager/issues/1995): ```{code-block} shell-session $ volta list all --format plain runtime node@12.4.0 (default) package-manager npm@6.13.4 (default) package-manager yarn@1.16.0 (default) package @larksuite/cli@1.0.79 / lark-cli / node@12.4.0 npm@built-in (default) package ember-cli@3.10.0 / ember / node@12.4.0 npm@built-in (default) package typescript@3.4.1 / tsc, tsserver / node@12.4.0 npm@built-in (default) ``` """ output = self.run_cli("list", "all", "--format", "plain", must_succeed=True) yield from self.parse_regex_lines(self._LIST_REGEXP, output)
[docs] def install(self, package_id: str, version: str | None = None) -> str: """Install one package. Volta pins exact versions natively through npm's `name@version` spec syntax; a bare name resolves to the latest release. ```{code-block} shell-session $ volta install typescript@3.4.1 ``` """ spec = f"{package_id}@{version}" if version else package_id return self.run_cli("install", spec)
[docs] def upgrade_one_cli( self, package_id: str, version: str | None = None, ) -> tuple[str, ...]: """Generates the CLI to upgrade the package provided as parameter. Volta has no dedicated upgrade command for packages: reinstalling a bare name fetches the latest release and swaps it in as the new default. ```{code-block} shell-session $ volta install ember-cli ``` """ spec = f"{package_id}@{version}" if version else package_id return self.build_cli("install", spec)
[docs] def remove(self, package_id: str) -> str: """Remove one package. ```{code-block} shell-session $ volta uninstall ember-cli ``` """ return self.run_cli("uninstall", package_id)