diff --git a/config/defaults.yaml b/config/defaults.yaml index 010268d..018fe49 100644 --- a/config/defaults.yaml +++ b/config/defaults.yaml @@ -10,172 +10,224 @@ repositories: repository: analysis-ready-code description: Analysis-Ready Code (ARC) is a Python utility that recursively scans directories and transforms source code into a streamlined, analysis-ready format by removing comments, filtering files, and compressing content—perfect for AI and automated code analysis. homepage: https://github.com/kevinveenbirkenbach/analysis-ready-code - verified: bef5f392d7e8a292fb9e4ee40809e434dd6142a9 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach description: A configurable Python package manager that automates repository tasks—including cloning, installation, updates, and status reporting—based on a YAML configuration file for streamlined software management which gives you access to the Kevin Veen-Birkenbach Code Universe. homepage: https://github.com/kevinveenbirkenbach/package-manager provider: github.com repository: package-manager alias: 'pkgmgr' + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach description: Money Monitor is a comprehensive financial document organizer that scans bank statements and invoices to extract, filter, and export transaction data in multiple formats. Installable via Kevin's Package Manager as "momo", it simplifies financial logging and tax preparation. homepage: https://github.com/kevinveenbirkenbach/money-monitor provider: github.com repository: money-monitor alias: 'momo' + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: os provider: github.com repository: omni-search description: OmniSearch (OS) is a versatile file content search tool that scans directories for strings across multiple formats (PDFs, text, images, spreadsheets, etc.), offering robust filtering, error handling, and JSON output. Easily installable via Kevin's Package Manager under the alias os 🔍🚀. homepage: https://github.com/kevinveenbirkenbach/omni-search - verified: 93875b27280191c317806056847809464d9f5282 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: bsr provider: github.com repository: bulk-string-replacer description: Bulk String Replacer CLI (bsr) is a command-line tool for efficiently searching and replacing strings in file names, folder names, and file contents across directories. It's perfect for bulk updates and streamlining your workflow. homepage: https://github.com/kevinveenbirkenbach/bulk-string-replacer - verified: daf45389383ef6b3d6569d63eb3e052ab811916d + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: imgrszr provider: github.com repository: image-resizer-cli description: Image Resizer CLI (imgrszr) is a lightweight command-line tool that resizes images by percentage, maximum dimensions, or target file size. Easily installable via Kevin's Package Manager. It’s perfect for preparing large photos for upload and streamlining your image workflow. 🚀 homepage: https://github.com/kevinveenbirkenbach/image-resizer-cli - verified: e8b48c08679a1fc73339514db55f42a6ba55b44e + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach provider: github.com alias: chroso repository: chrono-sorter description: ChronoSorter (chroso) is a CLI tool that updates media file metadata and renames images based on their earliest timestamps. Easily installed via Kevin's Package Manager under the alias chroso, it helps organize your photos chronologically. homepage: https://github.com/kevinveenbirkenbach/chrono-sorter - verified: + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach provider: github.com repository: media-sorter alias: medso description: MediaSorter (medso) is a command-line tool that automatically sorts media files by moving them between your Pictures and Videos directories based on file extensions, with support for preview and verbose modes. homepage: https://github.com/kevinveenbirkenbach/media-sorter - verified: 502fba5198474caaf805fb6bc3a7bbe63e699d69 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: ansenc provider: github.com repository: ansible-encryptor description: Ansible Encrypter is a versatile Python script for secure file management using Ansible Vault. It allows encryption and decryption of files within a directory, supports temporary file access, and automatically updates .gitignore to secure sensitive data. Ideal for enhancing data security in Ansible-managed environments homepage: https://github.com/kevinveenbirkenbach/ansible-encryptor/ - verified: 2803ea3102cf3bc3d9caf670493b4270cf2d3293 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: lim provider: github.com repository: linux-image-manager description: Linux Image Manager is a powerful suite of shell scripts for downloading, configuring, and managing Linux images—including encrypted storage, RAID1 setups, chroot environments, and backups. homepage: https://github.com/kevinveenbirkenbach/linux-image-manager - verified: 7f82c6fcb9e5e6e8a03713e6e21184761ca0c719 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: dufiha provider: github.com repository: duplicate-file-handler description: Duplicate File Handler is a CLI tool that detects duplicate files and lets you manage them by deleting or replacing with hard/symbolic links. Easily installable via Kevin's Package Manager under the alias dufiha, it supports file-type filtering and offers interactive, preview, or active modes. homepage: https://github.com/kevinveenbirkenbach/duplicate-file-handler - verified: 89e15dd023aee82190bacaadc337c282b91f5357 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach provider: github.com alias: swafo repository: swap-forge description: SwapForge (swafo) is a Bash script that automates the creation and management of Linux swapfiles. It easily creates a swapfile of a specified size, sets proper permissions, and updates /etc/fstab to ensure the swap is activated at boot, streamlining swap management for your Linux system. homepage: https://github.com/kevinveenbirkenbach/swap-forge - verified: 0af1e6ea425ecc46f4055a10cfa00dad7b1cc511 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: sisec provider: github.com repository: splitted-secret description: Split Secret (sisec) is a command-line tool that securely splits a master secret among multiple users using secret sharing. Only a predefined quorum of users can combine their shares to decrypt the original secret. The tool supports robust encryption, decryption, and cleanup operations. homepage: https://github.com/kevinveenbirkenbach/splitted-secret - verified: 5e4fe2c01a8b024e5f455e555adfc99e9f131311 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: dirval provider: github.com repository: directory-validator description: Directory Validator (dirval) is a Python CLI tool that creates a unique hash stamp for a directory and validates its contents, ensuring you can quickly detect any changes. homepage: https://github.com/kevinveenbirkenbach/directory-validator - verified: 7db99608dd1ce4cf442cc4e3d37d2b61e6a6ec00 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: lukyma provider: github.com repository: luks-key-manager description: LUKS Key Manager(lukyma) is a Python-based utility that simplifies the management of LUKS encryption keys on Linux systems. It enables you to add new keys with a configurable memory cost and securely remove old ones, making it ideal for resource-constrained devices like the Raspberry Pi. homepage: https://github.com/kevinveenbirkenbach/luks-key-manager - verified: 18ef26f9878ff9c605b9f6af0a5bd96c8da4fc9d + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: 4dvt provider: github.com repository: 4d-video-titel homepage: https://github.com/kevinveenbirkenbach/4d-video-titel description: 4D Video Titel is a CLI tool that extracts metadata from video files and automatically generates descriptive titles including local date, time, timezone, and GPS coordinates – perfect for organizing and enriching your video content! - verified: eb31cde28a940f01a3068142f93dfb2954c9adef + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: btrfsauba description: The btrfs-balance-automator automates Btrfs filesystem balancing with dynamic decrementing usage thresholds. It provides real-time status updates, enhancing system performance and storage optimization. This Python script is essential for system admins aiming to streamline Btrfs maintenance. provider: github.com repository: btrfs-auto-balancer - verified: 24acdf9643f946ea3513890b0ddc547c2426c0c4 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: baudolo homepage: https://github.com/kevinveenbirkenbach/backup-docker-to-local description: Backup Docker Volumes to Local is a comprehensive solution that leverages rsync to create incremental backups of Docker volumes, providing seamless recovery for both file and database data. Ideal for ensuring the integrity and security of your container data. provider: github.com repository: backup-docker-to-local - verified: a355f34e6ebce608ce1bad5464d7c52cf8b80f8b + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: clndoba homepage: https://github.com/kevinveenbirkenbach/cleanup-failed-docker-backups description: Backup Docker Volumes to Local is a comprehensive solution that leverages rsync to create incremental backups of Docker volumes, providing seamless recovery for both file and database data. Ideal for ensuring the integrity and security of your container data. provider: github.com repository: cleanup-failed-docker-backups - verified: 13b1b7d49394f9d82f8e53e3559ddbe1155e9dbb + verified: + gpg_keys: + - 44D8F11FD62F878E - alias: cedama account: kevinveenbirkenbach homepage: https://github.com/kevinveenbirkenbach/central-database-manager description: Scripts to manage the Central Databases Postgres in CyMaIS provider: github.com repository: central-database-manager - verified: f350c135e63946f9840c1d205deb8f62fbb6fcf1 + verified: + gpg_keys: + - 44D8F11FD62F878E - alias: dovore account: kevinveenbirkenbach homepage: https://github.com/kevinveenbirkenbach/docker-volume-renamer description: A Bash script to create a new Docker volume, copy data from an existing volume to it, and remove the old volume. provider: github.com repository: docker-volume-renamer - verified: e702deb3347bc868134ce89273cd81b70af51899 + verified: + gpg_keys: + - 44D8F11FD62F878E - alias: gigimi account: kevinveenbirkenbach homepage: https://github.com/kevinveenbirkenbach/github-to-gitea-mirror/ description: A tool designed to automate the process of mirroring repositories from GitHub to Gitea. By leveraging the APIs of both platforms, this script identifies repositories on GitHub that aren't mirrored on Gitea and seamlessly mirrors them. Ideal for developers aiming to maintain a backup or a consistent repo state across both platforms. provider: github.com repository: github-to-gitea-mirror - verified: a6037b955425c256c535ac55047867af5d991e37 + verified: + gpg_keys: + - 44D8F11FD62F878E - alias: seedssh account: kevinveenbirkenbach homepage: https://github.com/kevinveenbirkenbach/seed-ssh-key description: A tool designed to automate the process of mirroring repositories from GitHub to Gitea. By leveraging the APIs of both platforms, this script identifies repositories on GitHub that aren't mirrored on Gitea and seamlessly mirrors them. Ideal for developers aiming to maintain a backup or a consistent repo state across both platforms. provider: github.com repository: seed-ssh-key - verified: 0e605e23220a29d54e0867a37c57ca42bf63aec9 + verified: + gpg_keys: + - 44D8F11FD62F878E - alias: anscrico account: kevinveenbirkenbach homepage: https://github.com/kevinveenbirkenbach/analyze-script-collection description: A versatile set of Bash scripts for Linux system diagnostics. Easily check SSH key encryption, find duplicate files, inspect your kernel version, locate large files, and list installed Java versions—all designed to simplify system management and troubleshooting. provider: github.com repository: analyze-script-collection - verified: ab8c92ef1d65fb5082930f80cf1736141d94a149 + verified: + gpg_keys: + - 44D8F11FD62F878E - alias: heicma account: kevinveenbirkenbach homepage: https://github.com/kevinveenbirkenbach/heic-management description: "Bash scripts for handling HEIC images: converting to JPEG, deleting, and listing in a directory and subdirectories." provider: github.com repository: heic-management - verified: 3dd336b67f0f9d1f7ee4cd66724ca6c9bd091409 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach account: kevinveenbirkenbach alias: portfolio @@ -183,33 +235,43 @@ repositories: description: Portfolio CMS is a Flask-based content management system that lets you effortlessly showcase your projects and online presence. With customizable cards, dynamic navigation, and YAML-driven configuration, it's a flexible solution for building modern, responsive portfolio websites. repository: portfolio homepage: https://github.com/kevinveenbirkenbach/portfolio - verified: fe1c038d1aea062ce7ad1d7ff186463994fd7f85 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: goexma provider: github.com homepage: https://github.com/kevinveenbirkenbach/cli-gnome-extension-manager description: CLI GNOME Extension Manager (goexma) is a Bash script that lets you install, update, enable, and disable GNOME extensions directly from the command line—perfect for automation and power users. repository: cli-gnome-extension-manager - verified: 834e6e95636a4875ca9d9c73c0521508e2192aed + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: cymais provider: github.com description: CyMaIS streamlines Linux-based system setups and Docker image administration, perfect for servers and PCs. It offers extensive solutions for system initialization, admin tools, backups, monitoring, updates, driver management, security, and VPNs. homepage: https://github.com/kevinveenbirkenbach/cymais repository: cymais - verified: 2898cc60868403220dcb69e0588d2d9aeb55bb27 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach provider: github.com repository: dynamic-miner homepage: https://github.com/kevinveenbirkenbach/dynamic-miner desciption: Automated Ethereum mining setup using Docker and a GPU/CPU monitoring script. Includes a Docker Compose file for an Ethereum miner container and a Bash script to start/stop mining based on system usage thresholds, ensuring efficient resource utilization. Perfect for idle-time mining. - verified: 27d85cf220f6c603487b61cb4518c5a5ad00b35d + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach provider: github.com description: This repository provides automated bridges for transferring content from federated platforms (like Pixelfed, Mastodon, and PeerTube) to centralized social networks (such as Instagram, X (Twitter), Facebook, and YouTube). It enables seamless cross-posting to maximize content reach across decentralized and mainstream platforms. repository: fediverse-to-oligopolies-bridge homepage: https://github.com/kevinveenbirkenbach/fediverse-to-oligopolies-bridge - verified: 1f6ace3fe84cb537436839a8a8164f1dc7b6e0c3 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: bure provider: github.com @@ -218,7 +280,9 @@ repositories: Bulk Rename is a Python-based CLI tool that renames files in bulk by replacing specified substrings in file names. It supports both recursive and non-recursive renaming with verbose output. homepage: https://github.com/kevinveenbirkenbach/bulk-rename - verified: f76b29720edf2df5760070e34eacae23beb9d3a7 + verified: + gpg_keys: + - 44D8F11FD62F878E - account: kevinveenbirkenbach alias: gitconfig provider: github.com @@ -228,4 +292,6 @@ repositories: It offers both interactive prompts and command-line options to set merge strategies, user details, and commit signing preferences. homepage: https://github.com/kevinveenbirkenbach/git-configurator - verified: 8441c2c34a6916a0610bf52155f8e63c0dbe203b + verified: + gpg_keys: + - 44D8F11FD62F878E diff --git a/main.py b/main.py index 347648d..fe103fb 100755 --- a/main.py +++ b/main.py @@ -42,6 +42,7 @@ GIT_DEFAULT_COMMANDS = [ "clone", "reset", "revert", + "rebase", "commit" ] @@ -98,7 +99,7 @@ For detailed help on each command, use: install_parser = subparsers.add_parser("install", help="Setup repository/repositories alias links to executables") add_identifier_arguments(install_parser) install_parser.add_argument("-q", "--quiet", action="store_true", help="Suppress warnings and info messages") - install_parser.add_argument("--no-verification", default=False, action="store_true", help="Disable verification of repository commit") + install_parser.add_argument("--no-verification", action="store_true", default=False, help="Disable verification via commit/gpg") deinstall_parser = subparsers.add_parser("deinstall", help="Remove alias links to repository/repositories") add_identifier_arguments(deinstall_parser) @@ -110,7 +111,7 @@ For detailed help on each command, use: add_identifier_arguments(update_parser) update_parser.add_argument("--system", action="store_true", help="Include system update commands") update_parser.add_argument("-q", "--quiet", action="store_true", help="Suppress warnings and info messages") - update_parser.add_argument("--no-verification", action="store_true", default=False, help="Disable verification of repository commit") + update_parser.add_argument("--no-verification", action="store_true", default=False, help="Disable verification via commit/gpg") status_parser = subparsers.add_parser("status", help="Show status for repository/repositories or system") add_identifier_arguments(status_parser) @@ -158,8 +159,9 @@ For detailed help on each command, use: formatter_class=argparse.RawTextHelpFormatter ) add_identifier_arguments(git_command_parsers[git_command]) - if git_command == "pull": - git_command_parsers[git_command].add_argument("--no-verification", action="store_true", default=False, help="Disable verification of repository commit") + if git_command in ["pull","clone"]: + git_command_parsers[git_command].add_argument("--no-verification", action="store_true", default=False, help="Disable verification via commit/gpg") + args = parser.parse_args() @@ -171,7 +173,7 @@ For detailed help on each command, use: install_repos(selected,repositories_base_dir, BIN_DIR, all_repos_list, args.no_verification, preview=args.preview, quiet=args.quiet) elif args.command in GIT_DEFAULT_COMMANDS: if args.command == "clone": - clone_repos(selected, repositories_base_dir, all_repos_list, args.preview) + clone_repos(selected, repositories_base_dir, all_repos_list, args.preview, no_verification=args.no_verification) elif args.command == "pull": from pkgmgr.pull_with_verification import pull_with_verification pull_with_verification(selected, repositories_base_dir, all_repos_list, args.extra_args, no_verification=args.no_verification, preview=args.preview) diff --git a/pkgmgr/clone_repos.py b/pkgmgr/clone_repos.py index 7916e0b..0d23652 100644 --- a/pkgmgr/clone_repos.py +++ b/pkgmgr/clone_repos.py @@ -2,8 +2,9 @@ import subprocess import os from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.get_repo_identifier import get_repo_identifier +from pkgmgr.verify import verify_repository -def clone_repos(selected_repos, repositories_base_dir: str, all_repos, preview=False): +def clone_repos(selected_repos, repositories_base_dir: str, all_repos, preview:bool, no_verification:bool): for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) repo_dir = get_repo_dir(repositories_base_dir, repo) @@ -21,12 +22,10 @@ def clone_repos(selected_repos, repositories_base_dir: str, all_repos, preview=F if preview: print(f"[Preview] Would run: git clone {clone_url} {repo_dir} in {parent_dir}") - # Simulate a successful clone in preview mode. result = subprocess.CompletedProcess(args=[], returncode=0) else: result = subprocess.run(f"git clone {clone_url} {repo_dir}", cwd=parent_dir, shell=True) - # If SSH clone returns an error code, ask user whether to try HTTPS. if result.returncode != 0: print(f"[WARNING] SSH clone failed for '{repo_identifier}' with return code {result.returncode}.") choice = input("Do you want to attempt HTTPS clone instead? (y/N): ").strip().lower() @@ -36,7 +35,21 @@ def clone_repos(selected_repos, repositories_base_dir: str, all_repos, preview=F print(f"[INFO] Attempting to clone '{repo_identifier}' using HTTPS from {clone_url} into '{repo_dir}'.") if preview: print(f"[Preview] Would run: git clone {clone_url} {repo_dir} in {parent_dir}") + result = subprocess.CompletedProcess(args=[], returncode=0) else: - subprocess.run(f"git clone {clone_url} {repo_dir}", cwd=parent_dir, shell=True) + result = subprocess.run(f"git clone {clone_url} {repo_dir}", cwd=parent_dir, shell=True) else: - print(f"[INFO] HTTPS clone not attempted for '{repo_identifier}'.") \ No newline at end of file + print(f"[INFO] HTTPS clone not attempted for '{repo_identifier}'.") + continue + + # After cloning, perform verification in local mode. + verified_info = repo.get("verified") + if verified_info: + verified_ok, errors, commit_hash, signing_key = verify_repository(repo, repo_dir, mode="local", no_verification=no_verification) + if not no_verification and not verified_ok: + print(f"Warning: Verification failed for {repo_identifier} after cloning:") + for err in errors: + print(f" - {err}") + choice = input("Proceed anyway? (y/N): ").strip().lower() + if choice != "y": + print(f"Skipping repository {repo_identifier} due to failed verification.") diff --git a/pkgmgr/delete_repos.py b/pkgmgr/delete_repos.py index 1fe5435..7c31fd9 100644 --- a/pkgmgr/delete_repos.py +++ b/pkgmgr/delete_repos.py @@ -1,5 +1,7 @@ import shutil import os +from pkgmgr.get_repo_identifier import get_repo_identifier +from pkgmgr.get_repo_dir import get_repo_dir def delete_repos(selected_repos, repositories_base_dir, all_repos, preview=False): for repo in selected_repos: diff --git a/pkgmgr/install_repos.py b/pkgmgr/install_repos.py index 5d686d8..f733324 100644 --- a/pkgmgr/install_repos.py +++ b/pkgmgr/install_repos.py @@ -5,16 +5,13 @@ from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.get_repo_dir import get_repo_dir from pkgmgr.create_ink import create_ink from pkgmgr.run_command import run_command +from pkgmgr.verify import verify_repository def install_repos(selected_repos, repositories_base_dir, bin_dir, all_repos, no_verification, preview=False, quiet=False): """ - Install repositories by creating symbolic links (via create_ink) and running setup commands. + Install repositories by creating symbolic links and running setup commands. - This version applies hash verification: - - It retrieves the current commit hash using 'git rev-parse HEAD' and compares it to the - configured 'verified' hash. - - If the hashes do not match and no_verification is False, the user is prompted for confirmation. - - If the user does not confirm, the installation for that repository is skipped. + Verifies repository state using verify_repository in local mode. """ for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) @@ -23,30 +20,21 @@ def install_repos(selected_repos, repositories_base_dir, bin_dir, all_repos, no_ print(f"Repository directory '{repo_dir}' does not exist. Clone it first.") continue - # Apply hash verification if a verified hash is defined. - verified_hash = repo.get("verified") - if verified_hash: - current_hash = "" - try: - result = subprocess.run("git rev-parse HEAD", cwd=repo_dir, shell=True, check=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - current_hash = result.stdout.strip() - except Exception as e: - print(f"Error retrieving current commit for {repo_identifier}: {e}") - - proceed = True - if not no_verification and current_hash and current_hash != verified_hash: - print(f"Warning: For {repo_identifier}, the current commit hash ({current_hash}) does not match the verified hash ({verified_hash}).") - choice = input("Proceed with installation? (y/N): ").strip().lower() - if choice != "y": - proceed = False - if not proceed: + verified_info = repo.get("verified") + verified_ok, errors, commit_hash, signing_key = verify_repository(repo, repo_dir, mode="local", no_verification=no_verification) + + if not no_verification and verified_info and not verified_ok: + print(f"Warning: Verification failed for {repo_identifier}:") + for err in errors: + print(f" - {err}") + choice = input("Proceed with installation? (y/N): ").strip().lower() + if choice != "y": print(f"Skipping installation for {repo_identifier}.") continue - # Create the symlink using the new create_ink function. + # Create the symlink using the create_ink function. create_ink(repo, repositories_base_dir, bin_dir, all_repos, quiet=quiet, preview=preview) setup_cmd = repo.get("setup") if setup_cmd: - run_command(setup_cmd, cwd=repo_dir, preview=preview) \ No newline at end of file + run_command(setup_cmd, cwd=repo_dir, preview=preview) diff --git a/pkgmgr/pull_with_verification.py b/pkgmgr/pull_with_verification.py index 904e036..d68dd67 100644 --- a/pkgmgr/pull_with_verification.py +++ b/pkgmgr/pull_with_verification.py @@ -3,15 +3,15 @@ import subprocess import sys from pkgmgr.get_repo_identifier import get_repo_identifier from pkgmgr.get_repo_dir import get_repo_dir +from pkgmgr.verify import verify_repository def pull_with_verification(selected_repos, repositories_base_dir, all_repos, extra_args, no_verification, preview=False): """ - Executes "git pull" for each repository with hash verification. - - For repositories with a 'verified' hash in the configuration, this function first - retrieves the remote commit hash (using 'git ls-remote origin HEAD'). If the remote hash - does not match the verified hash, the user is prompted to confirm the pull (unless --no-verification - is set, in which case the pull proceeds automatically). + Executes "git pull" for each repository with verification. + + Uses the verify_repository function in "pull" mode. + If verification fails (and verification info is set) and --no-verification is not enabled, + the user is prompted to confirm the pull. """ for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) @@ -20,30 +20,23 @@ def pull_with_verification(selected_repos, repositories_base_dir, all_repos, ext print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") continue - verified_hash = repo.get("verified") - remote_hash = "" - try: - result = subprocess.run("git ls-remote origin HEAD", cwd=repo_dir, shell=True, check=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - # The first token in the output is the remote HEAD commit hash. - remote_hash = result.stdout.split()[0].strip() - except Exception as e: - print(f"Error retrieving remote commit for {repo_identifier}: {e}") + verified_info = repo.get("verified") + verified_ok, errors, commit_hash, signing_key = verify_repository(repo, repo_dir, mode="pull", no_verification=no_verification) - proceed = True - if not no_verification and verified_hash and remote_hash and remote_hash != verified_hash: - print(f"Warning: For {repo_identifier}, the remote hash ({remote_hash}) does not match the verified hash ({verified_hash}).") + if not no_verification and verified_info and not verified_ok: + print(f"Warning: Verification failed for {repo_identifier}:") + for err in errors: + print(f" - {err}") choice = input("Proceed with 'git pull'? (y/N): ").strip().lower() if choice != "y": - proceed = False + continue - if proceed: - full_cmd = f"git pull {' '.join(extra_args)}" - if preview: - print(f"[Preview] In '{repo_dir}': {full_cmd}") - else: - print(f"Running in '{repo_dir}': {full_cmd}") - result = subprocess.run(full_cmd, cwd=repo_dir, shell=True) - if result.returncode != 0: - print(f"'git pull' for {repo_identifier} failed with exit code {result.returncode}.") - sys.exit(result.returncode) + full_cmd = f"git pull {' '.join(extra_args)}" + if preview: + print(f"[Preview] In '{repo_dir}': {full_cmd}") + else: + print(f"Running in '{repo_dir}': {full_cmd}") + result = subprocess.run(full_cmd, cwd=repo_dir, shell=True) + if result.returncode != 0: + print(f"'git pull' for {repo_identifier} failed with exit code {result.returncode}.") + sys.exit(result.returncode) diff --git a/pkgmgr/verify.py b/pkgmgr/verify.py new file mode 100644 index 0000000..a86baea --- /dev/null +++ b/pkgmgr/verify.py @@ -0,0 +1,107 @@ +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