コード例 #1
0
ファイル: test.py プロジェクト: Vinesma/.dotfiles
def run():
    """Run sanity tests on package files"""
    message.info("Checking for package errors...")
    groups = Group.load_all()
    errors_found = 0

    for group in groups:
        files = group.files
        packages = group.packages

        for package in packages:
            errors_found += package.evaluate()

            for _file in package.files:
                errors_found += _file.evaluate()

        for _file in files:
            errors_found += _file.evaluate()

    if errors_found > 0:
        message.alert(
            f"{errors_found} errors found. It is highly recommended to fix them before proceeding."
        )
        if not message.question("Proceed without fixing?", "boolean"):
            sys.exit(1)
    else:
        message.info("No errors found")

    message.break_line()
コード例 #2
0
ファイル: file.py プロジェクト: Vinesma/.dotfiles
    def link(self):
        """Link this file from source to destination"""

        if self.path_source is not None:
            full_source_path = os.path.join(
                os.path.expandvars(self.path_source), self.name)
            full_destination_path = os.path.join(
                os.path.expandvars(self.path_destination), self.name)

            try:
                if self.sudo:
                    spawn.process(
                        f'ln -sfv "{full_source_path}" "{full_destination_path}"',
                        sudo=True,
                    )
                else:
                    os.symlink(full_source_path, full_destination_path)
            except FileExistsError:
                message.error(
                    "Can't symlink, file already exists at destination. Attempting fix."
                )
                os.remove(full_destination_path)
                message.info(f"Removed: '{full_destination_path}'")
                os.symlink(full_source_path, full_destination_path)
            finally:
                message.info(
                    f"Symlink created: '{full_source_path}' <--> '{full_destination_path}'"
                )
        else:
            message.error(
                f"'{self.name}' has no source from which to create a link from."
            )
コード例 #3
0
ファイル: spawn.py プロジェクト: Vinesma/.dotfiles
def process(process_string, silent=False, capture=False, sudo=False):
    """Run a process.

    If silent = False, displays process output to the screen,
    otherwise only this program's output is shown.

    If capture = True, return the process' stdout instead of a CompletedProcess instance.

    If sudo = True, run the process as super user
    (may require user intervention for collecting password).

    On error, stops execution for 30 seconds for evaluation, alternatively, crashes the program.
    """

    if sudo:
        process_string = f"{config.admin_command} {process_string}"

    [process_name, *args] = process_string.split()

    # Remove special {SPACE} character, which denotes a space that doesn't mean a new argument
    for index, arg in enumerate(args):
        if "{SPACE}" in arg:
            args[index] = arg.replace("{SPACE}", " ")

    try:
        output = run(
            [process_name, *args],
            check=True,
            text=True,
            capture_output=(capture or silent),
        )
    except CalledProcessError as error:
        message.error(
            f"'{process_name}' failed with code {error.returncode} :: {error.output}"
        )
        message.alert(f"ARGS: {args}")

        if config.fail_fast:
            message.alert(
                f"Halting execution because of fail_fast = {config.fail_fast}")
            sys.exit(1)
        else:
            message.info("Stopping execution temporarily for your evaluation.")

            for i in range(3, 0, -1):
                message.info(f"Program will continue in {i * 10} seconds...")
                sleep(config.seconds_to_wait_on_fail)

            output = run(["echo"], check=True, text=True)

    if capture:
        return output.stdout

    return output
コード例 #4
0
ファイル: file.py プロジェクト: Vinesma/.dotfiles
    def show_comments(self):
        """Show useful comments to the user,
        such as how to configure a program further or what to do next.
        """

        for index, comment in enumerate(self.comments):
            if index == 0:
                message.heading(f"[{self.name}]")
            message.info(comment)

        if len(self.comments) > 0:
            log.write(f"FILE ({self.name})\n" + "\n".join(self.comments))
コード例 #5
0
ファイル: file.py プロジェクト: Vinesma/.dotfiles
    def copy(self):
        """Copy this file from source to destination"""

        if self.path_source is not None:
            full_source_path = os.path.join(
                os.path.expandvars(self.path_source), self.name)

            if self.sudo:
                spawn.process(
                    f'cp -v -- "{full_source_path}" "{self.path_destination}"',
                    sudo=True,
                )
            else:
                message.info(
                    f"Copied: '{full_source_path}' --> '{self.path_destination}'"
                )
                shutil.copy(full_source_path, self.path_destination)
        else:
            message.error(
                f"'{self.name}' has no source from which to copy from.")
コード例 #6
0
ファイル: file.py プロジェクト: Vinesma/.dotfiles
    def touch(self):
        """Create this file at its destination. Write text to it."""
        full_destination_path = os.path.join(
            os.path.expandvars(self.path_destination), self.name)

        try:
            with open(full_destination_path, "w", encoding="utf-8") as _file:
                _file.write(self.text)
            message.info(
                f"Created file: '{self.name}' at '{self.path_destination}'")
        except OSError:
            message.error(
                f"There was a problem creating the file '{self.name}' at '{self.path_destination}'"
            )

            if config.fail_fast:
                sys.exit(1)

            message.info("Stopping execution temporarily for your evaluation.")

            for i in range(3, 0, -1):
                message.info(f"Program will continue in {i * 10} seconds...")
                sleep(config.seconds_to_wait_on_fail)
コード例 #7
0
ファイル: main.py プロジェクト: Vinesma/.dotfiles
def main():
    """Start here."""

    if config.dev_mode:
        print("DEV MODE: ON")
        test.run()

    while True:
        message.heading("Welcome! Pick your poison:")

        choice = message.choose(
            [
                "Full install",
                "Select group",
                "Create links",
                "Create new package group",
                "Populate main.json with all group .json files",
            ],
            allow_exit=True,
        )

        if choice == "Full install":
            log.write("--LOG START--", refresh=True)
            groups = Group.load_all()

            for index, group in enumerate(groups, start=1):
                if index == 1:
                    group.install(sync=True)
                else:
                    group.install()

            log.write("--LOG END--")
        elif choice == "Select group":
            groups = Group.load_all()

            group_choice = message.choose([group.name for group in groups],
                                          allow_exit=True)

            if group_choice != "EXIT":
                Group.load(group_choice).install()
            else:
                os.system("clear")
        elif choice == "Create links":
            groups = Group.load_all()

            group_choice = message.choose([group.name for group in groups],
                                          allow_exit=True)

            if group_choice != "EXIT":
                Group.load(group_choice).link_files()
            else:
                os.system("clear")
        elif choice == "Create new package group":
            Group.interactive_insert().save()
            message.info(
                "Don't forget to add this group to the 'main.json' file. This decides the order in which it is installed."
            )
        elif choice == "Populate main.json with all group .json files":
            group_names = [
                group.replace(".json", "")
                for group in os.listdir(config.group_files_path)
                if group.endswith(".json") and group != "initial.json"
            ]

            group_names.insert(0, "initial")

            with open(
                    os.path.join(config.program_path, "data", "main.json"),
                    "w",
                    encoding="utf-8",
            ) as _file:
                json.dump(group_names, fp=_file, indent=4)
            os.system("clear")
            message.info("Populated main.json with all groups found.")
        else:
            message.normal("See ya!")
            break
コード例 #8
0
ファイル: file.py プロジェクト: Vinesma/.dotfiles
    def interactive_insert(group_name=None, package_name=None):
        """Create a new file object from the command line"""
        file_name = ""
        file_destination = ""
        file_source = None
        file_create_link = False
        file_sudo = False
        file_comments = []

        def ask_file_source():
            return message.question("What is the full source file path?")

        def ask_sudo():
            return message.question("Is sudo needed for this operation?",
                                    "boolean")

        while True:
            file_source = None
            file_create_link = False
            file_sudo = False

            message.heading(
                "Creating a new file. (${vars} is supported, '~' is not)")
            if group_name is not None:
                message.info(f"Current group: {group_name}")
            if package_name is not None:
                message.info(f"Current package: {package_name}")

            file_destination = message.question(
                "Where will this file be (created/linked/copied) to? (no basename)"
            )

            if message.question("Will this file be linked to [destination]?",
                                "boolean"):
                file_create_link = True
                file_source = ask_file_source()
                file_sudo = ask_sudo()
            elif message.question("Will this file be copied to [destination]?",
                                  "boolean"):
                file_source = ask_file_source()
                file_sudo = ask_sudo()

            if file_source is not None:
                [_, file_name] = os.path.split(os.path.expandvars(file_source))
            else:
                file_name = message.question("What will be the file's name?")

            if message.question("Will the file have comments to aid the user?",
                                "boolean"):
                while True:
                    comment = message.question("New comment:")
                    file_comments.append(comment)
                    if not message.question("Add another comment?", "boolean"):
                        break

            new_file = File(
                file_name,
                file_destination,
                os.path.split(file_source)[0]
                if file_source is not None else None,
                "",
                file_create_link,
                file_sudo,
                file_comments,
            )

            new_file.evaluate()

            message.info(f"""File info:
            [Name]: '{new_file.name}'
            [Destination]: '{new_file.path_destination}'
            [Source]: '{new_file.path_source}'
            [Link?]: '{'Yes' if new_file.create_link else 'No'}'
            [Need superuser?]: '{'Yes' if new_file.sudo else 'No'}'
            [Comments]: {new_file.comments}""")
            if message.question("Confirm?", "boolean"):
                break

        return new_file
コード例 #9
0
    def interactive_insert(group_name=None):
        """Create a new package object from the command line"""
        package_display_name = ""
        package_name = ""
        package_installer = config.default_installer
        package_files = []
        package_auto_start = []
        package_comments = []
        package_post_install_commands = []

        while True:
            message.heading("Creating a new package.")
            if group_name is not None:
                message.info(f"Current group: {group_name}")

            package_display_name = message.question(
                "What is the display name of the package?")
            package_name = message.question(
                "What is the exact name of the package? (Will be used for installing, needs to be exact)"
            )
            package_installer = message.choose(
                ["default", "pacman", "aur", "pip"])
            if package_installer == "default":
                package_installer = config.default_installer

            if message.question("Will this package be autostarted?",
                                "boolean"):
                while True:
                    command = message.question(
                        "New command for autostart (will appear on a newline):"
                    )
                    package_auto_start.append(command)
                    if not message.question("Add another autostart command?",
                                            "boolean"):
                        break

            if message.question(
                    "Will this package have post install commands?",
                    "boolean"):
                while True:
                    command = message.question(
                        "New command for post install (${vars} supported):")
                    package_post_install_commands.append(command)
                    if not message.question("Add another command?", "boolean"):
                        break

            if message.question("Will this package have files associated?",
                                "boolean"):
                while True:
                    _file = File.interactive_insert(group_name,
                                                    package_name).to_dict()
                    package_files.append(_file)
                    if not message.question("Add another file?", "boolean"):
                        break

            if message.question(
                    "Will the package have comments to aid the user?",
                    "boolean"):
                while True:
                    comment = message.question("New comment:")
                    package_comments.append(comment)
                    if not message.question("Add another comment?", "boolean"):
                        break

            package = Package(
                package_display_name,
                package_name,
                package_installer,
                package_files,
                package_auto_start,
                package_comments,
                package_post_install_commands,
            )

            package.evaluate()

            message.info(f"""Package info:
            [Display Name]: '{package.display_name}'
            [Install Name]: '{package.name}'
            [Installer]: '{package.installer}'
            [Autostart]: '{package.auto_start}'
            [Post Install Commands]: '{package.post_install_commands}'
            [Files]: '{[_file.name for _file in package.files]}'
            [Comments]: {package.comments}""")
            if message.question("Confirm?", "boolean"):
                break

        return package