Added config

This commit is contained in:
Kevin Veen-Birkenbach
2025-03-04 13:43:23 +01:00
parent c0cdfc6de2
commit e807d8030d
2 changed files with 113 additions and 31 deletions

11
config.yaml Normal file
View File

@@ -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: ''

133
main.py
View File

@@ -21,10 +21,14 @@ This script provides the following commands:
- Combines pull and install; if --system is specified also runs system update commands. - Combines pull and install; if --system is specified also runs system update commands.
status {identifier(s)|--all|--system} status {identifier(s)|--all|--system}
- Shows git status for each repository or, if --system is set, shows basic system update information. - 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: Additional flags:
--preview Only show the changes without executing commands. --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: Identifiers:
- If a repositorys name is unique then you can just use the repository name. - If a repositorys name is unique then you can just use the repository name.
@@ -71,6 +75,12 @@ def load_config(config_path):
sys.exit(1) sys.exit(1)
return config 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): def run_command(command, cwd=None, preview=False):
"""Run a shell command in a given directory, or print it in preview mode.""" """Run a shell command in a given directory, or print it in preview mode."""
if preview: if preview:
@@ -94,19 +104,16 @@ def get_repo_identifier(repo, all_repos):
def resolve_repos(identifiers, all_repos): def resolve_repos(identifiers, all_repos):
""" """
Given a list of identifier strings (or an empty list), return a list of repository configs. Given a list of identifier strings, return a list of repository configs.
If identifiers is empty, return an empty list (caller can decide what to do).
The identifier can be either just the repository name (if unique) or full provider/account/repository. The identifier can be either just the repository name (if unique) or full provider/account/repository.
""" """
selected = [] selected = []
for ident in identifiers: for ident in identifiers:
matches = [] matches = []
for repo in all_repos: for repo in all_repos:
# Check for full identifier match
full_id = f'{repo.get("provider")}/{repo.get("account")}/{repo.get("repository")}' full_id = f'{repo.get("provider")}/{repo.get("account")}/{repo.get("repository")}'
if ident == full_id: if ident == full_id:
matches.append(repo) matches.append(repo)
# Or if repository name matches and it is unique, accept it
elif ident == repo.get("repository"): elif ident == repo.get("repository"):
# Only add if it is unique among all_repos # Only add if it is unique among all_repos
if sum(1 for r in all_repos if r.get("repository") == ident) == 1: 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) selected.extend(matches)
return selected 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.""" """Create an executable bash wrapper for the repository."""
repo_identifier = get_repo_identifier(repo, all_repos) repo_identifier = get_repo_identifier(repo, all_repos)
repo_dir = os.path.join(base_dir, repo.get("provider"), repo.get("account"), repo.get("repository")) 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) os.chmod(alias_path, 0o755)
print(f"Installed executable for {repo_identifier} at {alias_path}") 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.""" """Install one or more repositories by creating their executable wrappers and running setup."""
for repo in selected_repos: for repo in selected_repos:
repo_identifier = get_repo_identifier(repo, all_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): if not os.path.exists(repo_dir):
print(f"Repository directory '{repo_dir}' does not exist. Clone it first.") print(f"Repository directory '{repo_dir}' does not exist. Clone it first.")
continue 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 # Execute setup command if defined
setup_cmd = repo.get("setup") setup_cmd = repo.get("setup")
if setup_cmd: if setup_cmd:
run_command(setup_cmd, cwd=repo_dir, preview=preview) 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.""" """Run 'git pull' in the repository directory."""
for repo in selected_repos: for repo in selected_repos:
repo_identifier = get_repo_identifier(repo, all_repos) repo_identifier = get_repo_identifier(repo, all_repos)
@@ -173,7 +180,7 @@ def pull_repos(selected_repos, base_dir, preview=False):
else: else:
print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") 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. """Clone repositories based on the config.
Uses replacement if defined; otherwise, constructs URL from provider/account/repository. 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) os.makedirs(parent_dir, exist_ok=True)
run_command(f"git clone {clone_url} {repo_dir}", cwd=parent_dir, preview=preview) 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.""" """Run 'git push' in the repository directory."""
for repo in selected_repos: for repo in selected_repos:
repo_identifier = get_repo_identifier(repo, all_repos) repo_identifier = get_repo_identifier(repo, all_repos)
@@ -201,7 +208,7 @@ def push_repos(selected_repos, base_dir, preview=False):
else: else:
print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") 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.""" """Remove the executable wrapper and run teardown command if defined."""
for repo in selected_repos: for repo in selected_repos:
repo_identifier = get_repo_identifier(repo, all_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): if teardown_cmd and os.path.exists(repo_dir):
run_command(teardown_cmd, cwd=repo_dir, preview=preview) 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).""" """Delete the repository directory (rm -rv equivalent)."""
for repo in selected_repos: for repo in selected_repos:
repo_identifier = get_repo_identifier(repo, all_repos) repo_identifier = get_repo_identifier(repo, all_repos)
@@ -234,16 +241,16 @@ def delete_repos(selected_repos, base_dir, preview=False):
else: else:
print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") 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.""" """Combine pull and install. If system_update is True, run system update commands."""
pull_repos(selected_repos, base_dir, preview=preview) pull_repos(selected_repos, base_dir, all_repos, preview=preview)
install_repos(selected_repos, base_dir, bin_dir, preview=preview) install_repos(selected_repos, base_dir, bin_dir, all_repos, preview=preview)
if system_update: if system_update:
# Example system update commands (for Arch-based systems) # Example system update commands (for Arch-based systems)
run_command("yay -S", preview=preview) run_command("yay -S", preview=preview)
run_command("sudo pacman -Syyu", 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. """Show status information for repositories.
If list_only is True, only list affected repositories. If list_only is True, only list affected repositories.
If system_status is True, show system update status. 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: if system_status:
print("System status:") print("System status:")
run_command("yay -Qu", preview=preview) run_command("yay -Qu", preview=preview)
# You might want to add additional system status commands here.
for repo in selected_repos: for repo in selected_repos:
repo_identifier = get_repo_identifier(repo, all_repos) repo_identifier = get_repo_identifier(repo, all_repos)
if list_only: if list_only:
@@ -264,11 +270,50 @@ def status_repos(selected_repos, base_dir, list_only=False, system_status=False,
else: else:
print(f"Repository directory '{repo_dir}' not found for {repo_identifier}.") 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__": if __name__ == "__main__":
# Load configuration # Load configuration
config = load_config(CONFIG_PATH) config = load_config(CONFIG_PATH)
base_dir = config["base"] base_dir = config["base"]
all_repos = config["repos"] all_repos_list = config["repos"]
parser = argparse.ArgumentParser(description="Package Manager") parser = argparse.ArgumentParser(description="Package Manager")
subparsers = parser.add_subparsers(dest="command", help="Subcommands") subparsers = parser.add_subparsers(dest="command", help="Subcommands")
@@ -314,30 +359,56 @@ if __name__ == "__main__":
add_identifier_arguments(status_parser) add_identifier_arguments(status_parser)
status_parser.add_argument("--system", action="store_true", help="Show system status") 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() args = parser.parse_args()
# Determine which repositories to operate on # Handle "config" command separately
if args.all or (hasattr(args, "identifiers") and not args.identifiers): if args.command == "config":
selected = all_repos 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: else:
selected = resolve_repos(args.identifiers, all_repos) selected = []
# Dispatch commands # Dispatch commands
if args.command == "install": 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": 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": 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": 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": 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": 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": 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": 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: else:
parser.print_help() parser.print_help()