#!/usr/bin/env python3 # # Ferit Yiğit BALABAN , 2022 # import os.path from subprocess import PIPE, run import sys from datetime import datetime as dt from termcolor import colored from termcolor import cprint DOTFILES_REPOSITORY = '$HOME/repos/dotfiles' REMOTE_REPOSITORY = 'https://github.com/fybalaban/dotfiles' LOCAL_CONFIG = '$HOME/.config' VER = 'v1.1' help_message = f''' dotman {VER} dotfiles manager by fyb Unrecognized keys are ignored. If every key supplied is unrecognized, this have the same effect as calling dotman without any key. Keys: -i, --interactive Interactively backup or deploy dotfiles. Not supplying any key will result in interactive mode. -b, --backup Backup your dotfiles. Doesn't require user assistance but errors may occur. -d, --deploy Deploy your dotfiles. Doesn't require user assistance but errors may occur. -v, --version Shows the version and quits -h, --help Shows this message and quits ''' def remove_from_left_until_slash(text): iterator = len(text) - 1 buffer = '' while text[iterator] != '/': buffer += text[iterator] iterator -= 1 return text.removesuffix(buffer[::-1]) def proc(command, cwd=''): if cwd == '': r = run(command, shell=True, capture_output=True, stdout=PIPE, text=True) else: r = run(command, shell=True, capture_output=True, stdout=PIPE, text=True, cwd=cwd) return r.returncode, r.stdout def prclr(text, color): cprint(text, color, end='') def grab_dotfiles(): os.mkdir(remove_from_left_until_slash(DOTFILES_REPOSITORY)) code, output = proc(f'git clone {REMOTE_REPOSITORY}', DOTFILES_REPOSITORY) return code == 0, code def copy(source, dest, method='subprocess'): if method == 'subprocess': proc(f'cp -av {source} {dest}') def special_copy(source, dest): whitelist = [ 'fish', 'gtk-2.0', 'gtk-3.0', 'htop', 'i3', 'kitty', 'neofetch', 'nitrogen', 'picom', 'polybar', 'rofi' ] dirs = os.listdir(source) full_dirs = [] for each_name in dirs: if each_name not in whitelist: dirs.remove(each_name) elif source.endswith('/'): full_dirs.append(source + each_name) else: full_dirs.append(source + '/' + each_name) for directory in full_dirs: copy(directory, dest, 'subprocess') def git_commit(): flag_commit = True code, output = proc('/usr/bin/git add .', DOTFILES_REPOSITORY) flag_commit = flag_commit or 'nothing to commit' in output if flag_commit: date = dt.now().strftime('%d.%m.%Y %H.%M') proc(f'/usr/bin/git commit -m "dotman {date}"', DOTFILES_REPOSITORY) def push_remote(): code, output = proc('/usr/bin/git push', DOTFILES_REPOSITORY) return code == 0, code def main(): global DOTFILES_REPOSITORY global LOCAL_CONFIG DOTFILES_REPOSITORY = os.path.expandvars(DOTFILES_REPOSITORY) LOCAL_CONFIG = os.path.expandvars(LOCAL_CONFIG) remote_shortname = REMOTE_REPOSITORY.removeprefix("https://github.com/") local_repo_exists = os.path.exists(DOTFILES_REPOSITORY) flag_interactive = False flag_backup = False flag_deploy = False flag_version = False flag_help = False sys.argv.remove(sys.argv[0]) sys.argv.reverse() if len(sys.argv) != 0: while len(sys.argv) > 0: key = sys.argv.pop() flag_interactive = flag_interactive or key == '-i' or key == '--interactive' flag_backup = flag_backup or key == '-b' or key == '--backup' flag_deploy = flag_deploy or key == '-d' or key == '--deploy' flag_version = flag_version or key == '-v' or key == '--version' flag_help = flag_help or key == '-h' or key == '--help' else: flag_interactive = True if flag_interactive: print(f"dotman {VER} by fyb, 2022") if not local_repo_exists: print(colored('Important warning:', 'red'), 'dotfiles repository cannot be located at: ', colored(DOTFILES_REPOSITORY, 'yellow')) print('Edit DOTFILES_REPOSITORY variable in this script to specify its location') print(f'To grab dotfiles from', colored(f'"{remote_shortname}"', 'yellow'), end='') ans = input('type Y. (y/N): ') if ans.lower() == 'y' or 'yes': grab_successed, exit_code = grab_dotfiles() if grab_successed: print(f'Successfully grabbed dotfiles from', colored(f'"{remote_shortname}"', 'yellow')) else: print(f'git exited with result code:', colored(str(exit_code), 'red'), '. An error may have occured.') exit(128) else: cprint('There isn\'t anything left for dotman to do. Have a nice day!', 'green') exit(0) print('dotman is', colored('ready', 'green'), 'for your command. ') print('You can either backup to remote, or copy local repo to local config (deploy)') ans = input('(B)ackup or (D)eploy: ') if ans.lower() == 'b' or ans.lower() == 'backup': print(colored('Step 1: ', 'magenta'), 'Copy from local config', colored("{LOCAL_CONFIG}", 'yellow'), 'to local repo', colored('"DOTFILES_REPOSITORY"', 'yellow')) special_copy(LOCAL_CONFIG, DOTFILES_REPOSITORY) print(colored('Step 2: ', 'magenta'), f'Create a commit and push to remote repo', colored(f'"{REMOTE_REPOSITORY}"', 'yellow')) git_commit() push_remote() cprint('Backup completed. Have a nice day!', 'green') exit(0) elif ans.lower() == 'd' or ans.lower() == 'deploy': print(colored('Step 1:', 'magenta'), ' Copy from local repo to local config') special_copy(DOTFILES_REPOSITORY, LOCAL_CONFIG) cprint('Deploy completed. Have a nice day!', 'green') exit(0) elif flag_backup and not flag_deploy: if local_repo_exists: special_copy(LOCAL_CONFIG, DOTFILES_REPOSITORY) git_commit() push_remote() exit(0) else: print(colored('[CRITICAL]', 'red'), 'Local repository couldn\'t be located. Aborting...') exit(128) elif flag_deploy and not flag_backup: if local_repo_exists: special_copy(DOTFILES_REPOSITORY, LOCAL_CONFIG) exit(0) else: print(colored('[CRITICAL]', 'red'), 'Local repository couldn\'t be located. Aborting...') exit(128) elif flag_version: print(f'dotman version: {VER.removeprefix("v")}') exit(0) elif not flag_deploy and not flag_backup and not flag_interactive or flag_help: print(help_message) exit(0) if __name__ == '__main__': main()