From a5ffdbb799c9c1120867cff4feb7410f9bf59f3c Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Wed, 13 May 2020 06:59:23 -0500 Subject: [PATCH 1/3] Add -dry_run flag Fix #274 --- README.md | 5 +- shallow_backup/__main__.py | 40 +++++---- shallow_backup/backup.py | 135 ++++++++++++++++++------------- shallow_backup/printing.py | 35 ++++++++ shallow_backup/reinstall.py | 91 ++++++++++++--------- tests/test_backups.py | 4 +- tests/test_reinstall_dotfiles.py | 2 +- 7 files changed, 200 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 59cdbe3c..30a5f729 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,9 @@ Options: -delete_config Delete config file. -destroy_backup Delete backup directory. -dotfiles Back up dotfiles. + -dry_run Don't backup or reinstall any files, just give + verbose output. + -fonts Back up installed fonts. -full_backup Full back up. --new_path TEXT Input a new back up directory path. @@ -86,7 +89,7 @@ Options: -show Display config file. -v, --version Display version and author info. - -h, -help, --help Show this message and exit. + -help, -h, --help Show this message and exit. ``` ### Git Integration diff --git a/shallow_backup/__main__.py b/shallow_backup/__main__.py index 9539c797..3de5ba77 100644 --- a/shallow_backup/__main__.py +++ b/shallow_backup/__main__.py @@ -14,8 +14,9 @@ @click.option('--add_dot', default=None, help="Add a dotfile or dotfolder to config by path.") @click.option('-configs', is_flag=True, default=False, help="Back up app config files.") @click.option('-delete_config', is_flag=True, default=False, help="Delete config file.") -@click.option('-destroy_backup', is_flag=True, default=False, help='Delete backup directory.') +@click.option('-destroy_backup', is_flag=True, default=False, help="Delete backup directory.") @click.option('-dotfiles', is_flag=True, default=False, help="Back up dotfiles.") +@click.option('-dry_run', is_flag=True, default=False, help="Don't backup or reinstall any files, just give verbose output.") @click.option('-fonts', is_flag=True, default=False, help="Back up installed fonts.") @click.option('-full_backup', is_flag=True, default=False, help="Full back up.") @click.option('--new_path', default=None, help="Input a new back up directory path.") @@ -31,7 +32,7 @@ @click.option('-separate_dotfiles_repo', is_flag=True, default=False, help="Use if you are trying to maintain a separate dotfiles repo and running into issue #229.") @click.option('-show', is_flag=True, default=False, help="Display config file.") @click.option('--version', '-v', is_flag=True, default=False, help='Display version and author info.') -def cli(add_dot, full_backup, configs, delete_config, destroy_backup, dotfiles, fonts, new_path, +def cli(add_dot, configs, delete_config, destroy_backup, dotfiles, dry_run, fonts, full_backup, new_path, no_splash, old_path, packages, reinstall_all, reinstall_configs, reinstall_dots, reinstall_fonts, reinstall_packages, remote, separate_dotfiles_repo, show, version): @@ -115,30 +116,35 @@ def cli(add_dot, full_backup, configs, delete_config, destroy_backup, dotfiles, # Command line options if skip_prompt: if reinstall_packages: - reinstall_packages_sb(packages_path) + reinstall_packages_sb(packages_path, dry_run=dry_run) elif reinstall_configs: - reinstall_configs_sb(configs_path) + reinstall_configs_sb(configs_path, dry_run=dry_run) elif reinstall_fonts: - reinstall_fonts_sb(fonts_path) + reinstall_fonts_sb(fonts_path, dry_run=dry_run) elif reinstall_dots: - reinstall_dots_sb(dotfiles_path) + reinstall_dots_sb(dotfiles_path, dry_run=dry_run) elif reinstall_all: - reinstall_all_sb(dotfiles_path, packages_path, fonts_path, configs_path) + reinstall_all_sb(dotfiles_path, packages_path, fonts_path, configs_path, dry_run=dry_run) elif full_backup: - backup_all(dotfiles_path, packages_path, fonts_path, configs_path, skip=True) - git_add_all_commit_push(repo, "full_backup") + backup_all(dotfiles_path, packages_path, fonts_path, configs_path, dry_run=dry_run, skip=True) + if not dry_run: + git_add_all_commit_push(repo, "full_backup") elif dotfiles: - backup_dotfiles(dotfiles_path, skip=True) - git_add_all_commit_push(repo, "dotfiles", separate_dotfiles_repo) + backup_dotfiles(dotfiles_path, dry_run=dry_run, skip=True) + if not dry_run: + git_add_all_commit_push(repo, "dotfiles", separate_dotfiles_repo) elif configs: - backup_configs(configs_path, skip=True) - git_add_all_commit_push(repo, "configs") + backup_configs(configs_path, dry_run=dry_run, skip=True) + if not dry_run: + git_add_all_commit_push(repo, "configs") elif packages: - backup_packages(packages_path, skip=True) - git_add_all_commit_push(repo, "packages") + backup_packages(packages_path, dry_run=dry_run, skip=True) + if not dry_run: + git_add_all_commit_push(repo, "packages") elif fonts: - backup_fonts(fonts_path, skip=True) - git_add_all_commit_push(repo, "fonts") + backup_fonts(fonts_path, dry_run=dry_run, skip=True) + if not dry_run: + git_add_all_commit_push(repo, "fonts") # No CL options, show action menu and process selected option. else: selection = main_menu_prompt().lower().strip() diff --git a/shallow_backup/backup.py b/shallow_backup/backup.py index 5c7016fa..07efc7dc 100644 --- a/shallow_backup/backup.py +++ b/shallow_backup/backup.py @@ -2,6 +2,7 @@ from shlex import quote from colorama import Fore import multiprocessing as mp +from pathlib import Path from shutil import copyfile from .utils import * from .printing import * @@ -9,16 +10,18 @@ from .config import get_config -def backup_dotfiles(backup_dest_path, home_path=os.path.expanduser("~"), skip=False): +def backup_dotfiles(backup_dest_path, dry_run=False, home_path=os.path.expanduser("~"), skip=False): """ Create `dotfiles` dir and makes copies of dotfiles and dotfolders. Assumes that dotfiles are stored in the home directory. :param skip: Boolean flag to skip prompting for overwrite. Used for scripting. :param backup_dest_path: Destination path for dotfiles. Like, ~/shallow-backup/dotfiles. Used in tests. :param home_path: Path where dotfiles will be found. $HOME by default. + :param dry_run: Flag for determining if debug info should be shown, or copying should occur. """ print_section_header("DOTFILES", Fore.BLUE) - overwrite_dir_prompt_if_needed(backup_dest_path, skip) + if not dry_run: + overwrite_dir_prompt_if_needed(backup_dest_path, skip) # get dotfolders and dotfiles config = get_config()["dotfiles"] @@ -56,6 +59,18 @@ def backup_dotfiles(backup_dest_path, home_path=os.path.expanduser("~"), skip=Fa else: dotfiles_mp_in.append(path_pair) + # Print source -> dest and skip the copying step + if dry_run: + print_yellow_bold("Dotfiles:") + for source, dest in dotfiles_mp_in: + print_dry_run_copy_info(source, dest) + + print_yellow_bold("\nDotfolders:") + for source, dest in dotfolders_mp_in: + print_dry_run_copy_info(source, dest) + + return + # Fix https://github.com/alichtman/shallow-backup/issues/230 for dest_path in [path_pair[1] for path_pair in dotfiles_mp_in + dotfolders_mp_in]: print(f"Creating: {os.path.split(dest_path)[0]}") @@ -75,7 +90,7 @@ def backup_dotfiles(backup_dest_path, home_path=os.path.expanduser("~"), skip=Fa p.join() -def backup_configs(backup_path, skip=False): +def backup_configs(backup_path, dry_run: bool = False, skip=False): """ Creates `configs` directory and places config backups there. Configs are application settings, generally. .plist files count. @@ -83,69 +98,79 @@ def backup_configs(backup_path, skip=False): path relative to the configs/ directory. """ print_section_header("CONFIGS", Fore.BLUE) - overwrite_dir_prompt_if_needed(backup_path, skip) + if not dry_run: + overwrite_dir_prompt_if_needed(backup_path, skip) config = get_config() print_blue_bold("Backing up configs...") # backup config files + dirs in backup_path// - for path_to_backup, target in config["config_mapping"].items(): - print("BACKUP:", path_to_backup) - print("TARGET:", target) + for config_path, target in config["config_mapping"].items(): + dest = os.path.join(backup_path, target) - if os.path.isdir(path_to_backup): - copytree(path_to_backup, quote(dest), symlinks=True) - elif os.path.isfile(path_to_backup): - parent_dir = dest[:dest.rfind("/")] + + if dry_run: + print_dry_run_copy_info(config_path, dest) + continue + + quoted_dest = quote(dest) + if os.path.isdir(config_path): + copytree(config_path, quoted_dest, symlinks=True) + elif os.path.isfile(config_path): + parent_dir = Path(dest).parent safe_mkdir(parent_dir) - copyfile(path_to_backup, quote(dest)) + copyfile(config_path, quoted_dest) -def backup_packages(backup_path, skip=False): +def backup_packages(backup_path, dry_run: bool = False, skip=False): """ Creates `packages` directory and places install list text files there. """ - print_section_header("PACKAGES", Fore.BLUE) - overwrite_dir_prompt_if_needed(backup_path, skip) + def run_cmd_if_no_dry_run(command, dest, dry_run) -> int: + if dry_run: + print_dry_run_copy_info(f"$ {command}", dest) + # Return -1 for any processes depending on chained successful commands (npm) + return -1 + else: + return run_cmd_write_stdout(command, dest) - std_package_managers = [ - "brew", - "brew cask", - "gem" - ] + print_section_header("PACKAGES", Fore.BLUE) + if not dry_run: + overwrite_dir_prompt_if_needed(backup_path, skip) - for mgr in std_package_managers: + for mgr in ["brew", "brew cask", "gem"]: # deal with package managers that have spaces in them. print_pkg_mgr_backup(mgr) - command = "{} list".format(mgr) - dest = "{}/{}_list.txt".format(backup_path, mgr.replace(" ", "-")) - run_cmd_write_stdout(command, dest) + command = f"{mgr} list" + dest = f"{backup_path}/{mgr.replace(' ', '-')}_list.txt" + run_cmd_if_no_dry_run(command, dest, dry_run) # cargo print_pkg_mgr_backup("cargo") command = "ls {}".format(home_prefix(".cargo/bin/")) - dest = "{}/cargo_list.txt".format(backup_path) - run_cmd_write_stdout(command, dest) + dest = f"{backup_path}/cargo_list.txt" + run_cmd_if_no_dry_run(command, dest, dry_run) # pip print_pkg_mgr_backup("pip") command = "pip list --format=freeze" - dest = "{}/pip_list.txt".format(backup_path) - run_cmd_write_stdout(command, dest) + dest = f"{backup_path}/pip_list.txt" + run_cmd_if_no_dry_run(command, dest, dry_run) # pip3 print_pkg_mgr_backup("pip3") command = "pip3 list --format=freeze" - dest = "{}/pip3_list.txt".format(backup_path) - run_cmd_write_stdout(command, dest) + dest = f"{backup_path}/pip3_list.txt" + run_cmd_if_no_dry_run(command, dest, dry_run) # npm print_pkg_mgr_backup("npm") command = "npm ls --global --parseable=true --depth=0" - temp_file_path = "{}/npm_temp_list.txt".format(backup_path) + temp_file_path = f"{backup_path}/npm_temp_list.txt" # If command is successful, go to the next parsing step. - if run_cmd_write_stdout(command, temp_file_path) == 0: - npm_dest_file = "{0}/npm_list.txt".format(backup_path) + npm_backup_cmd_success = run_cmd_if_no_dry_run(command, dest, dry_run) == 0 + if npm_backup_cmd_success: + npm_dest_file = f"{backup_path}/npm_list.txt" # Parse npm output with open(temp_file_path, mode="r+") as temp_file: # Skip first line of file @@ -158,35 +183,35 @@ def backup_packages(backup_path, skip=False): # atom package manager print_pkg_mgr_backup("Atom") command = "apm list --installed --bare" - dest = "{}/apm_list.txt".format(backup_path) - run_cmd_write_stdout(command, dest) + dest = f"{backup_path}/apm_list.txt" + run_cmd_if_no_dry_run(command, dest, dry_run) # vscode extensions print_pkg_mgr_backup("VSCode") command = "code --list-extensions --show-versions" - dest = "{}/vscode_list.txt".format(backup_path) - run_cmd_write_stdout(command, dest) + dest = f"{backup_path}/vscode_list.txt" + run_cmd_if_no_dry_run(command, dest, dry_run) # macports print_pkg_mgr_backup("macports") command = "port installed requested" - dest = "{}/macports_list.txt".format(backup_path) - run_cmd_write_stdout(command, dest) + dest = f"{backup_path}/macports_list.txt" + run_cmd_if_no_dry_run(command, dest, dry_run) # system installs print_pkg_mgr_backup("System Applications") applications_path = get_applications_dir() command = "ls {}".format(applications_path) - dest = "{}/system_apps_list.txt".format(backup_path) - run_cmd_write_stdout(command, dest) + dest = f"{backup_path}/system_apps_list.txt" + run_cmd_if_no_dry_run(command, dest, dry_run) -def backup_fonts(backup_path, skip=False): - """ - Copies all .ttf and .otf files in ~/Library/Fonts/ to backup/fonts/ +def backup_fonts(backup_path: str, dry_run: bool = False, skip: bool = False): + """Copies all .ttf and .otf files in the to backup/fonts/ """ print_section_header("FONTS", Fore.BLUE) - overwrite_dir_prompt_if_needed(backup_path, skip) + if not dry_run: + overwrite_dir_prompt_if_needed(backup_path, skip) print_blue("Copying '.otf' and '.ttf' fonts...") fonts_path = get_fonts_dir() if os.path.isdir(fonts_path): @@ -194,17 +219,19 @@ def backup_fonts(backup_path, skip=False): font.endswith(".otf") or font.endswith(".ttf")] for font in fonts: + dest = os.path.join(backup_path, font.split("/")[-1]) if os.path.exists(font): - copyfile(font, os.path.join(backup_path, font.split("/")[-1])) + if dry_run: + print_dry_run_copy_info(font, dest) + else: + copyfile(font, dest) else: print_red('Skipping fonts backup. No fonts directory found.') -def backup_all(dotfiles_path, packages_path, fonts_path, configs_path, skip=False): - """ - Complete backup procedure. - """ - backup_dotfiles(dotfiles_path, skip=skip) - backup_packages(packages_path, skip) - backup_fonts(fonts_path, skip) - backup_configs(configs_path, skip) +def backup_all(dotfiles_path, packages_path, fonts_path, configs_path, dry_run=False, skip=False): + """Complete backup procedure.""" + backup_dotfiles(dotfiles_path, dry_run=dry_run, skip=skip) + backup_packages(packages_path, dry_run=dry_run, skip=skip) + backup_fonts(fonts_path, dry_run=dry_run, skip=skip) + backup_configs(configs_path, dry_run=dry_run, skip=skip) diff --git a/shallow_backup/printing.py b/shallow_backup/printing.py index aebe6a08..bcd71c9a 100644 --- a/shallow_backup/printing.py +++ b/shallow_backup/printing.py @@ -1,3 +1,4 @@ +import os import sys import inquirer from colorama import Fore, Style @@ -52,6 +53,40 @@ def print_path_green(text, path): print(Fore.GREEN + Style.BRIGHT + text, Style.NORMAL + path + Style.RESET_ALL) +def print_dry_run_copy_info(source, dest): + """Show source -> dest copy. Replaces expanded ~ with ~ if it's at the beginning of paths. + source and dest are trimmed in the middle if needed. Removed characters will be replaced by ... + :param source: Can be of type str or Path + :param dest: Can be of type str or Path + """ + def shorten_home(path): + expanded_home = os.path.expanduser("~") + path = str(path) + if path.startswith(expanded_home): + return path.replace(expanded_home, "~") + return path + + def truncate_middle(path: str, acceptable_len: int): + """Middle truncate a string + https://www.xormedia.com/string-truncate-middle-with-ellipsis/ + """ + if len(path) <= acceptable_len: + return path + # half of the size, minus the 3 .'s + n_2 = int(acceptable_len / 2 - 3) + # whatever's left + n_1 = int(acceptable_len - n_2 - 3) + return f"{path[:n_1]}...{path[-n_2:]}" + + trimmed_source = shorten_home(source) + trimmed_dest = shorten_home(dest) + longest_allowed_path_len = 87 + if len(trimmed_source) + len(trimmed_dest) > longest_allowed_path_len: + trimmed_source = truncate_middle(trimmed_source, longest_allowed_path_len) + trimmed_dest = truncate_middle(trimmed_dest, longest_allowed_path_len) + print(Fore.YELLOW + Style.BRIGHT + trimmed_source + Style.NORMAL, "->", Style.BRIGHT + trimmed_dest + Style.RESET_ALL) + + def print_version_info(cli=True): """ Formats version differently for CLI and splash screen. diff --git a/shallow_backup/reinstall.py b/shallow_backup/reinstall.py index c6a1d5c3..b52d2be6 100644 --- a/shallow_backup/reinstall.py +++ b/shallow_backup/reinstall.py @@ -11,7 +11,7 @@ # conflict with the function names. -def reinstall_dots_sb(dots_path: str, home_path: str = os.path.expanduser("~")): +def reinstall_dots_sb(dots_path: str, home_path: str = os.path.expanduser("~"), dry_run: bool = False): """Reinstall all dotfiles and folders by copying them from dots_path to a path relative to home_path, or to an absolute path.""" exit_if_dir_is_empty(dots_path, 'dotfile') @@ -42,19 +42,21 @@ def reinstall_dots_sb(dots_path: str, home_path: str = os.path.expanduser("~")): # The absolute file paths prepended with ':' are converted back to valid paths # Format: [(source, dest), ... ] full_path_dotfiles_to_reinstall = [] - for dot in dotfiles_to_reinstall: - source = os.path.join(dots_path, dot) + for source in dotfiles_to_reinstall: # If it's an absolute path, dest is the corrected path - if dot.startswith(":"): - dest = "/" + dot[1:] + if source.startswith(":"): + dest = "/" + source[1:] else: # Otherwise, it should go in a path relative to the home path - relative_dotfile_path = dot.replace(dots_path, "") - dest = os.path.join(home_path, relative_dotfile_path) + dest = source.replace(dots_path, home_path + "/") full_path_dotfiles_to_reinstall.append((Path(source), Path(dest))) # Copy files from backup to system for dot_source, dot_dest in full_path_dotfiles_to_reinstall: + if dry_run: + print_dry_run_copy_info(dot_source, dot_dest) + continue + # Create dest parent dir if it doesn't exist safe_mkdir(dot_dest.parent) try: @@ -67,20 +69,23 @@ def reinstall_dots_sb(dots_path: str, home_path: str = os.path.expanduser("~")): print_section_header("DOTFILE REINSTALLATION COMPLETED", Fore.BLUE) -def reinstall_fonts_sb(fonts_path: str): +def reinstall_fonts_sb(fonts_path: str, dry_run: bool = False): """Reinstall all fonts.""" exit_if_dir_is_empty(fonts_path, 'font') print_section_header("REINSTALLING FONTS", Fore.BLUE) # Copy every file in fonts_path to ~/Library/Fonts for font in get_abs_path_subfiles(fonts_path): - font_lib_path = get_fonts_dir() - dest_path = os.path.join(font_lib_path, font.split("/")[-1]) + fonts_dir = get_fonts_dir() + dest_path = quote(os.path.join(fonts_dir, font.split("/")[-1])) + if dry_run: + print_dry_run_copy_info(font, dest_path) + continue copyfile(quote(font), quote(dest_path)) print_section_header("FONT REINSTALLATION COMPLETED", Fore.BLUE) -def reinstall_configs_sb(configs_path: str): +def reinstall_configs_sb(configs_path: str, dry_run: bool = False): """Reinstall all configs from the backup.""" exit_if_dir_is_empty(configs_path, 'config') print_section_header("REINSTALLING CONFIG FILES", Fore.BLUE) @@ -88,18 +93,30 @@ def reinstall_configs_sb(configs_path: str): config = get_config() for dest_path, backup_loc in config["config_mapping"].items(): dest_path = quote(dest_path) - path_to_backup = quote(os.path.join(configs_path, backup_loc)) - # TODO: REFACTOR WITH GENERIC COPY FUNCTION. - if os.path.isdir(path_to_backup): - copytree(path_to_backup, dest_path) - elif os.path.isfile(path_to_backup): - copyfile(path_to_backup, dest_path) + source_path = quote(os.path.join(configs_path, backup_loc)) + + if dry_run: + print_dry_run_copy_info(source_path, dest_path) + continue + + if os.path.isdir(source_path): + copytree(source_path, dest_path) + elif os.path.isfile(source_path): + copyfile(source_path, dest_path) print_section_header("CONFIG REINSTALLATION COMPLETED", Fore.BLUE) -def reinstall_packages_sb(packages_path: str): +def reinstall_packages_sb(packages_path: str, dry_run: bool = False): """Reinstall all packages from the files in backup/installs.""" + def run_cmd_if_no_dry_run(command, dry_run) -> int: + if dry_run: + print_yellow_bold(f"$ {command}") + # Return 0 for any processes depending on chained successful commands + return 0 + else: + return run_cmd(command) + exit_if_dir_is_empty(packages_path, 'package') print_section_header("REINSTALLING PACKAGES", Fore.BLUE) @@ -121,30 +138,30 @@ def reinstall_packages_sb(packages_path: str): if pm in ["brew", "brew-cask"]: pm_formatted = pm.replace("-", " ") print_pkg_mgr_reinstall(pm_formatted) - cmd = "xargs {0} install < {1}/{2}_list.txt".format(pm.replace("-", " "), packages_path, pm_formatted) - run_cmd(cmd) + cmd = f"xargs {pm.replace('-', ' ')} install < {packages_path}/{pm_formatted}_list.txt" + run_cmd_if_no_dry_run(cmd, dry_run) elif pm == "npm": print_pkg_mgr_reinstall(pm) - cmd = "cat {0}/npm_list.txt | xargs npm install -g".format(packages_path) - run_cmd(cmd) + cmd = f"cat {packages_path}/npm_list.txt | xargs npm install -g" + run_cmd_if_no_dry_run(cmd, dry_run) elif pm == "pip": print_pkg_mgr_reinstall(pm) - cmd = "pip install -r {0}/pip_list.txt".format(packages_path) - run_cmd(cmd) + cmd = f"pip install -r {packages_path}/pip_list.txt" + run_cmd_if_no_dry_run(cmd, dry_run) elif pm == "pip3": print_pkg_mgr_reinstall(pm) - cmd = "pip3 install -r {0}/pip3_list.txt".format(packages_path) - run_cmd(cmd) + cmd = f"pip3 install -r {packages_path}/pip3_list.txt" + run_cmd_if_no_dry_run(cmd, dry_run) elif pm == "vscode": print_pkg_mgr_reinstall(pm) - with open("{0}/vscode_list.txt".format(packages_path), "r") as f: - for x in f: - cmd = "code --install-extension {0}".format(x) - run_cmd(cmd) + with open(f"{packages_path}/vscode_list.txt", "r") as file: + for package in file: + cmd = f"code --install-extension {package}" + run_cmd_if_no_dry_run(cmd, dry_run) elif pm == "apm": print_pkg_mgr_reinstall(pm) - cmd = "apm install --packages-file {0}/apm_list.txt".format(packages_path) - run_cmd(cmd) + cmd = f"apm install --packages-file {packages_path}/apm_list.txt" + run_cmd_if_no_dry_run(cmd, dry_run) elif pm == "macports": print_red_bold("WARNING: Macports reinstallation is not supported.") elif pm == "gem": @@ -156,9 +173,9 @@ def reinstall_packages_sb(packages_path: str): print_section_header("PACKAGE REINSTALLATION COMPLETED", Fore.BLUE) -def reinstall_all_sb(dotfiles_path: str, packages_path: str, fonts_path: str, configs_path: str): +def reinstall_all_sb(dotfiles_path: str, packages_path: str, fonts_path: str, configs_path: str, dry_run: bool = False): """Call all reinstallation methods.""" - reinstall_dots_sb(dotfiles_path) - reinstall_packages_sb(packages_path) - reinstall_fonts_sb(fonts_path) - reinstall_configs_sb(configs_path) + reinstall_dots_sb(dotfiles_path, dry_run=dry_run) + reinstall_packages_sb(packages_path, dry_run=dry_run) + reinstall_fonts_sb(fonts_path, dry_run=dry_run) + reinstall_configs_sb(configs_path, dry_run=dry_run) diff --git a/tests/test_backups.py b/tests/test_backups.py index 2247e090..9f2ddfa9 100644 --- a/tests/test_backups.py +++ b/tests/test_backups.py @@ -39,7 +39,7 @@ def teardown_method(): def test_backup_dotfiles(self): """Test backing up dotfiles and dotfolders.""" backup_dest_path = os.path.join(BACKUP_DEST_DIR, "dotfiles") - backup_dotfiles(backup_dest_path, home_path=FAKE_HOME_DIR, skip=True) + backup_dotfiles(backup_dest_path, dry_run=False, home_path=FAKE_HOME_DIR, skip=True) assert os.path.isdir(backup_dest_path) for path in DOTFILES: print(f"\nBACKUP DESTINATION DIRECTORY: ({backup_dest_path}) CONTENTS:", os.listdir(backup_dest_path), "") @@ -58,7 +58,7 @@ def test_conditions(self): write_config(config) backup_dest_path = os.path.join(BACKUP_DEST_DIR, "dotfiles") - backup_dotfiles(backup_dest_path, home_path=FAKE_HOME_DIR, skip=True) + backup_dotfiles(backup_dest_path, dry_run=False, home_path=FAKE_HOME_DIR, skip=True) assert os.path.isdir(backup_dest_path) for path in DOTFILES: print(f"\nBACKUP DESTINATION DIRECTORY: ({backup_dest_path}) CONTENTS:", os.listdir(backup_dest_path), "") diff --git a/tests/test_reinstall_dotfiles.py b/tests/test_reinstall_dotfiles.py index c0973ff8..c5032dc1 100644 --- a/tests/test_reinstall_dotfiles.py +++ b/tests/test_reinstall_dotfiles.py @@ -64,7 +64,7 @@ def test_reinstall_dotfiles(self): """ Test reinstalling dotfiles to fake home dir """ - reinstall_dots_sb(dots_path=DOTFILES_PATH, home_path=FAKE_HOME_DIR) + reinstall_dots_sb(dots_path=DOTFILES_PATH, home_path=FAKE_HOME_DIR, dry_run=False) assert os.path.isfile(os.path.join(FAKE_HOME_DIR, '.zshenv')) testfolder2 = os.path.join(os.path.join(FAKE_HOME_DIR, '.config/tmux/'), 'testfolder2') assert os.path.isdir(testfolder2) From 3eeb9fabe34bbf2e58ee5d19815586c8ede64756 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Wed, 13 May 2020 07:31:16 -0500 Subject: [PATCH 2/3] Standardize option / argument formats (- instead of _) --- README.md | 52 +++++++++++++++--------------- shallow_backup/__main__.py | 65 +++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 30a5f729..a16899fe 100644 --- a/README.md +++ b/README.md @@ -64,32 +64,32 @@ Usage: shallow-backup [OPTIONS] Written by Aaron Lichtman (@alichtman). Options: - --add_dot TEXT Add a dotfile or dotfolder to config by path. - -configs Back up app config files. - -delete_config Delete config file. - -destroy_backup Delete backup directory. - -dotfiles Back up dotfiles. - -dry_run Don't backup or reinstall any files, just give - verbose output. - - -fonts Back up installed fonts. - -full_backup Full back up. - --new_path TEXT Input a new back up directory path. - -no_splash Don't display splash screen. - -old_path Skip setting new back up directory path prompt. - -packages Back up package libraries. - -reinstall_all Full reinstallation. - -reinstall_configs Reinstall configs. - -reinstall_dots Reinstall dotfiles and dotfolders. - -reinstall_fonts Reinstall fonts. - -reinstall_packages Reinstall packages. - --remote TEXT Set remote URL for the git repo. - -separate_dotfiles_repo Use if you are trying to maintain a separate - dotfiles repo and running into issue #229. - - -show Display config file. - -v, --version Display version and author info. - -help, -h, --help Show this message and exit. + --add-dot TEXT Add a dotfile or dotfolder to config by path. + -backup-all Full back up. + -backup-configs Back up app config files. + -backup-dots Back up dotfiles. + -backup-packages Back up package libraries. + -delete-config Delete config file. + -destroy-backup Delete backup directory. + -dry-run Don't backup or reinstall any files, just give + verbose output. + + -backup-fonts Back up installed fonts. + --new-path TEXT Input a new back up directory path. + -no-new-backup-path-prompt Skip setting new back up directory path prompt. + -no-splash Don't display splash screen. + -reinstall-all Full reinstallation. + -reinstall-configs Reinstall configs. + -reinstall-dots Reinstall dotfiles and dotfolders. + -reinstall-fonts Reinstall fonts. + -reinstall-packages Reinstall packages. + --remote TEXT Set remote URL for the git repo. + -separate-dotfiles-repo Use if you are trying to maintain a separate + dotfiles repo and running into issue #229. + + -show Display config file. + -v, --version Display version and author info. + -h, -help, --help Show this message and exit. ``` ### Git Integration diff --git a/shallow_backup/__main__.py b/shallow_backup/__main__.py index 3de5ba77..0066807d 100644 --- a/shallow_backup/__main__.py +++ b/shallow_backup/__main__.py @@ -11,30 +11,30 @@ # custom help options @click.command(context_settings=dict(help_option_names=['-h', '-help', '--help'])) -@click.option('--add_dot', default=None, help="Add a dotfile or dotfolder to config by path.") -@click.option('-configs', is_flag=True, default=False, help="Back up app config files.") -@click.option('-delete_config', is_flag=True, default=False, help="Delete config file.") -@click.option('-destroy_backup', is_flag=True, default=False, help="Delete backup directory.") -@click.option('-dotfiles', is_flag=True, default=False, help="Back up dotfiles.") -@click.option('-dry_run', is_flag=True, default=False, help="Don't backup or reinstall any files, just give verbose output.") -@click.option('-fonts', is_flag=True, default=False, help="Back up installed fonts.") -@click.option('-full_backup', is_flag=True, default=False, help="Full back up.") -@click.option('--new_path', default=None, help="Input a new back up directory path.") -@click.option('-no_splash', is_flag=True, default=False, help="Don't display splash screen.") -@click.option('-old_path', is_flag=True, default=False, help="Skip setting new back up directory path prompt.") -@click.option('-packages', is_flag=True, default=False, help="Back up package libraries.") -@click.option('-reinstall_all', is_flag=True, default=False, help="Full reinstallation.") -@click.option('-reinstall_configs', is_flag=True, default=False, help="Reinstall configs.") -@click.option('-reinstall_dots', is_flag=True, default=False, help="Reinstall dotfiles and dotfolders.") -@click.option('-reinstall_fonts', is_flag=True, default=False, help="Reinstall fonts.") -@click.option('-reinstall_packages', is_flag=True, default=False, help="Reinstall packages.") +@click.option('--add-dot', default=None, help="Add a dotfile or dotfolder to config by path.") +@click.option('-backup-all', 'backup_all_flag', is_flag=True, default=False, help="Full back up.") +@click.option('-backup-configs', 'backup_configs_flag', is_flag=True, default=False, help="Back up app config files.") +@click.option('-backup-dots', 'backup_dots_flag', is_flag=True, default=False, help="Back up dotfiles.") +@click.option('-backup-packages', 'backup_packages_flag', is_flag=True, default=False, help="Back up package libraries.") +@click.option('-delete-config', is_flag=True, default=False, help="Delete config file.") +@click.option('-destroy-backup', is_flag=True, default=False, help="Delete backup directory.") +@click.option('-dry-run', is_flag=True, default=False, help="Don't backup or reinstall any files, just give verbose output.") +@click.option('-backup-fonts', 'backup_fonts_flag', is_flag=True, default=False, help="Back up installed fonts.") +@click.option('--new-path', default=None, help="Input a new back up directory path.") +@click.option('-no-new-backup-path-prompt', is_flag=True, default=False, help="Skip setting new back up directory path prompt.") +@click.option('-no-splash', is_flag=True, default=False, help="Don't display splash screen.") +@click.option('-reinstall-all', is_flag=True, default=False, help="Full reinstallation.") +@click.option('-reinstall-configs', is_flag=True, default=False, help="Reinstall configs.") +@click.option('-reinstall-dots', is_flag=True, default=False, help="Reinstall dotfiles and dotfolders.") +@click.option('-reinstall-fonts', is_flag=True, default=False, help="Reinstall fonts.") +@click.option('-reinstall-packages', is_flag=True, default=False, help="Reinstall packages.") @click.option('--remote', default=None, help="Set remote URL for the git repo.") -@click.option('-separate_dotfiles_repo', is_flag=True, default=False, help="Use if you are trying to maintain a separate dotfiles repo and running into issue #229.") +@click.option('-separate-dotfiles-repo', is_flag=True, default=False, help="Use if you are trying to maintain a separate dotfiles repo and running into issue #229.") @click.option('-show', is_flag=True, default=False, help="Display config file.") @click.option('--version', '-v', is_flag=True, default=False, help='Display version and author info.') -def cli(add_dot, configs, delete_config, destroy_backup, dotfiles, dry_run, fonts, full_backup, new_path, - no_splash, old_path, packages, reinstall_all, reinstall_configs, - reinstall_dots, reinstall_fonts, reinstall_packages, remote, +def cli(add_dot, backup_configs_flag, delete_config, destroy_backup, backup_dots_flag, dry_run, + backup_fonts_flag, backup_all_flag, new_path, no_splash, no_new_backup_path_prompt, backup_packages_flag, + reinstall_all, reinstall_configs, reinstall_dots, reinstall_fonts, reinstall_packages, remote, separate_dotfiles_repo, show, version): """ \b @@ -48,12 +48,13 @@ def cli(add_dot, configs, delete_config, destroy_backup, dotfiles, dry_run, font # Process CLI args admin_action = any([add_dot, delete_config, destroy_backup, show, version]) - has_cli_arg = any([old_path, full_backup, dotfiles, packages, fonts, configs, - reinstall_dots, reinstall_fonts, reinstall_all, - reinstall_configs, reinstall_packages]) - skip_prompt = any([full_backup, dotfiles, configs, packages, fonts, - reinstall_packages, reinstall_configs, reinstall_dots, - reinstall_fonts]) + has_cli_arg = any([no_new_backup_path_prompt, backup_all_flag, backup_dots_flag, + backup_packages_flag, backup_fonts_flag, backup_configs_flag, + reinstall_dots, reinstall_fonts, reinstall_all, reinstall_configs, + reinstall_packages]) + skip_prompt = any([backup_all_flag, backup_dots_flag, backup_configs_flag, + backup_packages_flag, backup_fonts_flag, reinstall_packages, + reinstall_configs, reinstall_dots, reinstall_fonts]) backup_config = get_config() @@ -125,23 +126,23 @@ def cli(add_dot, configs, delete_config, destroy_backup, dotfiles, dry_run, font reinstall_dots_sb(dotfiles_path, dry_run=dry_run) elif reinstall_all: reinstall_all_sb(dotfiles_path, packages_path, fonts_path, configs_path, dry_run=dry_run) - elif full_backup: + elif backup_all_flag: backup_all(dotfiles_path, packages_path, fonts_path, configs_path, dry_run=dry_run, skip=True) if not dry_run: git_add_all_commit_push(repo, "full_backup") - elif dotfiles: + elif backup_dots_flag: backup_dotfiles(dotfiles_path, dry_run=dry_run, skip=True) if not dry_run: git_add_all_commit_push(repo, "dotfiles", separate_dotfiles_repo) - elif configs: + elif backup_configs_flag: backup_configs(configs_path, dry_run=dry_run, skip=True) if not dry_run: git_add_all_commit_push(repo, "configs") - elif packages: + elif backup_packages_flag: backup_packages(packages_path, dry_run=dry_run, skip=True) if not dry_run: git_add_all_commit_push(repo, "packages") - elif fonts: + elif backup_fonts_flag: backup_fonts(fonts_path, dry_run=dry_run, skip=True) if not dry_run: git_add_all_commit_push(repo, "fonts") From efc833c134593d5b69bfc6a9625973c64aa3ad94 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Wed, 13 May 2020 07:38:23 -0500 Subject: [PATCH 3/3] Update script in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a16899fe..fae55e83 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ backup-dots () { echo "Backing up..." crontab -l > ~/.config/crontab ( - shallow-backup -no_splash -dotfiles -separate_dotfiles_repo + shallow-backup -no-splash -backup-dots -separate-dotfiles-repo cd "$HOME/shallow-backup/dotfiles/" || exit git add . commit_msg="$1"