2025-03-06 12:40:50 +01:00
#!/usr/bin/env python3
2025-03-04 13:17:57 +01:00
import os
import yaml
2025-03-05 09:11:45 +01:00
import argparse
2025-03-05 11:20:59 +01:00
import json
2025-03-06 12:40:50 +01:00
import os
# Ensure the current working directory is the script’ s directory
os . chdir ( os . path . dirname ( os . path . realpath ( __file__ ) ) )
2025-03-04 14:51:31 +01:00
# Define configuration file paths.
2025-03-06 12:40:50 +01:00
USER_CONFIG_PATH = os . path . join ( os . path . dirname ( os . path . realpath ( __file__ ) ) , " config " , " config.yaml " )
2025-03-04 13:17:57 +01:00
BIN_DIR = os . path . expanduser ( " ~/.local/bin " )
2025-03-06 11:10:11 +01:00
from pkgmgr . clone_repos import clone_repos
from pkgmgr . config_init import config_init
2025-03-06 12:07:55 +01:00
from pkgmgr . create_ink import create_ink
2025-03-06 11:10:11 +01:00
from pkgmgr . deinstall_repos import deinstall_repos
from pkgmgr . delete_repos import delete_repos
from pkgmgr . exec_git_command import exec_git_command
from pkgmgr . filter_ignored import filter_ignored
from pkgmgr . generate_alias import generate_alias
from pkgmgr . get_repo_dir import get_repo_dir
from pkgmgr . get_repo_identifier import get_repo_identifier
from pkgmgr . get_selected_repos import get_selected_repos
from pkgmgr . install_repos import install_repos
from pkgmgr . interactive_add import interactive_add
from pkgmgr . list_repositories import list_repositories
from pkgmgr . load_config import load_config
from pkgmgr . resolve_repos import resolve_repos
from pkgmgr . run_command import run_command
from pkgmgr . save_user_config import save_user_config
from pkgmgr . show_config import show_config
from pkgmgr . status_repos import status_repos
from pkgmgr . update_repos import update_repos
2025-03-05 09:11:45 +01:00
# Commands proxied by package-manager
GIT_DEFAULT_COMMANDS = [
" pull " ,
" push " ,
" diff " ,
" add " ,
" show " ,
" checkout " ,
" clone " ,
2025-03-05 11:48:32 +01:00
" reset " ,
2025-03-05 14:19:08 +01:00
" revert " ,
" commit "
2025-03-05 09:11:45 +01:00
]
2025-03-04 15:41:39 +01:00
# Main program.
2025-03-04 13:17:57 +01:00
if __name__ == " __main__ " :
2025-03-06 11:10:11 +01:00
config_merged = load_config ( USER_CONFIG_PATH )
2025-03-05 11:20:59 +01:00
repositories_base_dir = os . path . expanduser ( config_merged [ " directories " ] [ " repositories " ] )
all_repos_list = config_merged [ " repositories " ]
2025-03-05 10:03:20 +01:00
description_text = """ \
2025-03-05 10:26:01 +01:00
\033 [ 1 ; 32 mPackage Manager 🤖 📦 \033 [ 0 m
2025-03-05 14:26:43 +01:00
\033 [ 3 mKevin ' s Package Manager ist drafted by and designed for:
\033 [ 1 ; 34 mKevin Veen - Birkenbach
\033 [ 0 m \033 [ 4 mhttps : / / www . veen . world / \033 [ 0 m
2025-03-04 13:17:57 +01:00
2025-03-05 10:26:01 +01:00
\033 [ 1 mOverview : \033 [ 0 m
2025-03-05 10:03:20 +01:00
A configurable Python tool to manage multiple repositories via a unified command - line interface .
2025-03-05 14:26:43 +01:00
This tool automates common Git operations ( clone , pull , push , status , etc . ) and creates executable wrappers and custom aliases to simplify your workflow .
2025-03-05 10:03:20 +01:00
2025-03-05 10:26:01 +01:00
\033 [ 1 mFeatures : \033 [ 0 m
• \033 [ 1 ; 33 mAuto - install & Setup : \033 [ 0 m Automatically detect and set up repositories .
• \033 [ 1 ; 33 mGit Command Integration : \033 [ 0 m Execute Git commands with extra parameters .
• \033 [ 1 ; 33 mExplorer & Terminal Support : \033 [ 0 m Open repositories in your file manager or a new terminal tab .
• \033 [ 1 ; 33 mComprehensive Configuration : \033 [ 0 m Manage settings via YAML files ( default & user - specific ) .
2025-03-04 13:17:57 +01:00
2025-03-05 10:03:20 +01:00
For detailed help on each command , use :
2025-03-05 10:26:01 +01:00
\033 [ 1 m pkgmgr < command > - - help \033 [ 0 m
2025-03-05 10:03:20 +01:00
"""
2025-03-05 10:26:01 +01:00
2025-03-05 10:03:20 +01:00
parser = argparse . ArgumentParser ( description = description_text , formatter_class = argparse . RawTextHelpFormatter )
subparsers = parser . add_subparsers ( dest = " command " , help = " Subcommands " )
2025-03-04 13:17:57 +01:00
def add_identifier_arguments ( subparser ) :
subparser . add_argument ( " identifiers " , nargs = " * " , help = " Identifier(s) for repositories " )
2025-03-06 12:07:55 +01:00
subparser . add_argument (
" --all " ,
action = " store_true " ,
default = False ,
2025-03-06 12:14:43 +01:00
help = " Apply the subcommand to all repositories in the config. Some subcommands ask for confirmation. If you want to give this confirmation for all repositories, pipe ' yes ' . E.g: yes | pkgmgr {subcommand} --all "
2025-03-06 12:07:55 +01:00
)
2025-03-04 13:17:57 +01:00
subparser . add_argument ( " --preview " , action = " store_true " , help = " Preview changes without executing commands " )
subparser . add_argument ( " --list " , action = " store_true " , help = " List affected repositories (with preview or status) " )
2025-03-05 19:09:27 +01:00
subparser . add_argument ( " -a " , " --args " , nargs = argparse . REMAINDER , dest = " extra_args " , help = " Additional parameters to be forwarded e.g. to the git command " , default = [ ] )
2025-03-04 13:17:57 +01:00
2025-03-06 12:07:55 +01:00
install_parser = subparsers . add_parser ( " install " , help = " Setup repository/repositories alias links to executables " )
2025-03-04 13:17:57 +01:00
add_identifier_arguments ( install_parser )
2025-03-04 16:46:23 +01:00
install_parser . add_argument ( " -q " , " --quiet " , action = " store_true " , help = " Suppress warnings and info messages " )
2025-03-05 09:11:45 +01:00
install_parser . add_argument ( " --no-verification " , default = False , action = " store_true " , help = " Disable verification of repository commit " )
2025-03-04 13:17:57 +01:00
2025-03-06 12:07:55 +01:00
deinstall_parser = subparsers . add_parser ( " deinstall " , help = " Remove alias links to repository/repositories " )
2025-03-04 13:17:57 +01:00
add_identifier_arguments ( deinstall_parser )
2025-03-06 12:07:55 +01:00
delete_parser = subparsers . add_parser ( " delete " , help = " Delete repository/repositories alias links to executables " )
2025-03-04 13:17:57 +01:00
add_identifier_arguments ( delete_parser )
update_parser = subparsers . add_parser ( " update " , help = " Update (pull + install) repository/repositories " )
add_identifier_arguments ( update_parser )
update_parser . add_argument ( " --system " , action = " store_true " , help = " Include system update commands " )
2025-03-04 16:46:23 +01:00
update_parser . add_argument ( " -q " , " --quiet " , action = " store_true " , help = " Suppress warnings and info messages " )
2025-03-05 09:11:45 +01:00
update_parser . add_argument ( " --no-verification " , action = " store_true " , default = False , help = " Disable verification of repository commit " )
2025-03-04 13:17:57 +01:00
status_parser = subparsers . add_parser ( " status " , help = " Show status for repository/repositories or system " )
add_identifier_arguments ( status_parser )
status_parser . add_argument ( " --system " , action = " store_true " , help = " Show system status " )
2025-03-04 14:11:56 +01:00
config_parser = subparsers . add_parser ( " config " , help = " Manage configuration " )
config_subparsers = config_parser . add_subparsers ( dest = " subcommand " , help = " Config subcommands " , required = True )
config_show = config_subparsers . add_parser ( " show " , help = " Show configuration " )
add_identifier_arguments ( config_show )
config_add = config_subparsers . add_parser ( " add " , help = " Interactively add a new repository entry " )
config_edit = config_subparsers . add_parser ( " edit " , help = " Edit configuration file with nano " )
2025-03-04 15:08:25 +01:00
config_init_parser = config_subparsers . add_parser ( " init " , help = " Initialize user configuration by scanning the base directory " )
2025-03-04 15:58:37 +01:00
config_delete = config_subparsers . add_parser ( " delete " , help = " Delete repository entry from user config " )
add_identifier_arguments ( config_delete )
config_ignore = config_subparsers . add_parser ( " ignore " , help = " Set ignore flag for repository entries in user config " )
add_identifier_arguments ( config_ignore )
config_ignore . add_argument ( " --set " , choices = [ " true " , " false " ] , required = True , help = " Set ignore to true or false " )
2025-03-04 17:04:33 +01:00
path_parser = subparsers . add_parser ( " path " , help = " Print the path(s) of repository/repositories " )
add_identifier_arguments ( path_parser )
2025-03-05 09:33:16 +01:00
explor_parser = subparsers . add_parser ( " explor " , help = " Open repository in Nautilus file manager " )
add_identifier_arguments ( explor_parser )
terminal_parser = subparsers . add_parser ( " terminal " , help = " Open repository in a new GNOME Terminal tab " )
add_identifier_arguments ( terminal_parser )
2025-03-05 11:20:59 +01:00
code_parser = subparsers . add_parser ( " code " , help = " Open repository workspace with VS Code " )
add_identifier_arguments ( code_parser )
2025-03-04 16:46:23 +01:00
2025-03-05 10:03:20 +01:00
list_parser = subparsers . add_parser ( " list " , help = " List all repositories with details and status " )
list_parser . add_argument ( " --search " , default = " " , help = " Filter repositories that contain the given string " )
list_parser . add_argument ( " --status " , type = str , default = " " , help = " Filter repositories by status (case insensitive) " )
2025-03-05 09:11:45 +01:00
# Proxies the default git commands
for git_command in GIT_DEFAULT_COMMANDS :
add_identifier_arguments (
2025-03-05 11:48:32 +01:00
subparsers . add_parser (
git_command ,
help = f " Proxies ' git { git_command } ' to one repository/repositories " ,
description = f " Executes ' git { git_command } ' for the identified repos. \n To recieve more help execute ' git { git_command } --help ' " ,
formatter_class = argparse . RawTextHelpFormatter
)
2025-03-05 09:11:45 +01:00
)
2025-03-04 13:17:57 +01:00
2025-03-04 14:11:56 +01:00
args = parser . parse_args ( )
2025-03-04 13:43:23 +01:00
2025-03-04 15:41:39 +01:00
# Dispatch commands.
2025-03-04 14:11:56 +01:00
if args . command == " install " :
2025-03-05 11:48:32 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
install_repos ( selected , repositories_base_dir , BIN_DIR , all_repos_list , args . no_verification , preview = args . preview , quiet = args . quiet )
2025-03-05 09:11:45 +01:00
elif args . command in GIT_DEFAULT_COMMANDS :
2025-03-06 12:07:55 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
2025-03-05 09:11:45 +01:00
if args . command == " clone " :
2025-03-05 11:20:59 +01:00
clone_repos ( selected , repositories_base_dir , all_repos_list , args . preview )
2025-03-06 12:07:55 +01:00
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 )
2025-03-05 09:11:45 +01:00
else :
2025-03-06 12:07:55 +01:00
exec_git_command ( selected , repositories_base_dir , all_repos_list , args . command , args . extra_args , args . preview )
2025-03-05 10:03:20 +01:00
elif args . command == " list " :
2025-03-05 11:20:59 +01:00
list_repositories ( all_repos_list , repositories_base_dir , BIN_DIR , search_filter = args . search , status_filter = args . status )
2025-03-04 13:17:57 +01:00
elif args . command == " deinstall " :
2025-03-05 11:48:32 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
deinstall_repos ( selected , repositories_base_dir , BIN_DIR , all_repos_list , preview = args . preview )
2025-03-04 13:17:57 +01:00
elif args . command == " delete " :
2025-03-05 11:48:32 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
delete_repos ( selected , repositories_base_dir , all_repos_list , preview = args . preview )
2025-03-04 13:17:57 +01:00
elif args . command == " update " :
2025-03-05 11:48:32 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
update_repos ( selected , repositories_base_dir , BIN_DIR , all_repos_list , args . no_verification , system_update = args . system , preview = args . preview , quiet = args . quiet )
2025-03-04 13:17:57 +01:00
elif args . command == " status " :
2025-03-05 11:48:32 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
status_repos ( selected , repositories_base_dir , all_repos_list , args . extra_args , list_only = args . list , system_status = args . system , preview = args . preview )
2025-03-05 09:33:16 +01:00
elif args . command == " explor " :
2025-03-05 11:48:32 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
2025-03-05 09:33:16 +01:00
for repo in selected :
2025-03-05 11:20:59 +01:00
repo_dir = get_repo_dir ( repositories_base_dir , repo )
2025-03-05 09:33:16 +01:00
run_command ( f " nautilus { repo_dir } " )
2025-03-05 11:20:59 +01:00
elif args . command == " code " :
2025-03-05 11:50:54 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
2025-03-05 11:20:59 +01:00
if not selected :
print ( " No repositories selected. " )
else :
identifiers = [ get_repo_identifier ( repo , all_repos_list ) for repo in selected ]
sorted_identifiers = sorted ( identifiers )
workspace_name = " _ " . join ( sorted_identifiers ) + " .code-workspace "
workspaces_dir = os . path . expanduser ( config_merged . get ( " directories " ) . get ( " workspaces " ) )
os . makedirs ( workspaces_dir , exist_ok = True )
workspace_file = os . path . join ( workspaces_dir , workspace_name )
folders = [ ]
for repo in selected :
repo_dir = os . path . expanduser ( get_repo_dir ( repositories_base_dir , repo ) )
folders . append ( { " path " : repo_dir } )
workspace_data = {
" folders " : folders ,
" settings " : { }
}
2025-03-05 13:54:19 +01:00
if not os . path . exists ( workspace_file ) :
with open ( workspace_file , " w " ) as f :
json . dump ( workspace_data , f , indent = 4 )
print ( f " Created workspace file: { workspace_file } " )
else :
print ( f " Using existing workspace file: { workspace_file } " )
2025-03-05 11:20:59 +01:00
run_command ( f ' code " { workspace_file } " ' )
2025-03-05 09:33:16 +01:00
elif args . command == " terminal " :
2025-03-05 11:48:32 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
2025-03-05 09:33:16 +01:00
for repo in selected :
2025-03-05 11:20:59 +01:00
repo_dir = get_repo_dir ( repositories_base_dir , repo )
2025-03-05 09:33:16 +01:00
run_command ( f ' gnome-terminal --tab --working-directory= " { repo_dir } " ' )
2025-03-04 17:04:33 +01:00
elif args . command == " path " :
2025-03-05 11:48:32 +01:00
selected = get_selected_repos ( args . all , all_repos_list , args . identifiers )
2025-03-04 17:04:33 +01:00
paths = [
2025-03-05 11:20:59 +01:00
get_repo_dir ( repositories_base_dir , repo )
2025-03-04 17:04:33 +01:00
for repo in selected
]
print ( " " . join ( paths ) )
2025-03-04 14:11:56 +01:00
elif args . command == " config " :
if args . subcommand == " show " :
if args . all or ( not args . identifiers ) :
2025-03-06 11:10:11 +01:00
show_config ( [ ] , USER_CONFIG_PATH , full_config = True )
2025-03-04 14:11:56 +01:00
else :
selected = resolve_repos ( args . identifiers , all_repos_list )
if selected :
2025-03-06 11:10:11 +01:00
show_config ( selected , USER_CONFIG_PATH , full_config = False )
2025-03-04 14:11:56 +01:00
elif args . subcommand == " add " :
2025-03-04 15:41:39 +01:00
interactive_add ( config_merged )
2025-03-04 14:11:56 +01:00
elif args . subcommand == " edit " :
2025-03-06 11:10:11 +01:00
""" Open the user configuration file in nano. """
run_command ( f " nano { USER_CONFIG_PATH } " )
2025-03-04 15:08:25 +01:00
elif args . subcommand == " init " :
if os . path . exists ( USER_CONFIG_PATH ) :
with open ( USER_CONFIG_PATH , ' r ' ) as f :
user_config = yaml . safe_load ( f ) or { }
else :
2025-03-05 11:20:59 +01:00
user_config = { " repositories " : [ ] }
2025-03-04 15:41:39 +01:00
config_init ( user_config , config_merged , BIN_DIR )
2025-03-04 15:58:37 +01:00
elif args . subcommand == " delete " :
# Load user config from USER_CONFIG_PATH.
if os . path . exists ( USER_CONFIG_PATH ) :
with open ( USER_CONFIG_PATH , ' r ' ) as f :
2025-03-05 11:20:59 +01:00
user_config = yaml . safe_load ( f ) or { " repositories " : [ ] }
2025-03-04 15:58:37 +01:00
else :
2025-03-05 11:20:59 +01:00
user_config = { " repositories " : [ ] }
2025-03-04 15:58:37 +01:00
if args . all or not args . identifiers :
print ( " You must specify identifiers to delete. " )
else :
2025-03-05 11:20:59 +01:00
to_delete = resolve_repos ( args . identifiers , user_config . get ( " repositories " , [ ] ) )
new_repos = [ entry for entry in user_config . get ( " repositories " , [ ] ) if entry not in to_delete ]
user_config [ " repositories " ] = new_repos
2025-03-04 15:58:37 +01:00
save_user_config ( user_config )
print ( f " Deleted { len ( to_delete ) } entries from user config. " )
elif args . subcommand == " ignore " :
# Load user config from USER_CONFIG_PATH.
if os . path . exists ( USER_CONFIG_PATH ) :
with open ( USER_CONFIG_PATH , ' r ' ) as f :
2025-03-05 11:20:59 +01:00
user_config = yaml . safe_load ( f ) or { " repositories " : [ ] }
2025-03-04 15:58:37 +01:00
else :
2025-03-05 11:20:59 +01:00
user_config = { " repositories " : [ ] }
2025-03-04 15:58:37 +01:00
if args . all or not args . identifiers :
print ( " You must specify identifiers to modify ignore flag. " )
else :
2025-03-05 11:20:59 +01:00
to_modify = resolve_repos ( args . identifiers , user_config . get ( " repositories " , [ ] ) )
for entry in user_config [ " repositories " ] :
2025-03-04 15:58:37 +01:00
key = ( entry . get ( " provider " ) , entry . get ( " account " ) , entry . get ( " repository " ) )
for mod in to_modify :
mod_key = ( mod . get ( " provider " ) , mod . get ( " account " ) , mod . get ( " repository " ) )
if key == mod_key :
entry [ " ignore " ] = ( args . set == " true " )
print ( f " Set ignore for { key } to { entry [ ' ignore ' ] } " )
save_user_config ( user_config )
2025-03-04 13:17:57 +01:00
else :
parser . print_help ( )