Beispiel #1
0
def backup_configs(backup_path):
    """
	Creates `configs` directory and places config backups there.
	Configs are application settings, generally. .plist files count.
	"""
    print_section_header("CONFIGS", Fore.BLUE)
    mkdir_warn_overwrite(backup_path)
    config = get_config()
    configs_dir_mapping = config["config_path_to_dest_map"]
    plist_files = config["plist_path_to_dest_map"]

    print(Fore.BLUE + Style.BRIGHT + "Backing up configs..." + Style.RESET_ALL)

    # backup config dirs in backup_path/<target>/
    for config, target in configs_dir_mapping.items():
        src_dir = home_prefix(config)
        configs_backup_path = os.path.join(backup_path, target)
        if os.path.isdir(src_dir):
            # TODO: Exclude Sublime/Atom/VS Code Packages here to speed things up
            copytree(src_dir, configs_backup_path, symlinks=True)

    # backup plist files in backup_path/configs/plist/
    print(Fore.BLUE + Style.BRIGHT + "Backing up plist files..." +
          Style.RESET_ALL)
    plist_backup_path = os.path.join(backup_path, "plist")
    mkdir_or_pass(plist_backup_path)
    for plist, dest in plist_files.items():
        plist_path = home_prefix(plist)
        if os.path.exists(plist_path):
            copyfile(plist_path, os.path.join(backup_path, dest))
Beispiel #2
0
def backup_dotfiles(backup_path):
    """
	Create `dotfiles` dir and makes copies of dotfiles and dotfolders.
	"""
    print_section_header("DOTFILES", Fore.BLUE)
    mkdir_warn_overwrite(backup_path)

    # assumes dotfiles are stored in home directory
    home_path = os.path.expanduser('~')

    # get dotfolders and dotfiles
    config = get_config()
    dotfiles_for_backup = config["dotfiles"]
    dotfolders_for_backup = config["dotfolders"]

    # Add dotfile/folder for backup if it exists on the machine
    dotfiles = [
        file for file in dotfiles_for_backup
        if os.path.isfile(os.path.join(home_path, file))
    ]
    dotfolders = [
        folder for folder in dotfolders_for_backup
        if os.path.exists(os.path.join(home_path, folder))
    ]

    # dotfiles/folders multiprocessing format: [(full_dotfile_path, full_dest_path), ...]
    dotfolders_mp_in = []
    for dotfolder in dotfolders:
        dotfolders_mp_in.append((os.path.join(home_path,
                                              dotfolder), backup_path))

    dotfiles_mp_in = []
    for dotfile in dotfiles:
        dotfiles_mp_in.append(
            (os.path.join(home_path,
                          dotfile), os.path.join(backup_path, dotfile)))

    # Multiprocessing
    with mp.Pool(mp.cpu_count()):
        print(Fore.BLUE + Style.BRIGHT + "Backing up dotfolders..." +
              Style.RESET_ALL)
        for x in dotfolders_mp_in:
            x = list(x)
            mp.Process(target=copy_dir, args=(
                x[0],
                x[1],
            )).start()

    with mp.Pool(mp.cpu_count()):
        print(Fore.BLUE + Style.BRIGHT + "Backing up dotfiles..." +
              Style.RESET_ALL)
        for x in dotfiles_mp_in:
            x = list(x)
            mp.Process(target=copyfile, args=(
                x[0],
                x[1],
            )).start()
def overwrite_dir_prompt_if_needed(path, needed):
	"""
	Prompts the user before deleting the directory if needed.
	This function lets the CLI args silence the prompts.
	:param path: absolute path
	:param needed: boolean
	"""
	if not needed:
		mkdir_warn_overwrite(path)
	else:
		mkdir_overwrite(path)
Beispiel #4
0
def backup_fonts(path):
    """
	Creates list of all .ttf and .otf files in ~/Library/Fonts/
	"""
    print_section_header("FONTS", Fore.BLUE)
    mkdir_warn_overwrite(path)
    print(Fore.BLUE + "Copying '.otf' and '.ttf' fonts..." + Style.RESET_ALL)
    fonts_path = home_prefix("Library/Fonts/")
    fonts = [
        os.path.join(fonts_path, font) for font in os.listdir(fonts_path)
        if font.endswith(".otf") or font.endswith(".ttf")
    ]

    for font in fonts:
        if os.path.exists(font):
            copyfile(font, os.path.join(path, font.split("/")[-1]))
Beispiel #5
0
def prompt_for_path_update(config):
    """
	Ask user if they'd like to update the backup path or not.
	If yes, update. If no... don't.
	"""
    current_path = config["backup_path"]
    print("{}{}Current shallow-backup path: {}{}{}".format(
        Fore.BLUE, Style.BRIGHT, Style.NORMAL, current_path, Style.RESET_ALL))

    if prompt_yes_no("Would you like to update this?", Fore.GREEN):
        print(Fore.GREEN + Style.BRIGHT + "Enter relative path:" +
              Style.RESET_ALL)
        abs_path = os.path.abspath(input())
        print(Fore.BLUE +
              "\nUpdating shallow-backup path to {}".format(abs_path) +
              Style.RESET_ALL)
        config["backup_path"] = abs_path
        write_config(config)
        mkdir_warn_overwrite(abs_path)
        move_git_repo(current_path, abs_path)
Beispiel #6
0
def cli(add, rm, show, all, dotfiles, configs, packages, fonts, old_path,
        new_path, remote, reinstall_all, reinstall_configs, reinstall_dots,
        reinstall_fonts, reinstall_packages, delete_config, destroy_backup, v):
    """
	Easily back up installed packages, dotfiles, and more.
	You can edit which dotfiles are backed up in ~/.shallow-backup.

	Written by Aaron Lichtman (@alichtman).
	"""

    # Process CLI args
    admin_action = any([v, delete_config, destroy_backup, show, rm
                        ]) or None not in add
    has_cli_arg = any([
        old_path, all, dotfiles, packages, fonts, configs, reinstall_dots,
        reinstall_fonts, reinstall_all, reinstall_configs, reinstall_packages
    ])
    skip_prompt = any([
        all, dotfiles, configs, packages, fonts, reinstall_packages,
        reinstall_configs, reinstall_dots, reinstall_fonts
    ])

    # Perform administrative action and exit.
    if admin_action:
        if v:
            print_version_info()
        elif delete_config:
            # TODO: Error checking.
            os.remove(get_config_path())
            print_red_bold("Removed config file...")
        elif destroy_backup:
            backup_home_path = get_config()["backup_path"]
            destroy_backup_dir(backup_home_path)
        elif None not in add:
            add_to_config(add[0], add[1])
        elif rm:
            rm_from_config(rm)
        elif show:
            show_config()
        sys.exit()

    # Start CLI
    splash_screen()
    safe_create_config()
    backup_config = get_config()

    # User entered a new path, so update the config
    if new_path:
        abs_path = os.path.abspath(new_path)
        print(Fore.BLUE + Style.NORMAL +
              "\nUpdating shallow-backup path to -> " + Style.BRIGHT +
              "{}".format(abs_path) + Style.RESET_ALL)
        backup_config["backup_path"] = abs_path
        write_config(backup_config)

    # User didn't enter any CLI args so prompt for path update before showing menu
    elif not has_cli_arg:
        prompt_for_path_update(backup_config)

    # Create backup directory and do git setup
    backup_home_path = get_config()["backup_path"]
    mkdir_warn_overwrite(backup_home_path)
    repo, new_git_repo_created = safe_git_init(backup_home_path)

    # Create default gitignore if we just ran git init
    if new_git_repo_created:
        safe_create_gitignore(backup_home_path)
        # Prompt user for remote URL
        if not remote:
            prompt_for_git_url(repo)

    # Set remote URL from CLI arg
    if remote:
        git_set_remote(repo, remote)

    dotfiles_path = os.path.join(backup_home_path, "dotfiles")
    configs_path = os.path.join(backup_home_path, "configs")
    packages_path = os.path.join(backup_home_path, "packages")
    fonts_path = os.path.join(backup_home_path, "fonts")

    # Command line options
    if skip_prompt:
        if reinstall_packages:
            reinstall_packages_sb(packages_path)
        elif reinstall_configs:
            reinstall_configs_sb(configs_path)
        elif reinstall_fonts:
            reinstall_fonts_sb(fonts_path)
        elif reinstall_dots:
            reinstall_dots_sb(dotfiles_path)
        elif reinstall_all:
            reinstall_all_sb(dotfiles_path, packages_path, fonts_path,
                             configs_path)
        elif all:
            backup_all(dotfiles_path,
                       packages_path,
                       fonts_path,
                       configs_path,
                       skip=True)
            git_add_all_commit_push(repo, "all")
        elif dotfiles:
            backup_dotfiles(dotfiles_path, skip=True)
            git_add_all_commit_push(repo, "dotfiles")
        elif configs:
            backup_configs(configs_path, skip=True)
            git_add_all_commit_push(repo, "configs")
        elif packages:
            backup_packages(packages_path, skip=True)
            git_add_all_commit_push(repo, "packages")
        elif fonts:
            backup_fonts(fonts_path, skip=True)
            git_add_all_commit_push(repo, "fonts")
    # No CL options, show action menu and process selected option.
    else:
        selection = actions_menu_prompt().lower().strip()
        selection_words = selection.split()
        if selection.startswith("back up"):
            if selection_words[-1] == "all":
                backup_all(dotfiles_path, packages_path, fonts_path,
                           configs_path)
                git_add_all_commit_push(repo, selection_words[-1])
            elif selection_words[-1] == "dotfiles":
                backup_dotfiles(dotfiles_path)
                git_add_all_commit_push(repo, selection_words[-1])
            elif selection_words[-1] == "configs":
                backup_configs(configs_path)
                git_add_all_commit_push(repo, selection_words[-1])
            elif selection_words[-1] == "packages":
                backup_packages(packages_path)
                git_add_all_commit_push(repo, selection_words[-1])
            elif selection_words[-1] == "fonts":
                backup_fonts(fonts_path)
                git_add_all_commit_push(repo, selection_words[-1])
        elif selection.startswith("reinstall"):
            if selection_words[-1] == "packages":
                reinstall_packages_sb(packages_path)
            elif selection_words[-1] == "configs":
                reinstall_configs_sb(configs_path)
            elif selection_words[-1] == "fonts":
                reinstall_fonts_sb(fonts_path)
            elif selection_words[-1] == "dotfiles":
                reinstall_dots_sb(dotfiles_path)
            elif selection_words[-1] == "all":
                reinstall_all_sb(dotfiles_path, packages_path, fonts_path,
                                 configs_path)
        else:
            if selection == "show config":
                show_config()
            elif selection == "destroy backup":
                if prompt_yes_no(
                        "Erase backup directory: {}?".format(backup_home_path),
                        Fore.RED):
                    destroy_backup_dir(backup_home_path)
                else:
                    print_red_bold(
                        "Exiting to prevent accidental deletion of backup directory."
                    )

    sys.exit()
Beispiel #7
0
def cli(add, rm, show, complete, dotfiles, configs, packages, fonts, old_path, new_path, remote, reinstall_packages,
        reinstall_configs, delete_config, destroy_backup, v):
	"""
	Easily back up installed packages, dotfiles, and more.
	You can edit which dotfiles are backed up in ~/.shallow-backup.

	Written by Aaron Lichtman (@alichtman).
	"""
	backup_config_path = get_config_path()

	# No interface going to be displayed
	if any([v, delete_config, destroy_backup, show, rm]) or None not in add:
		if v:
			print_version_info()
		elif delete_config:
			os.remove(backup_config_path)
			print_bright_red("Removed config file...")
		elif destroy_backup:
			backup_home_path = get_config()["backup_path"]
			destroy_backup_dir(backup_home_path)
		elif None not in add:
			add_path_to_config(add[0], add[1])
		elif rm:
			rm_path_from_config(rm)
		elif show:
			show_config()
		sys.exit()

	# Start CLI
	splash_screen()
	create_config_file_if_needed()
	backup_config = get_config()

	# User entered a new path, so update the config
	if new_path:
		abs_path = os.path.abspath(new_path)
		print(Fore.BLUE + Style.NORMAL + "\nUpdating shallow-backup path to -> " + Style.BRIGHT + "{}".format(
			abs_path) + Style.RESET_ALL)
		backup_config["backup_path"] = abs_path
		write_config(backup_config)

	# User didn't enter any CLI args so prompt for path update before showing menu
	elif not (old_path or complete or dotfiles or packages or fonts):
		prompt_for_path_update(backup_config)

	# Create backup directory and do git setup
	backup_home_path = get_config()["backup_path"]
	mkdir_warn_overwrite(backup_home_path)
	repo, new_git_repo_created = safe_git_init(backup_home_path)

	# Create default gitignore if we just ran git init
	if new_git_repo_created:
		safe_create_gitignore(backup_home_path)
		# Prompt user for remote URL
		if not remote:
			prompt_for_git_url(repo)

	# Set remote URL from CLI arg
	if remote:
		git_set_remote(repo, remote)

	dotfiles_path = os.path.join(backup_home_path, "dotfiles")
	configs_path = os.path.join(backup_home_path, "configs")
	packages_path = os.path.join(backup_home_path, "packages")
	fonts_path = os.path.join(backup_home_path, "fonts")

	# Command line options
	if any([complete, dotfiles, configs, packages, fonts, reinstall_packages, reinstall_configs]):
		if reinstall_packages:
			reinstall_packages_from_lists(packages_path)
		elif reinstall_configs:
			reinstall_config_files(configs_path)
		elif complete:
			backup_all(dotfiles_path, packages_path, fonts_path, configs_path)
			git_add_all_commit_push(repo, "everything")
		elif dotfiles:
			backup_dotfiles(dotfiles_path)
			git_add_all_commit_push(repo, "dotfiles")
		elif configs:
			backup_configs(configs_path)
			git_add_all_commit_push(repo, "configs")
		elif packages:
			backup_packages(packages_path)
			git_add_all_commit_push(repo, "packages")
		elif fonts:
			backup_fonts(fonts_path)
			git_add_all_commit_push(repo, "fonts")
	# No CL options, prompt for selection
	else:
		selection = actions_menu_prompt().lower().strip()
		if selection == "back up everything":
			backup_all(dotfiles_path, packages_path, fonts_path, configs_path)
			git_add_all_commit_push(repo, "everything")
		elif selection == "back up dotfiles":
			backup_dotfiles(dotfiles_path)
			git_add_all_commit_push(repo, "dotfiles")
		elif selection == "back up configs":
			backup_configs(configs_path)
			git_add_all_commit_push(repo, "configs")
		elif selection == "back up packages":
			backup_packages(packages_path)
			git_add_all_commit_push(repo, "packages")
		elif selection == "back up fonts":
			backup_fonts(fonts_path)
			git_add_all_commit_push(repo, "fonts")
		elif selection == "reinstall packages":
			reinstall_packages_from_lists(packages_path)
		elif selection == "reinstall configs":
			reinstall_config_files(configs_path)
		elif selection == "show config":
			show_config()
		elif selection == "destroy backup":
			if prompt_yes_no("Erase backup directory: {}?".format(backup_home_path), Fore.RED):
				destroy_backup_dir(backup_home_path)
			else:
				print_bright_red("Exiting to prevent accidental deletion of backup directory.")

	sys.exit()
Beispiel #8
0
def backup_packages(backup_path):
    """
	Creates `packages` directory and places install list text files there.
	"""
    print_section_header("PACKAGES", Fore.BLUE)
    mkdir_warn_overwrite(backup_path)

    std_package_managers = ["brew", "brew cask", "gem"]

    for mgr in std_package_managers:
        # 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)

    # 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)

    # pip
    print_pkg_mgr_backup("pip")
    command = "pip list --format=freeze".format(backup_path)
    dest = "{}/pip_list.txt".format(backup_path)
    run_cmd_write_stdout(command, dest)

    # npm
    print_pkg_mgr_backup("npm")
    command = "npm ls --global --parseable=true --depth=0"
    temp_file_path = "{}/npm_temp_list.txt".format(backup_path)
    run_cmd_write_stdout(command, temp_file_path)
    npm_dest_file = "{0}/npm_list.txt".format(backup_path)
    # Parse npm output
    with open(temp_file_path, mode="r+") as temp_file:
        # Skip first line of file
        temp_file.seek(1)
        with open(npm_dest_file, mode="w+") as dest:
            for line in temp_file:
                dest.write(line.split("/")[-1])

    os.remove(temp_file_path)

    # 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)

    # sublime text 2 packages
    sublime_2_path = home_prefix(
        "Library/Application Support/Sublime Text 2/Packages/")
    if os.path.isdir(sublime_2_path):
        print_pkg_mgr_backup("Sublime Text 2")
        command = ["ls", sublime_2_path]
        dest = "{}/sublime2_list.txt".format(backup_path)
        run_cmd_write_stdout(command, dest)

    # sublime text 3 packages
    sublime_3_path = home_prefix(
        "Library/Application Support/Sublime Text 3/Installed Packages/")
    if os.path.isdir(sublime_3_path):
        print_pkg_mgr_backup("Sublime Text 3")
        command = ["ls", sublime_3_path]
        dest = "{}/sublime3_list.txt".format(backup_path)
        run_cmd_write_stdout(command, dest)
    else:
        print(sublime_3_path, "IS NOT DIR")

    # macports
    print_pkg_mgr_backup("macports")
    command = "port installed requested"
    dest = "{}/macports_list.txt".format(backup_path)
    run_cmd_write_stdout(command, dest)

    # system installs
    print_pkg_mgr_backup("macOS Applications")
    command = "ls /Applications/"
    dest = "{}/system_apps_list.txt".format(backup_path)
    run_cmd_write_stdout(command, dest)

    # Clean up empty package list files
    print(Fore.BLUE + "Cleaning up empty package lists..." + Style.RESET_ALL)
    for file in get_subfiles(backup_path):
        if os.path.getsize(file) == 0:
            os.remove(file)