Files
pkgmgr/pkgmgr/core/repository/verify.py
Kevin Veen-Birkenbach d50891dfe5 Refactor: Restructure pkgmgr into actions/, core/, and cli/ (full module breakup)
This commit introduces a large-scale structural refactor of the pkgmgr
codebase. All functionality has been moved from the previous flat
top-level layout into three clearly separated namespaces:

  • pkgmgr.actions      – high-level operations invoked by the CLI
  • pkgmgr.core         – pure logic, helpers, repository utilities,
                          versioning, git helpers, config IO, and
                          command resolution
  • pkgmgr.cli          – parser, dispatch, context, and command
                          handlers

Key improvements:
  - Moved all “branch”, “release”, “changelog”, repo-management
    actions, installer pipelines, and proxy execution logic into
    pkgmgr.actions.<domain>.
  - Reworked installer structure under
        pkgmgr.actions.repository.install.installers
    including OS-package installers, Nix, Python, and Makefile.
  - Consolidated all low-level functionality under pkgmgr.core:
        • git helpers → core/git
        • config load/save → core/config
        • repository helpers → core/repository
        • versioning & semver → core/version
        • command helpers (alias, resolve, run, ink) → core/command
  - Replaced pkgmgr.cli_core with pkgmgr.cli and updated all imports.
  - Added minimal __init__.py files for clean package exposure.
  - Updated all E2E, integration, and unit tests with new module paths.
  - Fixed patch targets so mocks point to the new structure.
  - Ensured backward compatibility at the CLI boundary (pkgmgr entry point unchanged).

This refactor produces a cleaner, layered architecture:
  - `core` = logic
  - `actions` = orchestrated behaviour
  - `cli` = user interface

Reference: ChatGPT-assisted refactor discussion
https://chatgpt.com/share/6938221c-e24c-800f-8317-7732cedf39b9
2025-12-09 14:20:19 +01:00

108 lines
4.6 KiB
Python

import subprocess
def verify_repository(repo, repo_dir, mode="local", no_verification=False):
"""
Verifies the repository based on its 'verified' field.
The 'verified' field can be a dictionary with the following keys:
commit: The expected commit hash.
gpg_keys: A list of valid GPG key IDs (at least one must match the signing key).
If mode == "pull", the remote HEAD commit is checked via "git ls-remote origin HEAD".
Otherwise (mode "local", used for install and clone), the local HEAD commit is checked via "git rev-parse HEAD".
Returns a tuple:
(verified_ok, error_details, commit_hash, signing_key)
- verified_ok: True if the verification passed (or no verification info is set), False otherwise.
- error_details: A list of error messages for any failed checks.
- commit_hash: The obtained commit hash.
- signing_key: The GPG key ID that signed the latest commit (obtained via "git log -1 --format=%GK").
"""
verified_info = repo.get("verified")
if not verified_info:
# Nothing to verify.
commit_hash = ""
signing_key = ""
if mode == "pull":
try:
result = subprocess.run("git ls-remote origin HEAD", cwd=repo_dir, shell=True, check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
commit_hash = result.stdout.split()[0].strip()
except Exception:
commit_hash = ""
else:
try:
result = subprocess.run("git rev-parse HEAD", cwd=repo_dir, shell=True, check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
commit_hash = result.stdout.strip()
except Exception:
commit_hash = ""
try:
result = subprocess.run(["git", "log", "-1", "--format=%GK"], cwd=repo_dir, shell=False, check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
signing_key = result.stdout.strip()
except Exception:
signing_key = ""
return True, [], commit_hash, signing_key
expected_commit = None
expected_gpg_keys = None
if isinstance(verified_info, dict):
expected_commit = verified_info.get("commit")
expected_gpg_keys = verified_info.get("gpg_keys")
else:
# If verified is a plain string, treat it as the expected commit.
expected_commit = verified_info
error_details = []
# Get commit hash according to the mode.
commit_hash = ""
if mode == "pull":
try:
result = subprocess.run("git ls-remote origin HEAD", cwd=repo_dir, shell=True, check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
commit_hash = result.stdout.split()[0].strip()
except Exception as e:
error_details.append(f"Error retrieving remote commit: {e}")
else:
try:
result = subprocess.run("git rev-parse HEAD", cwd=repo_dir, shell=True, check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
commit_hash = result.stdout.strip()
except Exception as e:
error_details.append(f"Error retrieving local commit: {e}")
# Get the signing key using "git log -1 --format=%GK"
signing_key = ""
try:
result = subprocess.run(["git", "log", "-1", "--format=%GK"], cwd=repo_dir, shell=False, check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
signing_key = result.stdout.strip()
except Exception as e:
error_details.append(f"Error retrieving signing key: {e}")
commit_check_passed = True
gpg_check_passed = True
if expected_commit:
if commit_hash != expected_commit:
commit_check_passed = False
error_details.append(f"Expected commit: {expected_commit}, found: {commit_hash}")
if expected_gpg_keys:
if signing_key not in expected_gpg_keys:
gpg_check_passed = False
error_details.append(f"Expected one of GPG keys: {expected_gpg_keys}, found: {signing_key}")
if expected_commit and expected_gpg_keys:
verified_ok = commit_check_passed and gpg_check_passed
elif expected_commit:
verified_ok = commit_check_passed
elif expected_gpg_keys:
verified_ok = gpg_check_passed
else:
verified_ok = True
return verified_ok, error_details, commit_hash, signing_key