diff --git a/.gitignore b/.gitignore index edfa45c..b80941e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ git_configs/ +home/ansible_playbooks/*_playbook.yaml # Byte-compiled / optimized / DLL files __pycache__/ -*.py[cod] +*.py[codz] *$py.class # C extensions @@ -29,8 +30,8 @@ share/python-wheels/ MANIFEST # PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec @@ -48,7 +49,7 @@ htmlcov/ nosetests.xml coverage.xml *.cover -*.py,cover +*.py.cover .hypothesis/ .pytest_cache/ cover/ @@ -94,25 +95,38 @@ ipython_config.py # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. -#Pipfile.lock +# Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +uv.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock +# poetry.lock +# poetry.toml # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml .pdm-python .pdm-build/ +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ @@ -120,11 +134,25 @@ __pypackages__/ celerybeat-schedule celerybeat.pid +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + # SageMath parsed files *.sage.py # Environments .env +.envrc .venv env/ venv/ @@ -157,8 +185,35 @@ dmypy.json cython_debug/ # PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# .idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3c01115 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": false, + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index c23cd67..f376268 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,9 @@ { "shellcheck.customArgs": [ "-P" + ], + "cSpell.words": [ + "chezmoi", + "Justfile" ] } \ No newline at end of file diff --git a/README.md b/README.md index 2288739..ecbc459 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,10 @@ Yoshikage Kira 's dotfiles, managed with [`chezmoi`](https://github.com/twpayne/ git clone https://github.com/isFakeAccount/dotfiles.git ~/.local/share/chezmoi ``` -2. Install `python3-venv` and `python3-pip` if necessary. -3. `cd` into the repo, create a python venv and activate it. -4. Run the ./install.sh script. +2. Install `justfile` and `chezmoi` if necessary. +3. Install `python3-venv` and `python3-pip` if necessary. +4. `cd` into the repo, create a python `.venv` and activate it. +5. Run the `chezmoi init` script. ## Chezmoi Quick Commands @@ -55,7 +56,7 @@ chezmoi --verbose apply **Apply the changes only for specified types like file changes, symlinks, and templates. So basically no scripts are run.** ```sh -chezmoi --verbose -i files,symlinks,templates apply +chezmoi --verbose -i dirs,files,remove,symlinks,templates apply ``` ### Pull changes from a remote repository and apply them diff --git a/home/.chezmoiscripts/run_after_00_install_paru.sh b/home/.chezmoiscripts/run_after_00_install_paru.sh new file mode 100755 index 0000000..32e1472 --- /dev/null +++ b/home/.chezmoiscripts/run_after_00_install_paru.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ ! -f /etc/os-release ]]; then + echo "Cannot determine OS (missing /etc/os-release). Exiting." + exit 1 +fi + +source /etc/os-release + +if [[ "${ID:-}" != "arch" ]]; then + echo "Not Arch Linux (ID=${ID:-unknown}). Exiting." + exit 0 +fi + +if command -v paru &> /dev/null; then + echo "paru is already installed. Exiting." + exit 0 +fi + +echo "Installing paru AUR helper..." + +sudo pacman -S --needed base-devel git + +temp_dir="$(mktemp -d)" +trap 'rm -rf "$temp_dir"' EXIT + +git clone https://aur.archlinux.org/paru.git "$temp_dir/paru" +cd "$temp_dir/paru" +makepkg -si diff --git a/home/.chezmoiscripts/run_after_01_gen_ansible_playbooks.py b/home/.chezmoiscripts/run_after_01_gen_ansible_playbooks.py new file mode 100755 index 0000000..958fd12 --- /dev/null +++ b/home/.chezmoiscripts/run_after_01_gen_ansible_playbooks.py @@ -0,0 +1,452 @@ +#!/usr/bin/env python3 + +import re +import tempfile +from collections.abc import Iterable +from dataclasses import dataclass +from enum import Enum, StrEnum +from pathlib import Path +from platform import freedesktop_os_release +from typing import Any, Literal, Self + +import questionary +import yaml +from platformdirs import user_data_path +from questionary import Choice, Style + +CHEZMOI_DIR = user_data_path("chezmoi", False) + + +class LinuxDistroBase(Enum): + ARCH = "arch" + DEBIAN = "debian" + FEDORA = "fedora" + + +class PackageInstallMethod(StrEnum): + FLATPAK = "flatpak" + PARU = "paru" + SCRIPT = "script" + SYSTEM_PACKAGE = "system_package" + + +@dataclass +class ScriptPackageMetadata: + """Metadata required to install a package using a custom script. + + :var url: URL from which the installation script can be downloaded. + :var environment_vars: Environment variables to be set when executing the installation script. + :var ansible_vars: Variables exposed to Ansible when running the script-based installation. + :var preinstall_packages: Optional list of system packages that must be installed before running the script. + :var post_install_packages: Optional list of system packages that maybe available after running the script if the + script adds PPA. + :var interpreter: Interpreter used to execute the script. Supported values are ``"bash"`` and ``"python3"``. + + """ + + url: str + environment_vars: dict[str, str] + ansible_vars: dict[str, str] + preinstall_packages: list[str] | None + post_install_packages: list[str] | None + args: list[str] | None + interpreter: Literal["bash", "python3"] = "bash" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> Self: + return cls( + url=data["url"], + environment_vars=data.get("environment", {}), + ansible_vars=data.get("vars", {}), + preinstall_packages=None, # TODO: Add this feature later. + post_install_packages=data.get("post_install_packages", None), + args=data.get("args", None), + interpreter=data.get("interpreter", "bash"), + ) + + +@dataclass +class LinuxPackage: + """Class representing a linux package and how to install the package. + + :var become: Indicates if package needs elevated privileges to install. + :var display_name: Display name for the package. + :var executable_name: Name of the executable that will be available on the PATH after the installation of the + package. + :var package_name: Name of the package used by the remote repositories. + :var method: Installation method used for this package. + :var script_extra: If installation method is script then this will contain the necessary information to install via + script. + + """ + + become: bool + display_name: str + executable_name: str + package_name: str + method: PackageInstallMethod + script_extra: ScriptPackageMetadata | None + + @classmethod + def to_system_package(cls, package_display_name: str, installation_metadata: dict[str, Any]) -> Self: + method = PackageInstallMethod(installation_metadata["method"]) + package_name = installation_metadata[method] if method != PackageInstallMethod.SCRIPT else installation_metadata["executable_name"] + return cls( + display_name=package_display_name, + package_name=package_name, + executable_name=installation_metadata["executable_name"], + method=method, + become=installation_metadata.get("become", False), + script_extra=ScriptPackageMetadata.from_dict(installation_metadata["script"]) if method == PackageInstallMethod.SCRIPT else None, + ) + + @classmethod + def to_flatpak_list(cls, package_names: Iterable[str], become: bool) -> list[Self]: + return [ + cls( + display_name=package_name, # for flatpak idc about display name. Could be made better in future. + package_name=package_name, + executable_name=package_name, + method=PackageInstallMethod.FLATPAK, + become=become, + script_extra=None, + ) + for package_name in package_names + ] + + +def get_linux_distro_base() -> LinuxDistroBase: + """Determines the base Linux distribution type. + + :returns: An instance of LinuxDistroBase representing the base distro. + + """ + os_release = freedesktop_os_release() + distro_id = os_release.get("ID_LIKE") or os_release.get("ID") or "" + + if "debian" in distro_id.lower(): + return LinuxDistroBase.DEBIAN + elif "fedora" in distro_id.lower() or "rhel" in distro_id.lower(): + return LinuxDistroBase.FEDORA + elif "arch" in distro_id.lower(): + return LinuxDistroBase.ARCH + else: + raise ValueError("Unsupported or unrecognized Linux distribution.") + + +def make_system_pkg_install_task(linux_package_names: list[str], become: bool) -> dict[str, object]: + """Create an Ansible task for installing system packages. + + :param linux_package_names: A list of Linux package names to install. + :param become: Whether to run the task with privilege escalation. This should typically be ``True`` for system + packages. + + :returns: A dictionary representing an Ansible task using ``ansible.builtin.package``. + + """ + return { + "name": "Install System packages", + "become": become, + "ansible.builtin.package": { + "name": linux_package_names, + "state": "present", + }, + } + + +def make_flatpak_install_task(linux_packages: list[LinuxPackage], become: bool) -> dict[str, object]: + """Create an Ansible task for installing Flatpak packages. + + The Flatpak installation method is derived from ``become``: - ``become=True`` → system-wide installation - + ``become=False`` → per-user installation + + :param linux_packages: A list of Flatpak LinuxPackage objects to install. + :param become: Whether to install Flatpaks system-wide (``True``) or for the current user (``False``). + + :returns: A dictionary representing an Ansible task using ``community.general.flatpak``. + + """ + return { + "name": f"Install {'System' if become else 'User'} Flatpak packages", + "become": become, + "community.general.flatpak": { + "name": [pkg.package_name for pkg in linux_packages], + "method": "system" if become else "user", + "state": "present", + }, + } + + +def make_script_install_task( + linux_package: LinuxPackage, + script_pkg: ScriptPackageMetadata, +) -> list[dict[str, Any]]: + """Generate an Ansible task list to install a Linux package via a script, optionally followed by post-install system packages. + + :param linux_package: Linux package metadata. + :param script_pkg: Script package metadata. + + :returns: List of Ansible task dictionaries. + + """ + temp_sh_path = tempfile.TemporaryDirectory(prefix=f"{linux_package.package_name}_installer_", delete=False) + + check_if_installed_step = { + "name": f"Check if {linux_package.package_name} is already installed", + "ansible.builtin.shell": { + "cmd": f"command -v {linux_package.executable_name}", + }, + "register": f"{linux_package.package_name}_check", + "ignore_errors": True, + "args": { + "executable": "/bin/bash", + }, + } + + download_step = { + "name": f"Download {linux_package.package_name} script installer", + "get_url": { + "url": script_pkg.url, + "dest": temp_sh_path.name + "/install.sh", + "mode": "0755", + "force": "yes", + }, + "when": f"{linux_package.package_name}_check.failed", + } + + install_step = { + "name": f"Install {linux_package.package_name} via script installer", + "ansible.builtin.shell": { + "cmd": temp_sh_path.name + "/install.sh " + " ".join(script_pkg.args or []), + }, + "args": { + "executable": f"/bin/{script_pkg.interpreter}", + }, + "environment": script_pkg.environment_vars, + "vars": script_pkg.ansible_vars, + "when": f"{linux_package.package_name}_check.failed", + } + + if script_pkg.post_install_packages is None: + return [check_if_installed_step, download_step, install_step] + + post_install_step = make_system_pkg_install_task(linux_package_names=script_pkg.post_install_packages, become=True) + post_install_step["when"] = f"{linux_package.package_name}_check.failed" + return [check_if_installed_step, download_step, install_step, post_install_step] + + +def package_selection_menu( + packages_dict: dict[str, list[str]] | dict[str, dict[str, Any]], +) -> set[str]: + """Generates selection menu from the packages dict and returns the user selection. + + :param packages_dict: A dict containing packages category as key and list of packages names as values. + + :returns: List of packages selected by the user. + + """ + + def expand_selection(selected: list[str]) -> set[str]: + expanded: set[str] = set() + for item in selected: + if item in packages_dict: + expanded.update(packages_dict[item]) + else: + expanded.add(item) + return expanded + + menu_style = Style( + [ + ("menu_header", "bold"), + ("selected", "bold fg:green"), + ] + ) + + flattened_choices: list[str | Choice] = ["All (all packages below)"] + for packages_category, packages_list in packages_dict.items(): + flattened_choices.append( + Choice( + title=[ + ("class:menu_header", packages_category.capitalize()), + ], + value=packages_category, + ) + ) + for item in packages_list: + flattened_choices.append(Choice(title=f" {item}", value=item)) + + selected_items = questionary.checkbox("Select the packages to install:", choices=flattened_choices, style=menu_style).ask() + + unique_selection: set[str] = set() + + # If user selects all packages then dump all packages into selection + if "All (all packages below)" in selected_items: + unique_selection.update(*packages_dict.values()) + negative_selection = expand_selection(selected_items[1:]) + return unique_selection.difference(negative_selection) + + unique_selection.update(expand_selection(selected_items)) + return unique_selection + + +def flatpak_install_wizard(packages_dict: dict[str, list[str]], method: Literal["System", "User"]) -> list[LinuxPackage]: + """Prompt the user to select Flatpak packages to install. + + :param packages_dict: Mapping of categories to Flatpak application IDs. + :param method: Installation scope (``"System"`` or ``"User"``). + + :returns: Selected Flatpak packages as :class:`LinuxPackage` objects. + + """ + selected_packages = package_selection_menu(packages_dict) + return LinuxPackage.to_flatpak_list(package_names=selected_packages, become=method == "System") + + +def system_package_wizard(packages_dict: dict[str, dict[str, Any]]) -> list[LinuxPackage]: + """Prompt the user to select system packages to install. + + Distro-specific installation metadata is selected automatically, with a fallback to ``default``. + + :param packages_dict: Mapping of package display names to installation metadata. + + :returns: Selected system packages as :class:`LinuxPackage` objects. + + """ + unique_selection = package_selection_menu(packages_dict) + + # Converts to LinuxPackage as well as picks the right installation method + # based on the current distro + distro_packages = [] + for packages_names in packages_dict.values(): + for package_display_name, package_metadata in packages_names.items(): + if package_display_name not in unique_selection: + continue + + current_distro = get_linux_distro_base() + installation_metadata = package_metadata.get(current_distro.value, package_metadata["default"]) + distro_packages.append(LinuxPackage.to_system_package(package_display_name=package_display_name, installation_metadata=installation_metadata)) + + return distro_packages + + +def generate_ansible_playbook_for_packages(packages: list[LinuxPackage], playbook_path: Path) -> None: + """Generates an Ansible playbook to install the given packages. + + :param packages: List of LinuxPackage instances to install. + :param playbook_path: Path where the generated playbook will be saved. + + """ + + flatpak_user_packages: list[LinuxPackage] = [] + flatpak_system_packages: list[LinuxPackage] = [] + system_packages: list[LinuxPackage] = [] + + scripts_install_tasks: list[dict[str, Any]] = [] + + for package in packages: + if package.method == PackageInstallMethod.FLATPAK: + if package.become: + flatpak_system_packages.append(package) + else: + flatpak_user_packages.append(package) + elif package.method == PackageInstallMethod.SYSTEM_PACKAGE: + system_packages.append(package) + elif package.method == PackageInstallMethod.SCRIPT: + if package.script_extra is None: + raise ValueError(f"Package {package.display_name} has method 'script' but no script metadata provided.") + scripts_install_tasks.extend(make_script_install_task(package, package.script_extra)) + + playbook_content: list[dict[str, Any]] = [ + { + "name": "Install selected packages", + "hosts": "localhost", + "connection": "local", + "gather_facts": True, + "tasks": [ + { + "name": "Add Flathub remote at user level if not already added", + "become": False, + "ansible.builtin.shell": {"cmd": "flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo"}, + }, + { + "name": "Add Flathub remote at system level if not already added", + "become": True, + "ansible.builtin.shell": {"cmd": "flatpak remote-add --system --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo"}, + }, + ], + }, + ] + + if flatpak_user_packages: + playbook_content[0]["tasks"].append(make_flatpak_install_task(linux_packages=flatpak_user_packages, become=False)) + + if flatpak_system_packages: + playbook_content[0]["tasks"].append(make_flatpak_install_task(linux_packages=flatpak_system_packages, become=True)) + + if system_packages: + playbook_content[0]["tasks"].append( + make_system_pkg_install_task( + linux_package_names=[x.package_name for x in system_packages], + become=True, + ) + ) + + if scripts_install_tasks: + playbook_content[0]["tasks"].extend(scripts_install_tasks) + + yaml_text = yaml.safe_dump( + playbook_content, + explicit_start=True, + sort_keys=False, + indent=4, + width=120, + default_flow_style=False, + ) + + task_pattern = re.compile(r"^\s*-\s+name:") + + lines = yaml_text.splitlines() + new_lines: list[str] = [] + for line in lines: + if task_pattern.match(line) and new_lines: + new_lines.append("") + new_lines.append(line) + + yaml_text = "\n".join(new_lines) + + with playbook_path.open("w") as fp: + fp.write(yaml_text) + + +def main() -> None: + playbooks_dir = CHEZMOI_DIR / "home" / "ansible_playbooks" + + flatpak_user_install_file = playbooks_dir / "flatpak_apps_user.yaml" + with flatpak_user_install_file.open("r") as fp: + file_content = yaml.safe_load(fp) + flatpak_user_packages = flatpak_install_wizard(packages_dict=file_content, method="User") + + flatpak_sys_install_file = playbooks_dir / "flatpak_apps_system.yaml" + with flatpak_sys_install_file.open("r") as fp: + file_content = yaml.safe_load(fp) + flatpak_sys_packages = flatpak_install_wizard(packages_dict=file_content, method="System") + + system_install_file = playbooks_dir / "system_apps.yaml" + with system_install_file.open("r") as fp: + file_content = yaml.safe_load(fp) + system_packages = system_package_wizard(packages_dict=file_content) + + combined_packages = [ + *flatpak_user_packages, + *flatpak_sys_packages, + *system_packages, + ] + + generate_ansible_playbook_for_packages( + combined_packages, + playbooks_dir / "universal_install_ansible_become_playbook.yaml", + ) + + +if __name__ == "__main__": + main() diff --git a/home/.chezmoiscripts/run_after_ansible_playbooks.py b/home/.chezmoiscripts/run_after_02_play_ansible_playbooks.py similarity index 81% rename from home/.chezmoiscripts/run_after_ansible_playbooks.py rename to home/.chezmoiscripts/run_after_02_play_ansible_playbooks.py index ab3f7bf..829cc7b 100755 --- a/home/.chezmoiscripts/run_after_ansible_playbooks.py +++ b/home/.chezmoiscripts/run_after_02_play_ansible_playbooks.py @@ -21,7 +21,7 @@ def load_env() -> dict[str, str]: return dict(os.environ) -cached_become_pass = None +cached_become_pass: questionary.Question | None = None def get_become_pass() -> questionary.Question: @@ -33,11 +33,11 @@ def get_become_pass() -> questionary.Question: password_confirmed = False while not password_confirmed: - become_pass = questionary.password("Enter sudo password for ansible:").ask() + become_pass: questionary.Question = questionary.password("Enter sudo password for ansible:").ask() result = subprocess.run( ["sudo", "-S", "whoami"], - input=become_pass + "\n", + input=str(become_pass) + "\n", capture_output=True, text=True, ) @@ -51,14 +51,10 @@ def get_become_pass() -> questionary.Question: return become_pass -def run_playbook( - playbook_name: str, playbook_info: PlaybookInfo, private_data_dir: str -) -> None: +def run_playbook(playbook_name: str, playbook_info: PlaybookInfo, private_data_dir: str) -> None: global cached_become_pass - playbook_path = ( - CHEZMOI_DIR / "home" / "ansible_playbooks" / playbook_info["filename"] - ) + playbook_path = CHEZMOI_DIR / "home" / "ansible_playbooks" / playbook_info["filename"] runner_args: dict[str, Any] = { "private_data_dir": private_data_dir, @@ -80,15 +76,11 @@ def run_playbook( print(f"Playbook {playbook_name} completed successfully.") -def main(): +def main() -> None: PLAYBOOKS: dict[str, PlaybookInfo] = {} + playbook_files = (CHEZMOI_DIR / "home" / "ansible_playbooks").glob("*_playbook.yaml") - for ansible_playbook in sorted( - (CHEZMOI_DIR / "home" / "ansible_playbooks").iterdir() - ): - if "setup" not in ansible_playbook.name: - continue - + for ansible_playbook in playbook_files: playbook: PlaybookInfo = { "filename": ansible_playbook.name, "sudo": True if "become" in ansible_playbook.name else False, diff --git a/home/.chezmoiscripts/run_git_setup.py b/home/.chezmoiscripts/run_git_setup.py old mode 100644 new mode 100755 index 6cf6150..f1dd9b5 --- a/home/.chezmoiscripts/run_git_setup.py +++ b/home/.chezmoiscripts/run_git_setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 from pathlib import Path -from posixpath import expanduser from git import GitConfigParser from platformdirs import user_config_path, user_data_path @@ -9,12 +8,13 @@ def create_local_git_config(account_name: str, email: str) -> Path: - """ - Creates a local .gitconfig file for a given account in a generated directory based on the account name. + """Creates a local .gitconfig file for a given account in a generated directory based on the account name. :param account_name: The name for this Git account. :param email: The email for this Git account. - :return: The path to the created .gitconfig file. + + :returns: The path to the created .gitconfig file. + """ git_configs_dir = CHEZMOI_DIR / "git_configs" / account_name.replace(" ", "_") @@ -29,12 +29,10 @@ def create_local_git_config(account_name: str, email: str) -> Path: def create_symlinks_for_all_gitconfigs() -> list[Path]: - """ - Loops through all directories in git_configs and prompts the user to provide - a file path for creating a symlink to each .gitconfig. Returns a list of paths - where the symlinks have been created. + """Loops through all directories in git_configs and prompts the user to provide a file path for creating a symlink to each .gitconfig. Returns a list of paths where the symlinks have been created. + + :returns: List of paths to the directories where the symlinks have been created. - :return: List of paths to the directories where the symlinks have been created. """ git_configs_base_dir = CHEZMOI_DIR / "git_configs" symlink_paths: set[Path] = set() @@ -44,11 +42,9 @@ def create_symlinks_for_all_gitconfigs() -> list[Path]: continue target_gitconfig_path = account_dir / ".gitconfig" - symlink_path = Path( - input( - f"Enter the file path where you want to create the symlink for {account_dir.name}_gitconfig: " - ).strip() - ).expanduser().absolute() + symlink_path = ( + Path(input(f"Enter the file path where you want to create the symlink for {account_dir.name}_gitconfig: ").strip()).expanduser().absolute() + ) symlink_gitconfig_path = symlink_path / ".gitconfig" if symlink_gitconfig_path.exists() or symlink_gitconfig_path.is_symlink(): symlink_gitconfig_path.unlink() @@ -62,13 +58,13 @@ def create_symlinks_for_all_gitconfigs() -> list[Path]: def create_global_gitconfig(git_account_dirs: list[Path]) -> None: - """ - This function creates a global Git config with includeIf set to correct paths. + """This function creates a global Git config with includeIf set to correct paths. - It assumes that git_account_dirs contains the paths to the directories with .gitconfig files. - It adds includeIf entries to the global Git config based on these directories. + It assumes that git_account_dirs contains the paths to the directories with .gitconfig files. It adds includeIf + entries to the global Git config based on these directories. :param git_account_dirs: List of paths to directories containing .gitconfig files. + """ git_global_config_dir = user_config_path("git", False) git_global_config_dir.mkdir(parents=True, exist_ok=True) @@ -108,11 +104,7 @@ def create_global_gitconfig(git_account_dirs: list[Path]) -> None: def main() -> None: - num_accounts = int( - input( - "How many Git accounts do you want to set up? (enter 0 to skip this step): " - ) - ) + num_accounts = int(input("How many Git accounts do you want to set up? (enter 0 to skip this step): ")) if num_accounts == 0: print("Skipping Git Account setup.") diff --git a/home/.chezmoiscripts/run_once_before_create_venv.sh b/home/.chezmoiscripts/run_once_before_create_venv.sh index 2443984..22d7d83 100755 --- a/home/.chezmoiscripts/run_once_before_create_venv.sh +++ b/home/.chezmoiscripts/run_once_before_create_venv.sh @@ -18,7 +18,7 @@ main() { exit 1 fi - pip install -r "$chezmoi_dir/home/requirements.txt" + pip install -r "$chezmoi_dir/requirements.txt" } main diff --git a/home/Syncthing/.gitkeep b/home/Syncthing/.keep similarity index 100% rename from home/Syncthing/.gitkeep rename to home/Syncthing/.keep diff --git a/home/ansible_playbooks/1_flatpak_setup__become.yaml b/home/ansible_playbooks/1_flatpak_setup__become.yaml deleted file mode 100644 index 1a00406..0000000 --- a/home/ansible_playbooks/1_flatpak_setup__become.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -- name: Flatpak and Flatpak Apps - hosts: localhost - connection: local - gather_facts: false - - tasks: - - name: Install Flatpak if not present - become: true - ansible.builtin.package: - name: flatpak - state: present - - - name: Add Flathub remote at user level if not already added - become: false - ansible.builtin.command: - cmd: flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - args: - creates: "{{ lookup('env', 'HOME') }}/.local/share/flatpak" diff --git a/home/ansible_playbooks/cosmic_setup.yaml b/home/ansible_playbooks/cosmic_setup.yaml deleted file mode 100644 index 3a86f0b..0000000 --- a/home/ansible_playbooks/cosmic_setup.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -- name: Cosmic Setup - hosts: localhost - connection: local - gather_facts: false - - tasks: - - name: Install Flatpak packages - become: false - community.general.flatpak: - name: - - dev.edfloreshz.CosmicTweaks - method: "user" - state: present diff --git a/home/ansible_playbooks/dev_setup__become.yaml b/home/ansible_playbooks/dev_setup__become.yaml deleted file mode 100644 index e267c0a..0000000 --- a/home/ansible_playbooks/dev_setup__become.yaml +++ /dev/null @@ -1,35 +0,0 @@ ---- -- name: Software Development Setup - hosts: localhost - connection: local - gather_facts: false - - tasks: - - name: Install packages - become: true - ansible.builtin.package: - name: - - code - - git - - neovim - - tmux - state: present - - - name: Install Flatpak packages - become: false - community.general.flatpak: - name: - - cc.arduino.IDE2 - - com.axosoft.GitKraken - - com.mongodb.Compass - - com.ultimaker.cura - - org.freecad.FreeCAD - - org.kde.kleopatra - method: "user" - state: present - - - name: Clone TPM - ansible.builtin.git: - repo: https://github.com/tmux-plugins/tpm - dest: ~/.config/tmux/plugins/tpm - version: master diff --git a/home/ansible_playbooks/flatpak_apps_setup.yaml b/home/ansible_playbooks/flatpak_apps_setup.yaml deleted file mode 100644 index 4605f89..0000000 --- a/home/ansible_playbooks/flatpak_apps_setup.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -- name: Flatpak and Flatpak Apps - hosts: localhost - connection: local - gather_facts: false - - tasks: - - name: Install Flatpak packages - become: false - community.general.flatpak: - name: - - dev.vencord.Vesktop - - com.github.tchx84.Flatseal - - com.google.Chrome - - com.usebottles.bottles - - io.github.flattool.Warehouse - - io.gitlab.librewolf-community - - org.remmina.Remmina - - io.github.seadve.Kooha - method: "user" - state: present diff --git a/home/ansible_playbooks/flatpak_apps_system.yaml b/home/ansible_playbooks/flatpak_apps_system.yaml new file mode 100644 index 0000000..471051e --- /dev/null +++ b/home/ansible_playbooks/flatpak_apps_system.yaml @@ -0,0 +1,4 @@ +# Config to install Flatpak applications at system level +# Read by home/.chezmoiscripts/run_after_ansible_playbooks.py +utilities: + - com.dec05eba.gpu_screen_recorder diff --git a/home/ansible_playbooks/flatpak_apps_user.yaml b/home/ansible_playbooks/flatpak_apps_user.yaml new file mode 100644 index 0000000..11fda96 --- /dev/null +++ b/home/ansible_playbooks/flatpak_apps_user.yaml @@ -0,0 +1,41 @@ +# Config to install Flatpak applications at user level +# Read by home/.chezmoiscripts/run_after_ansible_playbooks.py +3d_printing: + - com.ultimaker.cura + - org.freecad.FreeCAD +browsers: + - com.google.Chrome + - io.gitlab.librewolf-community +chatting: + - dev.vencord.Vesktop + - im.riot.Riot + - com.mattermost.Desktop +cosmic: + - dev.edfloreshz.CosmicTweaks +flatpak_utils: + - io.github.flattool.Warehouse + - com.github.tchx84.Flatseal +gaming: + - com.github.Matoking.protontricks + - com.heroicgameslauncher.hgl + - com.vysp3r.ProtonPlus +gnome: + - ca.desrt.dconf-editor + - com.mattjakeman.ExtensionManager +nvidia: + - io.github.ilya_zlobintsev.LACT +office: + - com.zettlr.Zettlr + - md.obsidian.Obsidian +programming: + - cc.arduino.IDE2 + - com.axosoft.GitKraken + - com.mongodb.Compass + - org.kde.kleopatra +utilities: + - com.usebottles.bottles + - io.github.seadve.Kooha + - org.cryptomator.Cryptomator + - org.filezillaproject.Filezilla + - org.freedesktop.Piper + - org.remmina.Remmina diff --git a/home/ansible_playbooks/gaming_setup__become.yaml b/home/ansible_playbooks/gaming_setup__become.yaml deleted file mode 100644 index 6099c61..0000000 --- a/home/ansible_playbooks/gaming_setup__become.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -- name: Setup Game Launchers - hosts: localhost - become: false - connection: local - gather_facts: false - - tasks: - - name: Install Game Launchers via Flatpak - community.general.flatpak: - name: - - com.heroicgameslauncher.hgl - - net.davidotek.pupgui2 - method: "user" - state: present - - - name: Install Steam via system package manager - become: true - ansible.builtin.package: - name: steam - state: present diff --git a/home/ansible_playbooks/gnome_setup.yaml b/home/ansible_playbooks/gnome_setup.yaml deleted file mode 100644 index 8901259..0000000 --- a/home/ansible_playbooks/gnome_setup.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -- name: Gnome Setup - hosts: localhost - connection: local - gather_facts: false - - tasks: - - name: Install Flatpak packages - become: false - community.general.flatpak: - name: - - ca.desrt.dconf-editor - - com.mattjakeman.ExtensionManager - method: "user" - state: present diff --git a/home/ansible_playbooks/node_setup.yaml b/home/ansible_playbooks/node_setup.yaml deleted file mode 100644 index 2798985..0000000 --- a/home/ansible_playbooks/node_setup.yaml +++ /dev/null @@ -1,43 +0,0 @@ ---- -- name: NVM and Node.js Setup with XDG_DATA_HOME - hosts: localhost - become: false - connection: local - gather_facts: true - - vars: - xdg_data_home: "{{ lookup('env', 'XDG_DATA_HOME') | default(ansible_env.HOME ~ '/.local/share', true) }}" - nvm_dir: "{{ xdg_data_home }}/nvm" - - tasks: - - name: Ensure NVM_DIR exists - file: - path: "{{ nvm_dir }}" - state: directory - mode: "0755" - tags: - - node - - - name: Install NVM - ansible.builtin.shell: > - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash - args: - executable: /bin/bash - creates: "{{ nvm_dir }}/nvm.sh" - environment: - NVM_DIR: "{{ nvm_dir }}" - XDG_DATA_HOME: "{{ xdg_data_home }}" - tags: - - node - - - name: Install latest Node.js using NVM - shell: | - export NVM_DIR="{{ nvm_dir }}" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - nvm install node - args: - executable: /bin/bash - environment: - NVM_DIR: "{{ nvm_dir }}" - tags: - - node diff --git a/home/ansible_playbooks/nvidia_setup__become.yaml b/home/ansible_playbooks/nvidia_setup__become.yaml deleted file mode 100644 index e4735ce..0000000 --- a/home/ansible_playbooks/nvidia_setup__become.yaml +++ /dev/null @@ -1,46 +0,0 @@ ---- -- name: Setup NVIDIA GPU Utilities and CoolerControl - hosts: localhost - become: false - connection: local - gather_facts: true - - tasks: - - name: Install NVIDIA GPU Utilities via Flatpak - community.general.flatpak: - name: - - com.leinardi.gwe - method: "user" - state: present - - - name: Install CoolerControl Download Dependencies - become: true - ansible.builtin.package: - name: - - curl - - apt-transport-https - state: present - when: ansible_os_family == "Debian" - - - name: Add CoolerControl setup script repository (Debian-based) - become: true - shell: | - curl -1sLf 'https://dl.cloudsmith.io/public/coolercontrol/coolercontrol/setup.deb.sh' | distro=ubuntu sudo -E bash - when: ansible_os_family == "Debian" - - - name: Install coolercontrol and coolercontrol-liqctld - become: true - ansible.builtin.package: - name: - - coolercontrol - - coolercontrol-liqctld - state: present - when: ansible_os_family == "Debian" - - - name: Enable and start coolercontrold service - become: true - ansible.builtin.systemd: - name: coolercontrold - enabled: yes - state: started - when: ansible_os_family == "Debian" diff --git a/home/ansible_playbooks/peripheral_drivers_setup__become.yaml b/home/ansible_playbooks/peripheral_drivers_setup__become.yaml deleted file mode 100644 index 0960ba9..0000000 --- a/home/ansible_playbooks/peripheral_drivers_setup__become.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -- name: Peripheral Drivers Setup - hosts: localhost - connection: local - gather_facts: true - - tasks: - - name: Install packages - become: true - ansible.builtin.package: - name: - - ratbagd - state: present - - - name: Install Flatpak packages - become: false - community.general.flatpak: - name: - - org.freedesktop.Piper - method: "user" - state: present diff --git a/home/ansible_playbooks/python_setup.yaml b/home/ansible_playbooks/python_setup.yaml deleted file mode 100644 index 06cd233..0000000 --- a/home/ansible_playbooks/python_setup.yaml +++ /dev/null @@ -1,30 +0,0 @@ ---- -- name: Python Setup - hosts: localhost - connection: local - gather_facts: true - - # Ensure uv is discoverable after installation by prepending ~/.local/bin to PATH - vars: - user_path: "{{ ansible_env.HOME }}/.local/bin:{{ ansible_env.PATH }}" - environment: - PATH: "{{ user_path }}" - - tasks: - - name: Install uv - ansible.builtin.shell: | - curl -LsSf https://astral.sh/uv/install.sh | env INSTALLER_NO_MODIFY_PATH=1 sh - args: - executable: /bin/bash - - - name: Install tldr via uv - ansible.builtin.shell: | - uv tool install tldr - args: - executable: /bin/bash - - - name: Install Poetry via uv - ansible.builtin.shell: | - uv tool install poetry - args: - executable: /bin/bash diff --git a/home/ansible_playbooks/rust_setup.yaml b/home/ansible_playbooks/rust_setup.yaml deleted file mode 100644 index 7354e75..0000000 --- a/home/ansible_playbooks/rust_setup.yaml +++ /dev/null @@ -1,28 +0,0 @@ ---- -- name: Rust Setup - hosts: localhost - become: false - connection: local - gather_facts: true - - tasks: - - name: check if cargo is installed - shell: command -v cargo - register: cargo_exists - ignore_errors: true - - - name: Download Installer - when: cargo_exists is failed - get_url: - url: https://sh.rustup.rs - dest: /tmp/sh.rustup.rs - mode: "0755" - force: "yes" - tags: - - rust - - - name: install rust/cargo - when: cargo_exists is failed - shell: /tmp/sh.rustup.rs -y - tags: - - rust diff --git a/home/ansible_playbooks/sync_storage_setup__become.yaml b/home/ansible_playbooks/sync_storage_setup__become.yaml deleted file mode 100644 index 28748b6..0000000 --- a/home/ansible_playbooks/sync_storage_setup__become.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -- name: Sync and Storage Setup - hosts: localhost - become: true - connection: local - gather_facts: true - - tasks: - - name: Install packages - ansible.builtin.package: - name: - - syncthing - state: present - - - name: Install rclone via official install script - ansible.builtin.shell: | - curl https://rclone.org/install.sh | bash - args: - executable: /bin/bash - ignore_errors: true - - - name: Install Flatpak packages - become: false - community.general.flatpak: - name: - - com.zettlr.Zettlr - - md.obsidian.Obsidian - - org.cryptomator.Cryptomator - - org.filezillaproject.Filezilla - method: "user" - state: present diff --git a/home/ansible_playbooks/system_apps.yaml b/home/ansible_playbooks/system_apps.yaml new file mode 100644 index 0000000..4d83c4d --- /dev/null +++ b/home/ansible_playbooks/system_apps.yaml @@ -0,0 +1,157 @@ +# Config to install system applications +# Read by home/.chezmoiscripts/run_after_ansible_playbooks.py +--- +gaming: + steam: + default: + become: true + executable_name: "steam" + method: "system_package" + system_package: "steam" +programming: + git: + default: + become: true + executable_name: "git" + method: "system_package" + system_package: "git" + neovim: + default: + become: true + executable_name: "nvim" + method: "system_package" + system_package: "neovim" + tmux: + default: + become: true + executable_name: "tmux" + method: "system_package" + system_package: "tmux" + vscode: + default: + become: true + executable_name: "code" + method: "system_package" + system_package: "code" +programming_languages: + nvm: + debian: + become: false + executable_name: "node" + method: "script" + script: + environment: + NVM_DIR: "{{ nvm_dir }}" + XDG_DATA_HOME: "{{ xdg_data_home }}" + PROFILE: "/dev/null" + url: "https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh" + vars: + nvm_dir: "{{ xdg_data_home }}/nvm" + xdg_data_home: "{{ lookup('env', 'XDG_DATA_HOME') | default(ansible_env.HOME ~ '/.local/share', true) }}" + default: + become: true + executable_name: "node" + method: "system_package" + system_package: "nvm" + rustup: + debian: + become: false + executable_name: "rustup" + method: "script" + script: + interpreter: "bash" + url: "https://sh.rustup.rs" + args: + - "-y" + - "--no-modify-path" + default: + become: true + executable_name: "rustup" + method: "system_package" + system_package: "rustup" + uv: + debian: + become: false + executable_name: "uv" + method: "script" + script: + environment: + INSTALLER_NO_MODIFY_PATH: "1" + interpreter: "bash" + url: "https://astral.sh/uv/install.sh" + default: + become: true + executable_name: "uv" + method: "system_package" + system_package: "uv" +sync_storage: + rclone: + debian: + become: false + executable_name: "rclone" + method: "script" + script: + interpreter: "bash" + url: "https://rclone.org/install.sh" + default: + become: true + executable_name: "rclone" + method: "system_package" + system_package: "rclone" + syncthing: + default: + become: true + executable_name: "syncthing" + method: "system_package" + system_package: "syncthing" +utilities: + cooler-control: + default: + become: true + executable_name: "coolercontrol" + method: "system_package" + system_package: "coolercontrol" + arch: + executable_name: "coolercontrol" + method: "paru" + paru: "coolercontrol-bin" + debian: + become: true + executable_name: "coolercontrol" + method: "script" + script: + interpreter: "bash" + post_install_packages: + - "coolercontrol" + - "liquidctl" + url: "https://dl.cloudsmith.io/public/coolercontrol/coolercontrol/setup.deb.sh" + curl: + default: + become: true + executable_name: "curl" + method: "system_package" + system_package: "curl" + exfatprogs: + default: + become: true + executable_name: "mkfs.exfat" + method: "system_package" + system_package: "exfatprogs" + ratbagd: + default: + become: true + executable_name: "ratbagd" + method: "system_package" + system_package: "ratbagd" + ripgrep: + default: + become: true + executable_name: "rg" + method: "system_package" + system_package: "ripgrep" + xdg-user-dirs: + default: + become: true + executable_name: "xdg-user-dirs-update" + method: "system_package" + system_package: "xdg-user-dirs" diff --git a/home/ansible_playbooks/system_utils_setup__become.yaml b/home/ansible_playbooks/system_utils_setup__become.yaml deleted file mode 100644 index 1e5d168..0000000 --- a/home/ansible_playbooks/system_utils_setup__become.yaml +++ /dev/null @@ -1,22 +0,0 @@ ---- -- name: System Utils Setup - hosts: localhost - become: true - connection: local - gather_facts: true - - tasks: - - name: Install packages - ansible.builtin.package: - name: - - curl - - exfatprogs - - ratbagd - - ripgrep - - xdg-user-dirs - state: present - - - name: Uninstall packages - ansible.builtin.package: - name: firefox - state: absent diff --git a/home/dot_config/bash/program_configs.sh b/home/dot_config/bash/program_configs.sh index 986cbd0..bf1eee0 100644 --- a/home/dot_config/bash/program_configs.sh +++ b/home/dot_config/bash/program_configs.sh @@ -6,7 +6,9 @@ # Cargo: Rust build system if [ -d "$XDG_DATA_HOME/cargo" ]; then # shellcheck source="$XDG_DATA_HOME/cargo/env" - . "$XDG_DATA_HOME/cargo/env" + if [ -f "$XDG_DATA_HOME/cargo/env" ]; then + . "$XDG_DATA_HOME/cargo/env" + fi fi # Nvm: Node version manager @@ -17,7 +19,3 @@ fi if [ -d "$XDG_DATA_HOME/JetBrains/Toolbox/scripts" ] && ! echo "$PATH" | grep -q "$XDG_DATA_HOME/JetBrains/Toolbox/scripts"; then export PATH="$PATH:$XDG_DATA_HOME/JetBrains/Toolbox/scripts" fi - -# pyenv: Python version manager -# command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH" -# eval "$(pyenv init -)" diff --git a/home/dot_config/git/.gitkeep b/home/dot_config/git/.keep similarity index 100% rename from home/dot_config/git/.gitkeep rename to home/dot_config/git/.keep diff --git a/home/dot_config/putty/.gitkeep b/home/dot_config/putty/.keep similarity index 100% rename from home/dot_config/putty/.gitkeep rename to home/dot_config/putty/.keep diff --git a/home/dot_local/share/private_gnupg/.gitkeep b/home/dot_local/share/nvm/.keep similarity index 100% rename from home/dot_local/share/private_gnupg/.gitkeep rename to home/dot_local/share/nvm/.keep diff --git a/home/dot_local/share/private_gnupg/.keep b/home/dot_local/share/private_gnupg/.keep new file mode 100644 index 0000000..e69de29 diff --git a/home/ppa_scripts/.keep b/home/ppa_scripts/.keep new file mode 100644 index 0000000..e69de29 diff --git a/home/requirements.txt b/home/requirements.txt deleted file mode 100644 index 8748d2d..0000000 --- a/home/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -ansible -ansible-runner -GitPython -platformdirs -questionary \ No newline at end of file diff --git a/install.sh b/install.sh deleted file mode 100755 index 62ae455..0000000 --- a/install.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -set -e # -e: exit on error - -if [ ! "$(command -v chezmoi)" ]; then - bin_dir="$HOME/.local/bin" - chezmoi="$bin_dir/chezmoi" - if [ "$(command -v curl)" ]; then - sh -c "$(curl -fsSL https://git.io/chezmoi)" -- -b "$bin_dir" - elif [ "$(command -v wget)" ]; then - sh -c "$(wget -qO- https://git.io/chezmoi)" -- -b "$bin_dir" - else - echo "To install chezmoi, you must have curl or wget installed." >&2 - exit 1 - fi -else - chezmoi=chezmoi -fi - -# POSIX way to get script's dir: https://stackoverflow.com/a/29834779/12156188 -script_dir="$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P)" -# exec: replace current process with chezmoi init -exec "$chezmoi" init --apply "--source=$script_dir" --verbose \ No newline at end of file diff --git a/justfile b/justfile new file mode 100644 index 0000000..c746744 --- /dev/null +++ b/justfile @@ -0,0 +1,44 @@ +# Justfile for easily running commands related to my dotfiles managed with chezmoi. + +just-fmt: + just --fmt --unstable + +# Runs the formatter for python scripts in the .chezmoiscripts directory. +fmt: + ruff format home/.chezmoiscripts/ + docstrfmt home/.chezmoiscripts/*.py -v + +checker: + mypy home/.chezmoiscripts/ + +# Creates python .venv +create-venv: + python3 -m venv .venv + +install-deps: + .venv/bin/pip install --upgrade pip + .venv/bin/pip install -r requirements.txt + +# Apply all the changes including running any scripts that are part of the dotfiles. +chezmoi-apply-all: + chezmoi --verbose apply + +# Apply the changes only for specified types like file changes, symlinks, and templates. So basically no scripts are run. +chezmoi-apply-files: + chezmoi --verbose -i dirs,files,remove,symlinks,templates apply + +# Pull changes from a remote repository and apply them +chezmoi-update: + chezmoi update + +# Resets the state of the script execution state (states like before/after script execution, etc.) +reset-chezmoi-script-state: + chezmoi state delete-bucket --bucket=scriptState + +# Run the generate playbook python script +run-gen-pb: + .venv/bin/python home/.chezmoiscripts/run_after_01_gen_ansible_playbooks.py + +# Run the python script to play the generated playbooks +run-play-pb: + .venv/bin/python home/.chezmoiscripts/run_after_02_play_ansible_playbooks.py diff --git a/pre_push.py b/pre_push.py new file mode 100755 index 0000000..6d2ad0d --- /dev/null +++ b/pre_push.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +"""Provides simple way to run formatter/linter/static analysis/tests on the project.""" + +import sys +from subprocess import CalledProcessError, check_call +from pathlib import Path + +def do_process(args: list[str], cwd: str = ".") -> bool: + """Run program provided by args. + + Returns ``True`` upon success. + + Output failed message on non-zero exit and return False. + + Exit if command is not found. + + """ + print(f"Running: {' '.join(args)}") + try: + check_call(args, shell=False, cwd=cwd) + except CalledProcessError: + print(f"\nFailed: {' '.join(args)}") + return False + except Exception as exc: + print(f"{exc!s}\n", file=sys.stderr) + raise SystemExit(1) from exc + return True + + +def run_static_and_lint() -> bool: + """Runs the static analysis and linting. + + :returns: False if everything ran correctly. Otherwise, it will return True + + """ + success = True + success &= do_process(["ruff", "format", "home/.chezmoiscripts/"]) + success &= do_process(["docstrfmt", "home/.chezmoiscripts/*.py", "-v"]) + return success + + +def main() -> int: + """Runs the main function.""" + success = True + try: + success &= run_static_and_lint() + except KeyboardInterrupt: + return int(not False) + return int(not success) + + +if __name__ == "__main__": + exit_code = main() + print("\npre_push.py: Success!" if not exit_code else "\npre_push.py: Fail") + sys.exit(exit_code) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..682931e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,61 @@ +[project] +name = "chezmoi" +version = "0.1.0" +description = "Yoshikage Kira's dotfiles, managed with chezmoi" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "ansible>=13.3.0", + "ansible-runner>=2.4.2", + "gitpython>=3.1.46", + "platformdirs>=4.5.1", + "questionary>=2.1.1", +] + +[tool.mypy] +strict = true + +disallow_any_decorated = false +disallow_any_explicit = false +disallow_any_expr = false +disallow_any_generics = true +disallow_any_unimported = true +disallow_subclassing_any = true + +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +no_implicit_reexport = true +show_error_codes = true +strict_equality = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true +warn_unused_ignores = true + +exclude = '(venv|\.venv)/' + +[[tool.mypy.overrides]] +module = "ansible_runner.*" +ignore_missing_imports = true + +[tool.ruff] +line-length = 160 + +[tool.ruff.format] +docstring-code-format = true +docstring-code-line-length = 120 + +[tool.docstrfmt] +line-length = 120 + +[dependency-groups] +linting = ["docstrfmt>=2.0.2", "ruff>=0.15.0"] +typing = [ + "mypy>=1.19.1", + "types-pyyaml>=6.0.12.20250915", +] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0bed93b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,271 @@ +# This file was autogenerated by uv via the following command: +# uv export --all-packages -o requirements.txt +ansible==13.3.0 \ + --hash=sha256:59638dc893088353254b2e589ac732012a0a005ad29d8d859ffecf52b5c1dc90 \ + --hash=sha256:7e7ed052c8ee7b1449fb285d0e1dac0029f6440c70ce9f473af6c1a31887bb84 + # via chezmoi +ansible-core==2.20.2 \ + --hash=sha256:1bbd101e3e3b1ace91d8be123007050f7efd94c4c78bbeb9e45ad1c7016d08ef \ + --hash=sha256:75e19a3ad8cf659579ea182cdf948ee0900d700e564802e92876de53dbd9715d + # via ansible +ansible-runner==2.4.2 \ + --hash=sha256:0bde6cb39224770ff49ccdc6027288f6a98f4ed2ea0c64688b31217033221893 \ + --hash=sha256:331d4da8d784e5a76aa9356981c0255f4bb1ba640736efe84b0bd7c73a4ca420 + # via chezmoi +cffi==2.0.0 ; platform_python_implementation != 'PyPy' \ + --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \ + --hash=sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b \ + --hash=sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f \ + --hash=sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9 \ + --hash=sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c \ + --hash=sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75 \ + --hash=sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e \ + --hash=sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e \ + --hash=sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25 \ + --hash=sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe \ + --hash=sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b \ + --hash=sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91 \ + --hash=sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592 \ + --hash=sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187 \ + --hash=sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1 \ + --hash=sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94 \ + --hash=sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba \ + --hash=sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529 \ + --hash=sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca \ + --hash=sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6 \ + --hash=sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4 \ + --hash=sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d \ + --hash=sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b \ + --hash=sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205 \ + --hash=sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27 \ + --hash=sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512 \ + --hash=sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d \ + --hash=sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c \ + --hash=sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037 \ + --hash=sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c \ + --hash=sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8 \ + --hash=sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9 \ + --hash=sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775 \ + --hash=sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc \ + --hash=sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062 \ + --hash=sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13 \ + --hash=sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26 \ + --hash=sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b \ + --hash=sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6 \ + --hash=sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c \ + --hash=sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef \ + --hash=sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5 \ + --hash=sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18 \ + --hash=sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad \ + --hash=sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3 \ + --hash=sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2 \ + --hash=sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5 + # via cryptography +cryptography==46.0.4 \ + --hash=sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa \ + --hash=sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc \ + --hash=sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da \ + --hash=sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255 \ + --hash=sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2 \ + --hash=sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485 \ + --hash=sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0 \ + --hash=sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d \ + --hash=sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616 \ + --hash=sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0 \ + --hash=sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908 \ + --hash=sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81 \ + --hash=sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc \ + --hash=sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd \ + --hash=sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b \ + --hash=sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019 \ + --hash=sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7 \ + --hash=sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973 \ + --hash=sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b \ + --hash=sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5 \ + --hash=sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef \ + --hash=sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0 \ + --hash=sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b \ + --hash=sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e \ + --hash=sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c \ + --hash=sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2 \ + --hash=sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af \ + --hash=sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4 \ + --hash=sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab \ + --hash=sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82 \ + --hash=sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59 \ + --hash=sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061 \ + --hash=sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085 \ + --hash=sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b \ + --hash=sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263 \ + --hash=sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e \ + --hash=sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829 \ + --hash=sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4 \ + --hash=sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c \ + --hash=sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f \ + --hash=sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095 \ + --hash=sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32 \ + --hash=sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976 + # via ansible-core +gitdb==4.0.12 \ + --hash=sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571 \ + --hash=sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf + # via gitpython +gitpython==3.1.46 \ + --hash=sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f \ + --hash=sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058 + # via chezmoi +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via ansible-core +lockfile==0.12.2 \ + --hash=sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799 \ + --hash=sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa + # via python-daemon +markupsafe==3.0.3 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via jinja2 +packaging==26.0 \ + --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 + # via + # ansible-core + # ansible-runner +pexpect==4.9.0 \ + --hash=sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 \ + --hash=sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f + # via ansible-runner +platformdirs==4.5.1 \ + --hash=sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda \ + --hash=sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31 + # via chezmoi +prompt-toolkit==3.0.52 \ + --hash=sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855 \ + --hash=sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955 + # via questionary +ptyprocess==0.7.0 \ + --hash=sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35 \ + --hash=sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220 + # via pexpect +pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' \ + --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ + --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 + # via cffi +python-daemon==3.1.2 \ + --hash=sha256:b906833cef63502994ad48e2eab213259ed9bb18d54fa8774dcba2ff7864cec6 \ + --hash=sha256:f7b04335adc473de877f5117e26d5f1142f4c9f7cd765408f0877757be5afbf4 + # via ansible-runner +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via + # ansible-core + # ansible-runner +questionary==2.1.1 \ + --hash=sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d \ + --hash=sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59 + # via chezmoi +resolvelib==1.2.1 \ + --hash=sha256:7d08a2022f6e16ce405d60b68c390f054efcfd0477d4b9bd019cc941c28fad1c \ + --hash=sha256:fb06b66c8da04172d9e72a21d7d06186d8919e32ae5ab5cdf5b9d920be805ac2 + # via ansible-core +smmap==5.0.2 \ + --hash=sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5 \ + --hash=sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e + # via gitdb +wcwidth==0.6.0 \ + --hash=sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad \ + --hash=sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159 + # via prompt-toolkit