From e807d8030db8f7a8e1f9550c7084afefe95a2253 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Tue, 4 Mar 2025 13:43:23 +0100 Subject: [PATCH] Added config --- config.yaml | 11 +++++ main.py | 133 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 113 insertions(+), 31 deletions(-) create mode 100644 config.yaml diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..46cdd8d --- /dev/null +++ b/config.yaml @@ -0,0 +1,11 @@ +base: /home/kevinveenbirkenbach/Repositories/ +repos: +- account: kevinveenbirkenbach + command: '' + description: Package Manager for the Kevin Veen-Birkenbach Scripting Universe + provider: github.com + replacement: '' + repository: package-manager + setup: '' + teardown: '' + verified: '' diff --git a/main.py b/main.py index ce4e15c..fe09851 100644 --- a/main.py +++ b/main.py @@ -21,10 +21,14 @@ This script provides the following commands: - Combines pull and install; if --system is specified also runs system update commands. status {identifier(s)|--all|--system} - Shows git status for each repository or, if --system is set, shows basic system update information. + config {identifier(s)|--all} + - Displays the configuration for one or more repositories. If no identifier is given, shows the entire config. + add + - Starts an interactive dialog to add a new repository configuration entry. Additional flags: --preview Only show the changes without executing commands. - --list When used with preview or status, only list affected repositories. + --list When used with preview or status, only list affected repositories (for status). Identifiers: - If a repository’s name is unique then you can just use the repository name. @@ -71,6 +75,12 @@ def load_config(config_path): sys.exit(1) return config +def save_config(config, config_path): + """Save the config dictionary back to the YAML file.""" + with open(config_path, 'w') as f: + yaml.dump(config, f) + print(f"Configuration updated in {config_path}.") + def run_command(command, cwd=None, preview=False): """Run a shell command in a given directory, or print it in preview mode.""" if preview: @@ -94,19 +104,16 @@ def get_repo_identifier(repo, all_repos): def resolve_repos(identifiers, all_repos): """ - Given a list of identifier strings (or an empty list), return a list of repository configs. - If identifiers is empty, return an empty list (caller can decide what to do). + Given a list of identifier strings, return a list of repository configs. The identifier can be either just the repository name (if unique) or full provider/account/repository. """ selected = [] for ident in identifiers: matches = [] for repo in all_repos: - # Check for full identifier match full_id = f'{repo.get("provider")}/{repo.get("account")}/{repo.get("repository")}' if ident == full_id: matches.append(repo) - # Or if repository name matches and it is unique, accept it elif ident == repo.get("repository"): # Only add if it is unique among all_repos if sum(1 for r in all_repos if r.get("repository") == ident) == 1: @@ -117,7 +124,7 @@ def resolve_repos(identifiers, all_repos): selected.extend(matches) return selected -def create_executable(repo, base_dir, bin_dir, preview=False): +def create_executable(repo, base_dir, bin_dir, all_repos, preview=False): """Create an executable bash wrapper for the repository.""" repo_identifier = get_repo_identifier(repo, all_repos) repo_dir = os.path.join(base_dir, repo.get("provider"), repo.get("account"), repo.get("repository")) @@ -149,7 +156,7 @@ cd "{repo_dir}" os.chmod(alias_path, 0o755) print(f"Installed executable for {repo_identifier} at {alias_path}") -def install_repos(selected_repos, base_dir, bin_dir, preview=False): +def install_repos(selected_repos, base_dir, bin_dir, all_repos, preview=False): """Install one or more repositories by creating their executable wrappers and running setup.""" for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) @@ -157,13 +164,13 @@ def install_repos(selected_repos, base_dir, bin_dir, preview=False): if not os.path.exists(repo_dir): print(f"Repository directory '{repo_dir}' does not exist. Clone it first.") continue - create_executable(repo, base_dir, bin_dir, preview=preview) + create_executable(repo, base_dir, bin_dir, all_repos, preview=preview) # Execute setup command if defined setup_cmd = repo.get("setup") if setup_cmd: run_command(setup_cmd, cwd=repo_dir, preview=preview) -def pull_repos(selected_repos, base_dir, preview=False): +def pull_repos(selected_repos, base_dir, all_repos, preview=False): """Run 'git pull' in the repository directory.""" for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) @@ -173,7 +180,7 @@ def pull_repos(selected_repos, base_dir, preview=False): else: print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") -def clone_repos(selected_repos, base_dir, preview=False): +def clone_repos(selected_repos, base_dir, all_repos, preview=False): """Clone repositories based on the config. Uses replacement if defined; otherwise, constructs URL from provider/account/repository. """ @@ -191,7 +198,7 @@ def clone_repos(selected_repos, base_dir, preview=False): os.makedirs(parent_dir, exist_ok=True) run_command(f"git clone {clone_url} {repo_dir}", cwd=parent_dir, preview=preview) -def push_repos(selected_repos, base_dir, preview=False): +def push_repos(selected_repos, base_dir, all_repos, preview=False): """Run 'git push' in the repository directory.""" for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) @@ -201,7 +208,7 @@ def push_repos(selected_repos, base_dir, preview=False): else: print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") -def deinstall_repos(selected_repos, base_dir, bin_dir, preview=False): +def deinstall_repos(selected_repos, base_dir, bin_dir, all_repos, preview=False): """Remove the executable wrapper and run teardown command if defined.""" for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) @@ -220,7 +227,7 @@ def deinstall_repos(selected_repos, base_dir, bin_dir, preview=False): if teardown_cmd and os.path.exists(repo_dir): run_command(teardown_cmd, cwd=repo_dir, preview=preview) -def delete_repos(selected_repos, base_dir, preview=False): +def delete_repos(selected_repos, base_dir, all_repos, preview=False): """Delete the repository directory (rm -rv equivalent).""" for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) @@ -234,16 +241,16 @@ def delete_repos(selected_repos, base_dir, preview=False): else: print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") -def update_repos(selected_repos, base_dir, bin_dir, system_update=False, preview=False): +def update_repos(selected_repos, base_dir, bin_dir, all_repos, system_update=False, preview=False): """Combine pull and install. If system_update is True, run system update commands.""" - pull_repos(selected_repos, base_dir, preview=preview) - install_repos(selected_repos, base_dir, bin_dir, preview=preview) + pull_repos(selected_repos, base_dir, all_repos, preview=preview) + install_repos(selected_repos, base_dir, bin_dir, all_repos, preview=preview) if system_update: # Example system update commands (for Arch-based systems) run_command("yay -S", preview=preview) run_command("sudo pacman -Syyu", preview=preview) -def status_repos(selected_repos, base_dir, list_only=False, system_status=False, preview=False): +def status_repos(selected_repos, base_dir, all_repos, list_only=False, system_status=False, preview=False): """Show status information for repositories. If list_only is True, only list affected repositories. If system_status is True, show system update status. @@ -251,7 +258,6 @@ def status_repos(selected_repos, base_dir, list_only=False, system_status=False, if system_status: print("System status:") run_command("yay -Qu", preview=preview) - # You might want to add additional system status commands here. for repo in selected_repos: repo_identifier = get_repo_identifier(repo, all_repos) if list_only: @@ -264,11 +270,50 @@ def status_repos(selected_repos, base_dir, list_only=False, system_status=False, else: print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") +def show_config(selected_repos, full_config=False): + """Display configuration for one or more repositories. If full_config is True, display entire config.""" + if full_config: + with open(CONFIG_PATH, 'r') as f: + print(f.read()) + else: + for repo in selected_repos: + identifier = f'{repo.get("provider")}/{repo.get("account")}/{repo.get("repository")}' + print(f"Repository: {identifier}") + for key, value in repo.items(): + print(f" {key}: {value}") + print("-" * 40) + +def interactive_add(config): + """Interactively prompt the user to add a new repository entry to the config.""" + print("Adding a new repository configuration entry.") + new_entry = {} + new_entry["provider"] = input("Provider (e.g., github.com): ").strip() + new_entry["account"] = input("Account (e.g., yourusername): ").strip() + new_entry["repository"] = input("Repository name (e.g., mytool): ").strip() + new_entry["verified"] = input("Verified commit id: ").strip() + new_entry["command"] = input("Command (optional, leave blank to auto-detect): ").strip() + new_entry["description"] = input("Description (optional): ").strip() + new_entry["replacement"] = input("Replacement (optional): ").strip() + new_entry["setup"] = input("Setup command (optional): ").strip() + new_entry["teardown"] = input("Teardown command (optional): ").strip() + + # Confirm addition + print("\nNew entry:") + for key, value in new_entry.items(): + if value: + print(f"{key}: {value}") + confirm = input("Add this entry to config? (y/N): ").strip().lower() + if confirm == "y": + config["repos"].append(new_entry) + save_config(config, CONFIG_PATH) + else: + print("Entry not added.") + if __name__ == "__main__": # Load configuration config = load_config(CONFIG_PATH) base_dir = config["base"] - all_repos = config["repos"] + all_repos_list = config["repos"] parser = argparse.ArgumentParser(description="Package Manager") subparsers = parser.add_subparsers(dest="command", help="Subcommands") @@ -314,30 +359,56 @@ if __name__ == "__main__": add_identifier_arguments(status_parser) status_parser.add_argument("--system", action="store_true", help="Show system status") + # config: show configuration details + config_parser = subparsers.add_parser("config", help="Show configuration for repository/repositories or entire config") + add_identifier_arguments(config_parser) + + # add: interactive add of a new repository entry + add_parser = subparsers.add_parser("add", help="Interactively add a new repository configuration entry") + args = parser.parse_args() - # Determine which repositories to operate on - if args.all or (hasattr(args, "identifiers") and not args.identifiers): - selected = all_repos + # Handle "config" command separately + if args.command == "config": + if args.all or (hasattr(args, "identifiers") and not args.identifiers): + # Show the entire configuration file + show_config([], full_config=True) + else: + selected = resolve_repos(args.identifiers, all_repos_list) + if selected: + show_config(selected, full_config=False) + sys.exit(0) + + # Handle "add" command + if args.command == "add": + interactive_add(config) + sys.exit(0) + + # For commands using identifiers, decide which repositories to operate on + if hasattr(args, "identifiers"): + if args.all or (not args.identifiers): + selected = all_repos_list + else: + selected = resolve_repos(args.identifiers, all_repos_list) else: - selected = resolve_repos(args.identifiers, all_repos) + selected = [] # Dispatch commands if args.command == "install": - install_repos(selected, base_dir, BIN_DIR, preview=args.preview) + install_repos(selected, base_dir, BIN_DIR, all_repos_list, preview=args.preview) elif args.command == "pull": - pull_repos(selected, base_dir, preview=args.preview) + pull_repos(selected, base_dir, all_repos_list, preview=args.preview) elif args.command == "clone": - clone_repos(selected, base_dir, preview=args.preview) + clone_repos(selected, base_dir, all_repos_list, preview=args.preview) elif args.command == "push": - push_repos(selected, base_dir, preview=args.preview) + push_repos(selected, base_dir, all_repos_list, preview=args.preview) elif args.command == "deinstall": - deinstall_repos(selected, base_dir, BIN_DIR, preview=args.preview) + deinstall_repos(selected, base_dir, BIN_DIR, all_repos_list, preview=args.preview) elif args.command == "delete": - delete_repos(selected, base_dir, preview=args.preview) + delete_repos(selected, base_dir, all_repos_list, preview=args.preview) elif args.command == "update": - update_repos(selected, base_dir, BIN_DIR, system_update=args.system, preview=args.preview) + update_repos(selected, base_dir, BIN_DIR, all_repos_list, system_update=args.system, preview=args.preview) elif args.command == "status": - status_repos(selected, base_dir, list_only=args.list, system_status=args.system, preview=args.preview) + status_repos(selected, base_dir, all_repos_list, list_only=args.list, system_status=args.system, preview=args.preview) else: parser.print_help()