Files
pkgmgr/src/pkgmgr/cli/commands/version.py
T
Kevin Veen-Birkenbach f4339a746a
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-env-virtual (push) Has been cancelled
Mark stable commit / test-env-nix (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / lint-shell (push) Has been cancelled
Mark stable commit / lint-python (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
executet 'ruff format --check .'
2025-12-18 14:04:44 +01:00

216 lines
7.1 KiB
Python

from __future__ import annotations
import os
import sys
from typing import Any, Dict, List, Optional, Tuple
from pkgmgr.cli.context import CLIContext
from pkgmgr.core.repository.dir import get_repo_dir
from pkgmgr.core.repository.identifier import get_repo_identifier
from pkgmgr.core.git.queries import get_tags
from pkgmgr.core.version.semver import SemVer, find_latest_version
from pkgmgr.core.version.installed import (
get_installed_python_version,
get_installed_nix_profile_version,
)
from pkgmgr.core.version.source import (
read_pyproject_version,
read_pyproject_project_name,
read_flake_version,
read_pkgbuild_version,
read_debian_changelog_version,
read_spec_version,
read_ansible_galaxy_version,
)
Repository = Dict[str, Any]
def _print_pkgmgr_self_version() -> None:
"""
Print version information for pkgmgr itself (installed env + nix profile),
used when no repository is selected (e.g. user is not inside a repo).
"""
print("pkgmgr version info")
print("====================")
print("\nRepository: <pkgmgr self>")
print("----------------------------------------")
# Common distribution/module naming variants.
python_candidates = [
"package-manager", # PyPI dist name in your project
"package_manager", # module-ish variant
"pkgmgr", # console/alias-ish
]
nix_candidates = [
"pkgmgr",
"package-manager",
]
installed_python = get_installed_python_version(*python_candidates)
installed_nix = get_installed_nix_profile_version(*nix_candidates)
if installed_python:
print(
f"Installed (Python env): {installed_python.version} "
f"(dist: {installed_python.name})"
)
else:
print("Installed (Python env): <not installed>")
if installed_nix:
print(
f"Installed (Nix profile): {installed_nix.version} "
f"(match: {installed_nix.name})"
)
else:
print("Installed (Nix profile): <not installed>")
# Helpful context for debugging "why do versions differ?"
print(f"Python executable: {sys.executable}")
print(f"Python prefix: {sys.prefix}")
def handle_version(
args,
ctx: CLIContext,
selected: List[Repository],
) -> None:
"""
Handle the 'version' command.
Shows version information from:
- Git tags
- packaging metadata
- installed Python environment
- installed Nix profile
Special case:
- If no repositories are selected (e.g. not in a repo and no identifiers),
print pkgmgr's own installed versions instead of exiting with an error.
"""
if not selected:
_print_pkgmgr_self_version()
return
print("pkgmgr version info")
print("====================")
for repo in selected:
identifier = get_repo_identifier(repo, ctx.all_repositories)
python_candidates: list[str] = []
nix_candidates: list[str] = [identifier]
for key in ("pypi", "pip", "python_package", "distribution", "package"):
val = repo.get(key)
if isinstance(val, str) and val.strip():
python_candidates.append(val.strip())
python_candidates.append(identifier)
installed_python = get_installed_python_version(*python_candidates)
installed_nix = get_installed_nix_profile_version(*nix_candidates)
repo_dir = repo.get("directory")
if not repo_dir:
try:
repo_dir = get_repo_dir(ctx.repositories_base_dir, repo)
except Exception:
repo_dir = None
if not repo_dir or not os.path.isdir(repo_dir):
print(f"\nRepository: {identifier}")
print("----------------------------------------")
print(
"[INFO] Skipped: repository directory does not exist locally, "
"version detection is not possible."
)
if installed_python:
print(
f"Installed (Python env): {installed_python.version} "
f"(dist: {installed_python.name})"
)
else:
print("Installed (Python env): <not installed>")
if installed_nix:
print(
f"Installed (Nix profile): {installed_nix.version} "
f"(match: {installed_nix.name})"
)
else:
print("Installed (Nix profile): <not installed>")
continue
print(f"\nRepository: {repo_dir}")
print("----------------------------------------")
try:
tags = get_tags(cwd=repo_dir)
except Exception as exc:
print(f"[ERROR] Could not read git tags: {exc}")
tags = []
latest_tag_info: Optional[Tuple[str, SemVer]] = (
find_latest_version(tags) if tags else None
)
if latest_tag_info:
tag, ver = latest_tag_info
print(f"Git (latest SemVer tag): {tag} (parsed: {ver})")
else:
print("Git (latest SemVer tag): <none found>")
pyproject_version = read_pyproject_version(repo_dir)
pyproject_name = read_pyproject_project_name(repo_dir)
flake_version = read_flake_version(repo_dir)
pkgbuild_version = read_pkgbuild_version(repo_dir)
debian_version = read_debian_changelog_version(repo_dir)
spec_version = read_spec_version(repo_dir)
ansible_version = read_ansible_galaxy_version(repo_dir)
if pyproject_name:
installed_python = get_installed_python_version(
pyproject_name, *python_candidates
)
if installed_python:
print(
f"Installed (Python env): {installed_python.version} "
f"(dist: {installed_python.name})"
)
else:
print("Installed (Python env): <not installed>")
if installed_nix:
print(
f"Installed (Nix profile): {installed_nix.version} "
f"(match: {installed_nix.name})"
)
else:
print("Installed (Nix profile): <not installed>")
print(f"pyproject.toml: {pyproject_version or '<not found>'}")
print(f"flake.nix: {flake_version or '<not found>'}")
print(f"PKGBUILD: {pkgbuild_version or '<not found>'}")
print(f"debian/changelog: {debian_version or '<not found>'}")
print(f"package-manager.spec: {spec_version or '<not found>'}")
print(f"Ansible Galaxy meta: {ansible_version or '<not found>'}")
if latest_tag_info and pyproject_version:
try:
file_ver = SemVer.parse(pyproject_version)
if file_ver != latest_tag_info[1]:
print(
f"[WARN] Version mismatch: "
f"Git={latest_tag_info[1]}, pyproject={file_ver}"
)
except ValueError:
print(
f"[WARN] pyproject version {pyproject_version!r} "
f"is not valid SemVer."
)