diff --git a/Makefile b/Makefile index ad20b5c..74f6e4d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help bootstrap lint test check apply dev local clean status tailscale tailscale-check tailscale-dev tailscale-status create-vault create-vm monitoring +.PHONY: help bootstrap lint test check dev datascience inventory inventory-all local clean status tailscale tailscale-check tailscale-dev tailscale-status create-vault create-vm monitoring .DEFAULT_GOAL := help ## Colors for output @@ -226,10 +226,6 @@ check-local: ## Dry-run the local playbook @echo "$(YELLOW)Running dry-run on localhost...$(RESET)" $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_LOCAL) --check --diff -K -apply: auto-fallback ## Run the development playbook on all dev hosts - @echo "$(YELLOW)Applying development playbook...$(RESET)" - $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) - site: ## Run the complete site playbook @echo "$(YELLOW)Running complete site deployment...$(RESET)" $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_SITE) @@ -248,6 +244,42 @@ endif @echo "$(YELLOW)Running on host: $(HOST)$(RESET)" $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --limit $(HOST) +# Data science role +datascience: ## Install data science stack (usage: make datascience HOST=server01) +ifndef HOST + @echo "$(RED)Error: HOST parameter required$(RESET)" + @echo "Usage: make datascience HOST=server01" + @exit 1 +endif + @echo "$(YELLOW)Installing data science stack on: $(HOST)$(RESET)" + $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --limit $(HOST) --tags datascience + +# Inventory system +inventory: ## Show installed software on host (usage: make inventory HOST=dev01) +ifndef HOST + @echo "$(RED)Error: HOST parameter required$(RESET)" + @echo "Usage: make inventory HOST=dev01" + @exit 1 +endif + @echo "$(BOLD)Software Inventory for: $(HOST)$(RESET)" + @echo "" + @ip=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_host // empty" 2>/dev/null); \ + user=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_user // empty" 2>/dev/null); \ + if [ -n "$$ip" ] && [ "$$ip" != "null" ] && [ -n "$$user" ] && [ "$$user" != "null" ]; then \ + scp -q scripts/inventory.sh $$user@$$ip:/tmp/inventory.sh 2>/dev/null && \ + ssh $$user@$$ip 'bash /tmp/inventory.sh; rm /tmp/inventory.sh' 2>/dev/null; \ + else \ + echo "$(RED)Could not determine IP or user for $(HOST)$(RESET)"; \ + fi + +inventory-all: ## Show installed software on all hosts + @echo "$(BOLD)Software Inventory - All Hosts$(RESET)\n" + @for host in $$(ansible all --list-hosts 2>/dev/null | grep -v "hosts" | tr -d ' '); do \ + echo "$(YELLOW)=== $$host ===$(RESET)"; \ + ansible $$host -m script -a "scripts/inventory.sh" 2>/dev/null | sed -n '/CHANGED/,$$p' | tail -n +3 || echo "$(RED)Failed to connect$(RESET)"; \ + echo ""; \ + done + # Tag-based execution security: ## Run only security-related roles @echo "$(YELLOW)Running security roles...$(RESET)" diff --git a/inventories/production/group_vars/all.yml b/inventories/production/group_vars/all.yml deleted file mode 100644 index 7ba15c1..0000000 --- a/inventories/production/group_vars/all.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- -# Common variables for all hosts -timezone: America/Toronto -locale: en_US.UTF-8 -ansible_python_interpreter: /usr/bin/python3 - -# Debug settings -ansible_debug_output: false - -# Security settings -fail2ban_bantime: 3600 -fail2ban_findtime: 600 -fail2ban_maxretry: 3 - -# Maintenance settings -maintenance_default_serial: "100%" # Default serial execution for maintenance -maintenance_reboot_timeout: 300 # Reboot timeout in seconds -maintenance_pre_reboot_delay: 5 # Delay before reboot in seconds - -# Global variables for all hosts - -# Tailscale configuration -# Store your actual auth key in vault_tailscale_auth_key using ansible-vault -# Example: ansible-vault create group_vars/all/vault.yml -# vault_tailscale_auth_key: "tskey-auth-your-actual-key-here" - -# Default Tailscale settings - these tell the playbook to use your vault key -tailscale_auth_key: "{{ vault_tailscale_auth_key | default('') }}" -tailscale_accept_routes: true -tailscale_accept_dns: true -tailscale_ssh: true -tailscale_hostname: "{{ inventory_hostname }}" diff --git a/inventories/production/group_vars/all/main.yml b/inventories/production/group_vars/all/main.yml index 7ba15c1..cc22ada 100644 --- a/inventories/production/group_vars/all/main.yml +++ b/inventories/production/group_vars/all/main.yml @@ -28,5 +28,5 @@ maintenance_pre_reboot_delay: 5 # Delay before reboot in seconds tailscale_auth_key: "{{ vault_tailscale_auth_key | default('') }}" tailscale_accept_routes: true tailscale_accept_dns: true -tailscale_ssh: true +tailscale_ssh: false tailscale_hostname: "{{ inventory_hostname }}" diff --git a/inventories/production/host_vars/devGPU.yml b/inventories/production/host_vars/devGPU.yml new file mode 100644 index 0000000..02df7a6 --- /dev/null +++ b/inventories/production/host_vars/devGPU.yml @@ -0,0 +1,31 @@ +ansible_become_password: root + +ansible_python_interpreter: /usr/bin/python3 + +# Shell configuration +# ansible_user (root) is configured by default +# Add additional users here if needed: +shell_additional_users: + - devuser01 + - devuser02 + - dev + +# Data Science configuration (datascience role) +install_conda: true +conda_install_path: "/root/anaconda3" +install_jupyter: true +jupyter_port: 8888 +jupyter_allow_remote: true +jupyter_bind_all_interfaces: true + +# R configuration +install_r: true + +# Cursor IDE configuration +install_cursor_extensions: true + +# Cursor extension groups to enable +install_python: true # Python development +install_jupyter: true # Jupyter notebooks +install_r: true # R language support +install_docs: true # Markdown/documentation diff --git a/inventories/production/hosts b/inventories/production/hosts index 2508a2b..6259fc4 100644 --- a/inventories/production/hosts +++ b/inventories/production/hosts @@ -13,13 +13,13 @@ portainerVM ansible_host=10.0.30.69 ansible_user=ladmin homepageVM ansible_host=10.0.30.12 ansible_user=homepage [vaultwarden] -vaultwardenVM ansible_host=100.100.19.11 ansible_host_fallback=10.0.10.142 ansible_user=ladmin +vaultwardenVM ansible_host=100.100.19.11 ansible_host_fallback=10.0.10.142 ansible_user=root [dev] dev01 ansible_host=10.0.30.105 ansible_user=ladmin bottom ansible_host=10.0.10.156 ansible_user=beast debianDesktopVM ansible_host=10.0.10.206 ansible_user=user skip_reboot=true - +devGPU ansible_host=10.0.30.63 ansible_user=root [ansible] ansibleVM ansible_host=10.0.10.157 ansible_user=master @@ -28,8 +28,8 @@ ansibleVM ansible_host=10.0.10.157 ansible_user=master tailscaleVM ansible_host=100.66.218.53 ansible_user=ladmin [services] -caddy ansible_host=100.117.106.18 ansible_host_fallback=10.0.10.50 ansible_user=ladmin -jellyfin ansible_host=100.104.109.45 ansible_host_fallback=10.0.10.232 ansible_user=user +caddy ansible_host=100.117.106.18 ansible_host_fallback=10.0.10.50 ansible_user=root +jellyfin ansible_host=100.104.109.45 ansible_host_fallback=10.0.10.232 ansible_user=root listmonk ansible_host=100.73.190.115 ansible_host_fallback=10.0.10.149 ansible_user=ladmin slack ansible_host=100.110.190.69 ansible_host_fallback=10.0.10.154 ansible_user=ladmin diff --git a/playbooks/development.yml b/playbooks/development.yml index e293367..7b07e01 100644 --- a/playbooks/development.yml +++ b/playbooks/development.yml @@ -11,10 +11,10 @@ - {role: ssh, tags: ['ssh', 'security']} - {role: shell, tags: ['shell']} - {role: development, tags: ['development', 'dev']} + - {role: datascience, tags: ['datascience', 'conda', 'jupyter', 'r']} - {role: docker, tags: ['docker']} - {role: applications, tags: ['applications', 'apps']} - - {role: snap, tags: ['snap', 'apps']} - - {role: tailscale, tags: ['tailscale', 'vpn']} + # - {role: tailscale, tags: ['tailscale', 'vpn']} - {role: monitoring, tags: ['monitoring']} pre_tasks: diff --git a/playbooks/local.yml b/playbooks/local.yml index 15ebb9d..ca60722 100644 --- a/playbooks/local.yml +++ b/playbooks/local.yml @@ -13,8 +13,7 @@ - {role: development, tags: ['development', 'dev']} - {role: docker, tags: ['docker']} - {role: applications, tags: ['applications', 'apps']} - - {role: snap, tags: ['snap', 'apps']} - - {role: tailscale, tags: ['tailscale', 'vpn']} + # - {role: tailscale, tags: ['tailscale', 'vpn']} - {role: monitoring, tags: ['monitoring']} pre_tasks: diff --git a/playbooks/shell.yml b/playbooks/shell.yml index 8828524..1cfb4d2 100644 --- a/playbooks/shell.yml +++ b/playbooks/shell.yml @@ -1,6 +1,6 @@ --- # Playbook: shell.yml -# Purpose: Configure shell environment (zsh, oh-my-zsh, plugins) on all hosts +# Purpose: Configure shell environment (zsh, oh-my-zsh, plugins) # Targets: all hosts # Tags: shell # Usage: make shell-all @@ -9,6 +9,8 @@ hosts: all become: true strategy: free + ignore_errors: true + ignore_unreachable: true roles: - {role: shell, tags: ['shell']} diff --git a/roles/base/README.md b/roles/base/README.md index 225dd44..60b21bb 100644 --- a/roles/base/README.md +++ b/roles/base/README.md @@ -1,38 +1,64 @@ -Role Name -========= +# Role: base -A brief description of the role goes here. +## Description +Installs core system packages and utilities required by all other roles. This is the foundation role that should be applied to all managed hosts. -Requirements ------------- +## Requirements +- Ansible 2.9+ +- Debian/Ubuntu systems +- Root or sudo access -Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. +## Installed Packages -Role Variables --------------- +### Base Utilities +- curl, wget - Download tools +- unzip - Archive extraction +- xclip - Clipboard utility +- tree - Directory visualization -A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. +### Network & Admin Tools +- net-tools - Network configuration +- ufw - Uncomplicated Firewall +- mailutils - Email utilities -Dependencies ------------- +### Modern CLI Tools +- jq - JSON processor +- yq - YAML processor (from apt or GitHub binary) +- ripgrep - Fast text search +- fd-find - Fast file finder -A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. +## Configuration Tasks +- Sets system timezone (default: UTC) +- Configures system locale (default: en_US.UTF-8) +- Creates Ansible temporary directory with proper permissions +- Creates fd symlink for Ubuntu compatibility -Example Playbook ----------------- +## Variables -Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: +| Variable | Default | Description | +|----------|---------|-------------| +| `timezone` | `UTC` | System timezone | +| `locale` | `en_US.UTF-8` | System locale | - - hosts: servers - roles: - - { role: username.rolename, x: 42 } +## Dependencies +None - this is the foundation role. -License -------- +## Example Playbook -BSD +```yaml +- hosts: all + roles: + - role: base + timezone: America/Toronto + locale: en_US.UTF-8 +``` -Author Information ------------------- +## Tags +- `base`: All base tasks +- `security`: Security-related tasks -An optional section for the role authors to include contact information, or a website (HTML is not allowed). +## Notes +- This role should be applied before all other roles +- yq is installed from apt if available, otherwise from GitHub binary +- fd-find is symlinked to fd for compatibility across distributions +- UFW firewall is installed but not enabled (handled by SSH role) diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml index 9fc7544..57f34bc 100644 --- a/roles/base/tasks/main.yml +++ b/roles/base/tasks/main.yml @@ -27,11 +27,27 @@ - fd-find state: present -- name: Install modern tools via snap - community.general.snap: - name: - - yq +- name: Install yq YAML processor + ansible.builtin.apt: + name: yq state: present + update_cache: false + failed_when: false + register: yq_apt_install + +- name: Install yq from binary if apt fails + when: yq_apt_install.failed or yq_apt_install is not succeeded + block: + - name: Download yq binary + ansible.builtin.get_url: + url: https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 + dest: /usr/local/bin/yq + mode: '0755' + register: yq_download + + - name: Verify yq installation + ansible.builtin.command: yq --version + changed_when: false - name: Create fd symlink (Ubuntu uses fd-find) ansible.builtin.file: diff --git a/roles/datascience/README.md b/roles/datascience/README.md new file mode 100644 index 0000000..7e14cbd --- /dev/null +++ b/roles/datascience/README.md @@ -0,0 +1,185 @@ +# Role: datascience + +## Description +Installs and configures a complete data science environment including Anaconda/Conda, Jupyter Notebook, and R statistical computing language. This role is optional and separate from general development tools for faster deployments when data science capabilities are not needed. + +## Requirements +- Ansible 2.9+ +- Debian/Ubuntu systems +- At least 5GB free disk space (Anaconda is ~700MB) +- Root or sudo access + +## Installed Components + +### Anaconda/Conda +- Full Anaconda3 distribution (latest version) +- Python data science packages (pandas, numpy, matplotlib, scikit-learn) +- Conda package manager +- Initialized for bash (zsh initialization via shell role) + +### Jupyter Notebook +- Jupyter Notebook server +- IPython kernel +- JupyterLab interface +- Systemd service for automatic startup +- Web-based access on configurable port + +### R Language +- R base and development packages +- R recommended packages +- CRAN repository configuration +- IRkernel for Jupyter integration +- Common R packages + +## Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `install_conda` | `false` | Install Anaconda/Conda | +| `conda_install_path` | `{{ ansible_env.HOME }}/anaconda3` | Conda installation directory | +| `install_jupyter` | `false` | Install Jupyter Notebook (requires conda) | +| `jupyter_port` | `8888` | Jupyter web server port | +| `jupyter_bind_all_interfaces` | `true` | Listen on all network interfaces | +| `jupyter_allow_remote` | `true` | Allow remote connections | +| `install_r` | `false` | Install R language | +| `r_packages` | `[r-base, r-base-dev, r-recommended]` | R packages to install | + +## Dependencies +- `base` role (for core utilities) + +## Example Playbook + +### Full Data Science Stack +```yaml +- hosts: datascience_servers + roles: + - role: datascience + install_conda: true + install_jupyter: true + install_r: true +``` + +### Conda + Jupyter Only +```yaml +- hosts: jupyter_servers + roles: + - role: datascience + install_conda: true + install_jupyter: true + install_r: false +``` + +### R Only (no Python) +```yaml +- hosts: r_servers + roles: + - role: datascience + install_conda: false + install_r: true +``` + +## Usage + +### Installation +```bash +# Install full data science stack +make datascience HOST=server01 + +# Install on specific host with custom vars +ansible-playbook playbooks/development.yml --limit server01 --tags datascience \ + -e "install_conda=true install_jupyter=true install_r=true" +``` + +### Post-Installation + +#### Set Jupyter Password +```bash +jupyter notebook password +``` + +#### Access Jupyter +```bash +# Local +http://localhost:8888 + +# Remote +http://your-server-ip:8888 +``` + +#### Verify Installations +```bash +conda --version +jupyter --version +R --version +``` + +## Tags +- `datascience`: All data science tasks +- `conda`: Conda/Anaconda installation only +- `jupyter`: Jupyter Notebook installation only +- `r`, `rstats`: R language installation only + +## Services + +### Jupyter Notebook Service +- **Service name**: `jupyter-notebook.service` +- **Start**: `systemctl start jupyter-notebook` +- **Status**: `systemctl status jupyter-notebook` +- **Logs**: `journalctl -u jupyter-notebook` + +## Performance Notes + +### Installation Times +- **Anaconda**: 5-10 minutes (700MB download) +- **Jupyter**: 2-5 minutes (if Anaconda already installed) +- **R**: 10-30 minutes (compilation of packages) + +### Disk Space +- **Anaconda**: ~2.5GB after installation +- **R + packages**: ~500MB +- **Total**: ~3GB for full stack + +## Security Considerations + +1. **Jupyter Password**: Always set a password after installation +2. **Firewall**: Ensure port 8888 (or custom port) is properly firewalled +3. **HTTPS**: Consider using HTTPS for remote access +4. **User Isolation**: Jupyter runs as the ansible user by default + +## Troubleshooting + +### Conda Not in PATH +```bash +# Add to .bashrc or .zshrc +export PATH="$HOME/anaconda3/bin:$PATH" +``` + +### Jupyter Service Won't Start +```bash +# Check logs +journalctl -u jupyter-notebook -n 50 + +# Verify conda is accessible +/root/anaconda3/bin/jupyter --version +``` + +### R Package Installation Fails +```bash +# Install manually in R +R +> install.packages("IRkernel") +``` + +## Integration with Other Roles + +- **Shell Role**: Provides zsh with conda integration +- **Monitoring**: btop/htop for resource monitoring +- **Docker**: Can run Jupyter in containers alternatively + +## Notes +- Anaconda installer is cleaned up after installation +- Conda init for zsh is handled by the shell role +- IRkernel is automatically installed if both Jupyter and R are enabled +- R packages are compiled during installation (can be slow) +- Jupyter service starts on boot automatically + diff --git a/roles/datascience/defaults/main.yml b/roles/datascience/defaults/main.yml new file mode 100644 index 0000000..6f49a3d --- /dev/null +++ b/roles/datascience/defaults/main.yml @@ -0,0 +1,20 @@ +--- +# Data Science role defaults + +# Conda/Anaconda configuration +install_conda: false +conda_install_path: "{{ ansible_env.HOME }}/anaconda3" + +# Jupyter Notebook configuration +install_jupyter: false +jupyter_port: 8888 +jupyter_allow_remote: true +jupyter_bind_all_interfaces: true + +# R language configuration +install_r: false +r_packages: + - r-base + - r-base-dev + - r-recommended + diff --git a/roles/datascience/handlers/main.yml b/roles/datascience/handlers/main.yml new file mode 100644 index 0000000..1c317d3 --- /dev/null +++ b/roles/datascience/handlers/main.yml @@ -0,0 +1,8 @@ +--- +- name: Restart jupyter-notebook + ansible.builtin.systemd: + name: jupyter-notebook + state: restarted + daemon_reload: true + become: true + diff --git a/roles/datascience/meta/main.yml b/roles/datascience/meta/main.yml new file mode 100644 index 0000000..337e929 --- /dev/null +++ b/roles/datascience/meta/main.yml @@ -0,0 +1,4 @@ +--- +dependencies: + - role: base + diff --git a/roles/datascience/tasks/main.yml b/roles/datascience/tasks/main.yml new file mode 100644 index 0000000..faa73cc --- /dev/null +++ b/roles/datascience/tasks/main.yml @@ -0,0 +1,203 @@ +--- +# Conda/Anaconda installation +- name: Conda installation block + when: install_conda | default(false) | bool + tags: ['conda'] + block: + - name: Check if conda is installed + ansible.builtin.stat: + path: "{{ conda_install_path }}/bin/conda" + register: conda_installed + + - name: Download Anaconda installer + ansible.builtin.get_url: + url: https://repo.anaconda.com/archive/Anaconda3-2024.10-1-Linux-x86_64.sh + dest: /tmp/anaconda_installer.sh + mode: '0755' + when: not conda_installed.stat.exists + + - name: Install Anaconda + ansible.builtin.shell: | + bash /tmp/anaconda_installer.sh -b -p {{ conda_install_path }} + args: + creates: "{{ conda_install_path }}/bin/conda" + when: not conda_installed.stat.exists + + - name: Initialize conda for bash + ansible.builtin.shell: | + {{ conda_install_path }}/bin/conda init bash + args: + creates: "{{ ansible_env.HOME }}/.bashrc" + when: not conda_installed.stat.exists + failed_when: false + + # Note: conda init zsh is skipped because conda initialization + # is already included in the custom .zshrc deployed by the shell role + # This prevents conda from overwriting our custom .zshrc configuration + + - name: Clean up Anaconda installer + ansible.builtin.file: + path: /tmp/anaconda_installer.sh + state: absent + + - name: Verify conda installation + ansible.builtin.command: "{{ conda_install_path }}/bin/conda --version" + register: conda_version + changed_when: false + + - name: Display conda version + ansible.builtin.debug: + msg: "Conda version installed: {{ conda_version.stdout if conda_version.stdout is defined else 'Not checked in dry-run mode' }}" + +# Jupyter Notebook installation +- name: Jupyter Notebook installation block + tags: ['jupyter'] + when: + - install_conda | default(false) | bool + - install_jupyter | default(false) | bool + block: + - name: Check if Jupyter is installed + ansible.builtin.command: "{{ conda_install_path }}/bin/conda list jupyter" + register: jupyter_installed + changed_when: false + failed_when: false + + - name: Install Jupyter Notebook and common packages via conda + ansible.builtin.shell: | + {{ conda_install_path }}/bin/conda install -y jupyter notebook ipython pandas numpy matplotlib scikit-learn + when: jupyter_installed.rc != 0 or 'jupyter' not in jupyter_installed.stdout + changed_when: true + + - name: Create Jupyter config directory + ansible.builtin.file: + path: "{{ ansible_env.HOME }}/.jupyter" + state: directory + mode: '0755' + + - name: Configure Jupyter Notebook + ansible.builtin.copy: + content: | + # Jupyter Notebook Configuration + c.NotebookApp.ip = '{{ "0.0.0.0" if jupyter_bind_all_interfaces | default(true) | bool else "localhost" }}' + c.NotebookApp.port = {{ jupyter_port | default(8888) }} + c.NotebookApp.open_browser = False + c.NotebookApp.allow_root = True + # Note: For security, set a password with: jupyter notebook password + dest: "{{ ansible_env.HOME }}/.jupyter/jupyter_notebook_config.py" + mode: '0644' + + - name: Create systemd service for Jupyter Notebook + ansible.builtin.copy: + content: | + [Unit] + Description=Jupyter Notebook Server + After=network.target + + [Service] + Type=simple + User={{ ansible_user_id }} + WorkingDirectory={{ ansible_env.HOME }} + ExecStart={{ conda_install_path }}/bin/jupyter notebook + Restart=on-failure + RestartSec=10 + + [Install] + WantedBy=multi-user.target + dest: /etc/systemd/system/jupyter-notebook.service + mode: '0644' + become: true + + - name: Enable and start Jupyter Notebook service + ansible.builtin.systemd: + name: jupyter-notebook + enabled: true + state: started + daemon_reload: true + become: true + + - name: Verify Jupyter installation + ansible.builtin.command: "{{ conda_install_path }}/bin/jupyter --version" + register: jupyter_version + changed_when: false + + - name: Display Jupyter installation info + ansible.builtin.debug: + msg: + - "Jupyter version: {{ jupyter_version.stdout if jupyter_version.stdout is defined else 'Not checked in dry-run mode' }}" + - "Access Jupyter at: http://{{ ansible_host }}:{{ jupyter_port | default(8888) }}" + - "Set a password with: jupyter notebook password" + +# R language installation +- name: R language installation block + when: install_r | default(false) | bool + tags: ['r', 'rstats'] + block: + - name: Install R dependencies + ansible.builtin.apt: + name: + - dirmngr + - gnupg + - apt-transport-https + - ca-certificates + - software-properties-common + state: present + update_cache: false + become: true + + - name: Fetch CRAN GPG key from keyserver + ansible.builtin.shell: | + gpg --keyserver keyserver.ubuntu.com --recv-key '95C0FAF38DB3CCAD0C080A7BDC78B2DDEABC47B7' + gpg --armor --export '95C0FAF38DB3CCAD0C080A7BDC78B2DDEABC47B7' | tee /etc/apt/trusted.gpg.d/cran_debian_key.asc + args: + creates: /etc/apt/trusted.gpg.d/cran_debian_key.asc + become: true + + - name: Add CRAN repository + ansible.builtin.apt_repository: + repo: "deb https://cloud.r-project.org/bin/linux/debian {{ ansible_distribution_release }}-cran40/" + state: present + filename: cran + update_cache: false + become: true + + - name: Update apt cache after adding CRAN + ansible.builtin.apt: + update_cache: true + become: true + retries: 2 + delay: 2 + + - name: Install R packages + ansible.builtin.apt: + name: "{{ r_packages }}" + state: present + become: true + + - name: Install common R packages via R (non-interactive) + ansible.builtin.shell: > + R --quiet --no-save -e "install.packages(c('IRkernel'), repos='https://cloud.r-project.org', Ncpus=4)" + environment: + R_LIBS_USER: "/usr/local/lib/R/site-library" + register: r_packages_install + changed_when: "'DONE' in r_packages_install.stdout or r_packages_install.rc == 0" + failed_when: false + async: 3600 + poll: 30 + + - name: Install IRkernel for Jupyter + ansible.builtin.command: > + R -e "IRkernel::installspec(user = TRUE)" + when: install_jupyter | default(false) | bool + register: irkernel_install + changed_when: "'✔' in irkernel_install.stdout or 'successfully' in irkernel_install.stdout.lower()" + failed_when: false + + - name: Verify R installation + ansible.builtin.command: R --version + register: r_version + changed_when: false + + - name: Display R version + ansible.builtin.debug: + msg: "R version installed: {{ r_version.stdout_lines[0] if r_version.stdout_lines | length > 0 else 'Not checked in dry-run mode' }}" + diff --git a/roles/development/README.md b/roles/development/README.md index 225dd44..447beca 100644 --- a/roles/development/README.md +++ b/roles/development/README.md @@ -1,38 +1,249 @@ -Role Name -========= +# Role: development -A brief description of the role goes here. +## Description +Installs core development tools and utilities for software development. This role provides a lightweight foundation for coding without heavy data science dependencies. -Requirements ------------- +**For data science tools (Anaconda, Jupyter, R), see the `datascience` role.** -Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. +## Requirements +- Ansible 2.9+ +- Debian/Ubuntu systems +- Root or sudo access -Role Variables --------------- +## Installed Components -A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. +### Development Tools +- **git**: Version control system +- **build-essential**: C/C++ compilation tools (gcc, g++, make) +- **python3**: Python 3 interpreter +- **python3-pip**: Python package manager -Dependencies ------------- +### Node.js +- **Node.js 22.x**: Latest LTS from NodeSource +- **npm**: Node package manager (included with Node.js) +- Configured from official NodeSource repository -A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. +### Code Editors +- **Cursor IDE**: AI-powered code editor (AppImage) + - Installed to `/usr/local/bin/cursor` + - Latest stable version from cursor.com -Example Playbook ----------------- +## Variables -Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: +### Core Settings +| Variable | Default | Description | +|----------|---------|-------------| +| `install_cursor` | `true` | Install Cursor IDE | +| `install_cursor_extensions` | `false` | Install Cursor extensions | - - hosts: servers - roles: - - { role: username.rolename, x: 42 } +### Extension Groups +Enable specific extension groups based on your development needs: -License -------- +| Variable | Default | Extensions Included | +|----------|---------|-------------------| +| `install_python` | `false` | Python, Pylance, Black, isort, Flake8, Ruff | +| `install_jupyter` | `false` | Jupyter notebooks, keybindings, renderers | +| `install_web` | `false` | Prettier, ESLint, Tailwind, Vue, Svelte | +| `install_playwright` | `false` | Playwright testing framework | +| `install_devops` | `false` | Go, Rust, YAML, Docker, Ansible | +| `install_r` | `false` | R language support and pack development | +| `install_docs` | `false` | Markdown tools and linter | -BSD +### Base Extensions (Always Installed) +When `install_cursor_extensions: true`, these are always installed: +- ErrorLens (better error highlighting) +- GitLens (Git supercharged) +- Git Graph (visualization) +- Code Spell Checker +- EditorConfig support +- Material Icon Theme +- GitHub Copilot (if licensed) +- Copilot Chat -Author Information ------------------- +## Dependencies +- `base` role (for core utilities) -An optional section for the role authors to include contact information, or a website (HTML is not allowed). +## Example Playbook + +### Basic Installation +```yaml +- hosts: developers + roles: + - role: development +``` + +### Python Data Science Machine +```yaml +- hosts: datascience + roles: + - role: development + vars: + install_cursor_extensions: true + install_python: true + install_jupyter: true + install_docs: true +``` + +### Web Development Machine +```yaml +- hosts: webdevs + roles: + - role: development + vars: + install_cursor_extensions: true + install_web: true + install_playwright: true + install_docs: true +``` + +### Full Stack with DevOps +```yaml +- hosts: fullstack + roles: + - role: development + vars: + install_cursor_extensions: true + install_python: true + install_web: true + install_devops: true + install_docs: true +``` + +### Custom Extension List +You can also override the extension list completely in `host_vars`: +```yaml +# host_vars/myhost.yml +install_cursor_extensions: true +cursor_extensions: + - ms-python.python + - golang.go + - hashicorp.terraform + # ... your custom list +``` + +### With Cursor disabled +```yaml +- hosts: servers + roles: + - role: development + install_cursor: false +``` + +## Usage + +```bash +# Install on specific host +make dev HOST=dev01 + +# Install only development tools (skip other roles) +ansible-playbook playbooks/development.yml --limit dev01 --tags development +``` + +## Tags +- `development`, `dev`: All development tasks +- `cursor`, `ide`: Cursor IDE installation only + +## Post-Installation + +### Verify Installations +```bash +git --version +node --version +npm --version +python3 --version +cursor --version +``` + +### Node.js Usage +```bash +# Install packages globally +npm install -g + +# Check Node.js version +node --version # Should show v22.x +``` + +### Cursor IDE Usage +```bash +# Launch Cursor (if using X11/Wayland) +cursor + +# For root users, use the aliased version from .zshrc: +cursor # Automatically adds --no-sandbox flags +``` + +## Performance Notes + +### Installation Time +- **Base packages**: 1-2 minutes +- **Node.js**: 1-2 minutes +- **Cursor IDE**: 2-5 minutes (~200MB download) +- **Total**: ~5-10 minutes + +### Disk Space +- **Node.js + npm**: ~100MB +- **Cursor IDE**: ~200MB +- **Build tools**: ~50MB +- **Total**: ~350MB + +## Integration + +### With Data Science Role +```yaml +- hosts: datascience_workstation + roles: + - role: development # Core dev tools + - role: datascience # Anaconda, Jupyter, R +``` + +### With Docker +```yaml +- hosts: fullstack_dev + roles: + - role: development + - role: docker +``` + +## Troubleshooting + +### Node.js Version Issues +If Node.js doesn't upgrade to v22: +```bash +# Check current version +node --version + +# Force reinstall +apt-get remove nodejs +# Re-run playbook +``` + +### Cursor Won't Launch +For root users, use the alias that adds required flags: +```bash +# Check alias in .zshrc +alias cursor="cursor --no-sandbox --disable-gpu-sandbox..." +``` + +## Notes +- Node.js 22 is the current LTS version +- NodeSource repository is configured for automatic updates +- Cursor IDE is installed as AppImage for easy updates +- Build tools (gcc, make) are essential for npm native modules +- Python 3 is included for development scripts +- All installations are idempotent (safe to re-run) + +## Comparison with Data Science Role + +| Component | Development Role | Data Science Role | +|-----------|------------------|-------------------| +| Git | ✅ | - | +| Node.js | ✅ | - | +| Build Tools | ✅ | - | +| Cursor IDE | ✅ | - | +| Anaconda | ❌ | ✅ | +| Jupyter | ❌ | ✅ | +| R Language | ❌ | ✅ | +| Install Time | ~10 min | ~30-60 min | +| Disk Space | ~350MB | ~3GB | + +**Recommendation**: Use `development` role for general coding. Add `datascience` role only when needed for data analysis/ML work. diff --git a/roles/development/defaults/main.yml b/roles/development/defaults/main.yml index 5a86ca1..9aa4db1 100644 --- a/roles/development/defaults/main.yml +++ b/roles/development/defaults/main.yml @@ -1,2 +1,87 @@ --- -# defaults file for development +# Development role defaults + +# Node.js is installed by default from NodeSource +# No additional configuration needed + +# Cursor IDE - lightweight IDE installation +install_cursor: true +install_cursor_extensions: false + +# Base Cursor extensions (always good to have) +cursor_extensions_base: + - usernamehw.errorlens # Better error highlighting + - eamodio.gitlens # Git supercharged + - mhutchie.git-graph # Git graph visualization + - streetsidesoftware.code-spell-checker # Spell checker + - EditorConfig.EditorConfig # EditorConfig support + - PKief.material-icon-theme # Better file icons + +# Python/Data Science extensions +cursor_extensions_python: + - ms-python.python # Python language support + - ms-python.vscode-pylance # Python IntelliSense + - ms-python.black-formatter # Black formatter + - ms-python.isort # Import sorter + - ms-python.flake8 # Linter + - charliermarsh.ruff # Fast Python linter + +# Jupyter/Data Science extensions +cursor_extensions_jupyter: + - ms-toolsai.jupyter # Jupyter notebooks + - ms-toolsai.jupyter-keymap # Jupyter keybindings + - ms-toolsai.jupyter-renderers # Jupyter renderers + +# Web Development extensions +cursor_extensions_web: + - esbenp.prettier-vscode # Code formatter + - dbaeumer.vscode-eslint # ESLint + - bradlc.vscode-tailwindcss # Tailwind CSS + - vue.volar # Vue 3 + - svelte.svelte-vscode # Svelte + +# Testing extensions +cursor_extensions_testing: + - ms-playwright.playwright # Playwright testing + +# Systems/DevOps extensions +cursor_extensions_devops: + - golang.go # Go language + - rust-lang.rust-analyzer # Rust language + - redhat.vscode-yaml # YAML support + - ms-azuretools.vscode-docker # Docker support + - redhat.ansible # Ansible support + +# R language extensions +cursor_extensions_r: + - REditorSupport.r # R language support + - Ikuyadeu.r-pack # R package development + +# Markdown/Documentation extensions +cursor_extensions_docs: + - yzhang.markdown-all-in-one # Markdown tools + - DavidAnson.vscode-markdownlint # Markdown linter + +# Default combined list (customize per host in host_vars) +cursor_extensions: >- + {{ + [ + cursor_extensions_base, + (cursor_extensions_python if install_python | default(false) else []), + (cursor_extensions_jupyter if install_jupyter | default(false) else []), + (cursor_extensions_web if install_web | default(false) else []), + (cursor_extensions_testing if install_playwright | default(false) else []), + (cursor_extensions_devops if install_devops | default(false) else []), + (cursor_extensions_r if install_r | default(false) else []), + (cursor_extensions_docs if install_docs | default(false) else []) + ] | flatten + }} + +# Feature flags to enable extension groups +install_python: false +install_jupyter: false +install_web: false +install_playwright: false +install_devops: false +install_r: false +install_docs: false diff --git a/roles/development/tasks/main.yml b/roles/development/tasks/main.yml index 921f847..64102aa 100644 --- a/roles/development/tasks/main.yml +++ b/roles/development/tasks/main.yml @@ -30,7 +30,7 @@ fi register: nodesource_repo_check failed_when: false - when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2') + when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - name: Check if NodeSource GPG key exists and is correct ansible.builtin.shell: | @@ -45,7 +45,7 @@ fi register: nodesource_key_check failed_when: false - when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2') + when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - name: Remove incorrect NodeSource repository ansible.builtin.file: @@ -53,7 +53,7 @@ state: absent become: true when: - - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2') + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - nodesource_repo_check.stdout == "wrong_config" - name: Remove incorrect NodeSource key @@ -62,7 +62,7 @@ state: absent become: true when: - - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2') + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - nodesource_key_check.stdout == "wrong_key" - name: Create keyrings directory @@ -72,7 +72,7 @@ mode: '0755' become: true when: - - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2') + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - nodesource_key_check.stdout in ["not_exists", "wrong_key"] - name: Add NodeSource GPG key only if needed @@ -83,7 +83,7 @@ force: true become: true when: - - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2') + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - nodesource_key_check.stdout in ["not_exists", "wrong_key"] - name: Add NodeSource repository only if needed @@ -93,7 +93,7 @@ update_cache: false become: true when: - - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2') + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - nodesource_repo_check.stdout in ["not_exists", "wrong_config"] - name: Install Node.js 22 from NodeSource @@ -101,7 +101,7 @@ name: nodejs state: present become: true - when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2') + when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - name: Verify Node.js installation ansible.builtin.command: node --version @@ -110,4 +110,93 @@ - name: Display Node.js version ansible.builtin.debug: - msg: "Node.js version installed: {{ final_node_version.stdout }}" + msg: "Node.js version installed: {{ final_node_version.stdout if final_node_version.stdout is defined else 'Not checked in dry-run mode' }}" + +# Cursor IDE installation (using AppImage) +# Downloads the latest version from cursor.com API +- name: Install Cursor IDE block + tags: ['cursor', 'ide'] + block: + - name: Install libfuse2 dependency for AppImage + ansible.builtin.apt: + name: libfuse2 + state: present + update_cache: false + become: true + when: ansible_os_family == "Debian" + + - name: Check if Cursor is already installed at /usr/local/bin + ansible.builtin.stat: + path: /usr/local/bin/cursor + register: cursor_bin_check + + - name: Get Cursor download URL from API and download AppImage + ansible.builtin.shell: | + DOWNLOAD_URL=$(curl -sL "https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable" | grep -o '"downloadUrl":"[^"]*' | cut -d'"' -f4) + wget --timeout=60 --tries=3 -O /tmp/cursor.AppImage "$DOWNLOAD_URL" + args: + creates: /tmp/cursor.AppImage + when: not cursor_bin_check.stat.exists + register: cursor_download + retries: 2 + delay: 5 + until: cursor_download.rc == 0 + + - name: Make Cursor AppImage executable + ansible.builtin.file: + path: /tmp/cursor.AppImage + mode: '0755' + when: + - not cursor_bin_check.stat.exists + - cursor_download is defined + - cursor_download.rc is defined + - cursor_download.rc == 0 + + - name: Install Cursor to /usr/local/bin + ansible.builtin.copy: + src: /tmp/cursor.AppImage + dest: /usr/local/bin/cursor + mode: '0755' + remote_src: true + when: + - not cursor_bin_check.stat.exists + - cursor_download is defined + - cursor_download.rc is defined + - cursor_download.rc == 0 + become: true + + - name: Clean up Cursor download + ansible.builtin.file: + path: /tmp/cursor.AppImage + state: absent + when: + - cursor_download is defined + - cursor_download.rc is defined + - cursor_download.rc == 0 + + - name: Display Cursor installation status + ansible.builtin.debug: + msg: "{{ 'Cursor already installed' if cursor_bin_check.stat.exists else ('Cursor installed successfully' if (cursor_download is defined and cursor_download.rc is defined and cursor_download.rc == 0) else 'Cursor installation failed - download manually from cursor.com') }}" + +# Cursor extensions installation +- name: Install Cursor extensions block + when: + - install_cursor | default(true) | bool + - install_cursor_extensions | default(false) | bool + - cursor_extensions is defined + - cursor_extensions | length > 0 + tags: ['cursor', 'extensions'] + block: + - name: Install Cursor extensions + ansible.builtin.shell: | + cursor --install-extension {{ item }} --force --user-data-dir={{ ansible_env.HOME }}/.cursor-root 2>/dev/null || true + loop: "{{ cursor_extensions }}" + register: cursor_ext_install + changed_when: "'successfully installed' in cursor_ext_install.stdout.lower()" + failed_when: false + become: true + become_user: "{{ ansible_user }}" + + - name: Display Cursor extensions status + ansible.builtin.debug: + msg: "Installed {{ cursor_extensions | length }} Cursor extensions" diff --git a/roles/monitoring/README.md b/roles/monitoring/README.md index e7bf3d0..a9071c7 100644 --- a/roles/monitoring/README.md +++ b/roles/monitoring/README.md @@ -9,7 +9,8 @@ Installs comprehensive system monitoring tools and custom monitoring scripts for - Sufficient disk space for logs ## Installed Tools -- **htop/btop**: Interactive process viewers +- **htop**: Interactive process viewer +- **btop**: Modern system monitor (from apt or binary) - **iotop**: I/O monitoring - **nethogs**: Network usage per process - **iftop**: Network bandwidth monitoring @@ -57,5 +58,5 @@ Installs comprehensive system monitoring tools and custom monitoring scripts for ## Notes - Creates monitoring user directories - Configures fail2ban with custom rules -- Installs both CLI and snap-based tools +- btop installed from apt on Debian 12+, or from GitHub binary on older versions - Custom scripts require manual execution \ No newline at end of file diff --git a/roles/monitoring/defaults/main.yml b/roles/monitoring/defaults/main.yml index 34ad06f..acb701f 100644 --- a/roles/monitoring/defaults/main.yml +++ b/roles/monitoring/defaults/main.yml @@ -1,5 +1,5 @@ --- # Monitoring role defaults -monitoring_install_snap_tools: true +monitoring_install_btop: true monitoring_enable_sysstat: true monitoring_create_scripts: true diff --git a/roles/monitoring/tasks/main.yml b/roles/monitoring/tasks/main.yml index 94c09b0..e60fa01 100644 --- a/roles/monitoring/tasks/main.yml +++ b/roles/monitoring/tasks/main.yml @@ -21,12 +21,56 @@ - atop state: present -- name: Install modern monitoring tools via snap - community.general.snap: - name: - - btop - - bandwhich +- name: Check if btop is available in apt + ansible.builtin.command: apt-cache policy btop + register: btop_apt_check + changed_when: false + failed_when: false + +- name: Install btop from apt if available (Debian 12+) + ansible.builtin.apt: + name: btop state: present + update_cache: false + when: + - btop_apt_check.rc == 0 + - "'Candidate:' in btop_apt_check.stdout" + - "'(none)' not in btop_apt_check.stdout" + failed_when: false + +- name: Install btop from binary if apt not available + when: btop_apt_check.rc != 0 or "(none)" in btop_apt_check.stdout + block: + - name: Download btop binary + ansible.builtin.get_url: + url: https://github.com/aristocratos/btop/releases/latest/download/btop-x86_64-linux-musl.tbz + dest: /tmp/btop.tbz + mode: '0644' + failed_when: false + + - name: Extract btop + ansible.builtin.unarchive: + src: /tmp/btop.tbz + dest: /tmp/ + remote_src: true + failed_when: false + + - name: Install btop binary + ansible.builtin.copy: + src: /tmp/btop/bin/btop + dest: /usr/local/bin/btop + mode: '0755' + remote_src: true + failed_when: false + + - name: Clean up btop download + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /tmp/btop.tbz + - /tmp/btop + failed_when: false - name: Configure fail2ban ansible.builtin.template: diff --git a/roles/shell/README.md b/roles/shell/README.md index 49ad7a5..ac66c93 100644 --- a/roles/shell/README.md +++ b/roles/shell/README.md @@ -1,46 +1,202 @@ -# Role: shell +### Role: shell ## Description -Configures modern shell environment with Zsh, Oh My Zsh, and Powerlevel10k theme for enhanced terminal experience. +Configures modern shell environment with zsh, Oh My Zsh, Powerlevel10k theme, and useful plugins. Can be configured for multiple users on the same host. ## Requirements - Ansible 2.9+ -- Debian/Ubuntu target systems -- Internet access for downloading themes and plugins +- Debian/Ubuntu systems +- Users must exist before running this role -## Features -- Installs and configures Zsh as default shell -- Sets up Oh My Zsh framework -- Installs Powerlevel10k theme -- Adds useful plugins: syntax highlighting, autosuggestions -- Configures tmux and fzf for enhanced productivity +## Installed Components + +### Shell Environment +- **zsh**: Z shell +- **Oh My Zsh**: Zsh configuration framework +- **Powerlevel10k**: Modern, feature-rich theme +- **tmux**: Terminal multiplexer +- **fzf**: Fuzzy finder + +### Zsh Plugins +- **zsh-syntax-highlighting**: Syntax highlighting for commands +- **zsh-autosuggestions**: Fish-like autosuggestions + +### Configuration Files +- `.zshrc`: Custom zsh configuration with conda support +- `.p10k.zsh`: Powerlevel10k theme configuration ## Variables | Variable | Default | Description | |----------|---------|-------------| -| `shell_user` | `{{ ansible_user }}` | User to configure shell for | -| `shell_install_ohmyzsh` | `true` | Whether to install Oh My Zsh | -| `shell_install_p10k` | `true` | Whether to install Powerlevel10k theme | +| `shell_users` | `[ansible_user]` | List of users to configure zsh for | +| `zsh_plugins` | See defaults/main.yml | List of zsh plugins to install | ## Dependencies -- `base` role (for basic packages) +None ## Example Playbook +### Configure Only Ansible User (Default) ```yaml -- hosts: dev +- hosts: servers roles: - - { role: shell, shell_user: "myuser" } + - role: shell ``` -## Tags -- `shell`: All shell configuration tasks -- `zsh`: Zsh installation and configuration -- `ohmyzsh`: Oh My Zsh framework setup -- `theme`: Theme and plugin configuration +### Configure Multiple Users +```yaml +- hosts: servers + roles: + - role: shell + shell_users: + - root + - ladmin + - beast + - user +``` + +### Host-Specific Configuration +In `host_vars/server01.yml`: +```yaml +shell_users: + - root + - myuser + - developer +``` + +## Usage + +### Default (Ansible User Only) +```bash +make shell HOST=dev01 +``` + +### Configure Additional Users +Add to host_vars file: +```yaml +# host_vars/devGPU.yml +shell_users: + - root + - beast + - ladmin +``` + +Then run: +```bash +make dev HOST=devGPU --tags shell +``` + +## Post-Installation + +### For Each Configured User +The shell configuration is immediately active. Users can: + +1. **Customize Powerlevel10k**: + ```bash + p10k configure + ``` + +2. **Reload Configuration**: + ```bash + source ~/.zshrc + ``` + +3. **View Available Aliases**: + ```bash + alias # List all aliases + ``` + +## Features + +### Custom Aliases +The `.zshrc` includes aliases for: +- Git operations (`gs`, `ga`, `gc`, etc.) +- Docker (`dps`, `dex`, `dlogs`) +- System (`ll`, `la`, `update`, `sysinfo`) +- Networking (`ports`, `myip`) +- Development (`serve`, `mkcd`) +- Data Science (`jup`, `conda-list`, `r-studio`) + +### Conda Integration +If Anaconda is installed (via datascience role), conda is automatically initialized in zsh. + +### Root User Support +Includes special aliases for root users (IDEs with `--no-sandbox` flags). ## Notes -- Changes default shell to zsh for the target user -- Downloads themes and plugins from GitHub -- Requires logout/login to see changes \ No newline at end of file + +- Zsh is set as the default shell for all configured users +- Oh My Zsh is installed in each user's home directory +- The role is idempotent - safe to run multiple times +- Existing `.zshrc` files are overwritten +- Users must log out and back in for shell changes to take effect +- The role skips users that don't exist on the system + +## Troubleshooting + +### User Not Found +If a user in `shell_users` doesn't exist: +``` +User username not found, skipping shell configuration +``` +Solution: Ensure the user exists or remove from `shell_users` list. + +### Oh My Zsh Installation Fails +Check user has a valid home directory and write permissions. + +### Powerlevel10k Not Loading +Verify the theme is cloned: +```bash +ls ~/.oh-my-zsh/custom/themes/powerlevel10k +``` + +### Conda Not Initialized +The datascience role must be run to install Anaconda. The shell role only adds the initialization code to `.zshrc`. + +## Integration + +### With User Role +The user role should run before the shell role to ensure users exist: +```yaml +roles: + - user + - shell +``` + +### With Data Science Role +Conda initialization is included in `.zshrc`. Run datascience role after shell: +```yaml +roles: + - shell + - datascience +``` + +## Security Considerations + +- The `.zshrc` is deployed from a template - review before deploying to production +- Root aliases include `--no-sandbox` flags for IDEs (required for root but less secure) +- Consider limiting which users get shell configuration on production servers + +## Performance + +- Installation time: ~2-3 minutes per user +- Disk space: ~10MB per user (Oh My Zsh + plugins + theme) +- First shell launch: ~1-2 seconds (Powerlevel10k initialization) +- Subsequent launches: <0.5 seconds + +## Customization + +### Adding Custom Aliases +Edit `roles/shell/files/.zshrc` and add your aliases. + +### Adding More Plugins +Update `roles/shell/defaults/main.yml`: +```yaml +zsh_plugins: + - name: "my-plugin" + repo: "https://github.com/user/my-plugin.git" +``` + +### Custom Theme +Replace Powerlevel10k in tasks if desired, or users can run `p10k configure` to customize. diff --git a/roles/shell/defaults/main.yml b/roles/shell/defaults/main.yml index 57ae107..6391145 100644 --- a/roles/shell/defaults/main.yml +++ b/roles/shell/defaults/main.yml @@ -1,2 +1,23 @@ --- -# defaults file for shell +# Shell role defaults + +# List of users to configure zsh for +# By default, only configure the ansible user +# Override in host_vars to add more users +shell_users: + - "{{ ansible_user | default(ansible_user_id) }}" + +# Additional users to configure (appended to shell_users) +# Use this to add users WITHOUT having to repeat ansible_user +# Example in host_vars: +# shell_additional_users: +# - beast +# - ladmin +shell_additional_users: [] + +# Zsh plugins to install +zsh_plugins: + - name: "zsh-syntax-highlighting" + repo: "https://github.com/zsh-users/zsh-syntax-highlighting.git" + - name: "zsh-autosuggestions" + repo: "https://github.com/zsh-users/zsh-autosuggestions.git" diff --git a/roles/shell/files/.p10k.zsh b/roles/shell/files/.p10k.zsh new file mode 100644 index 0000000..90e212e --- /dev/null +++ b/roles/shell/files/.p10k.zsh @@ -0,0 +1,1719 @@ +# Generated by Powerlevel10k configuration wizard on 2025-08-28 at 15:03 EDT. +# Based on romkatv/powerlevel10k/config/p10k-lean.zsh, checksum 37983. +# Wizard options: powerline, unicode, lean, 12h time, 2 lines, disconnected, no frame, +# sparse, concise, transient_prompt, instant_prompt=verbose. +# Type `p10k configure` to generate another config. +# +# Config for Powerlevel10k with lean prompt style. Type `p10k configure` to generate +# your own config based on it. +# +# Tip: Looking for a nice color? Here's a one-liner to print colormap. +# +# for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'}; done + +# Temporarily change options. +'builtin' 'local' '-a' 'p10k_config_opts' +[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases') +[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob') +[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand') +'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand' + +() { + emulate -L zsh -o extended_glob + + # Unset all configuration options. This allows you to apply configuration changes without + # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`. + unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR' + + # Zsh >= 5.1 is required. + [[ $ZSH_VERSION == (5.<1->*|<6->.*) ]] || return + + # The list of segments shown on the left. Fill it with the most important segments. + typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=( + # =========================[ Line #1 ]========================= + # os_icon # os identifier + dir # current directory + vcs # git status + # =========================[ Line #2 ]========================= + newline # \n + prompt_char # prompt symbol + ) + + # The list of segments shown on the right. Fill it with less important segments. + # Right prompt on the last prompt line (where you are typing your commands) gets + # automatically hidden when the input line reaches it. Right prompt above the + # last prompt line gets hidden if it would overlap with left prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=( + # =========================[ Line #1 ]========================= + status # exit code of the last command + command_execution_time # duration of the last command + background_jobs # presence of background jobs + direnv # direnv status (https://direnv.net/) + asdf # asdf version manager (https://github.com/asdf-vm/asdf) + virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html) + anaconda # conda environment (https://conda.io/) + pyenv # python environment (https://github.com/pyenv/pyenv) + goenv # go environment (https://github.com/syndbg/goenv) + nodenv # node.js version from nodenv (https://github.com/nodenv/nodenv) + nvm # node.js version from nvm (https://github.com/nvm-sh/nvm) + nodeenv # node.js environment (https://github.com/ekalinin/nodeenv) + # node_version # node.js version + # go_version # go version (https://golang.org) + # rust_version # rustc version (https://www.rust-lang.org) + # dotnet_version # .NET version (https://dotnet.microsoft.com) + # php_version # php version (https://www.php.net/) + # laravel_version # laravel php framework version (https://laravel.com/) + # java_version # java version (https://www.java.com/) + # package # name@version from package.json (https://docs.npmjs.com/files/package.json) + rbenv # ruby version from rbenv (https://github.com/rbenv/rbenv) + rvm # ruby version from rvm (https://rvm.io) + fvm # flutter version management (https://github.com/leoafarias/fvm) + luaenv # lua version from luaenv (https://github.com/cehoffman/luaenv) + jenv # java version from jenv (https://github.com/jenv/jenv) + plenv # perl version from plenv (https://github.com/tokuhirom/plenv) + perlbrew # perl version from perlbrew (https://github.com/gugod/App-perlbrew) + phpenv # php version from phpenv (https://github.com/phpenv/phpenv) + scalaenv # scala version from scalaenv (https://github.com/scalaenv/scalaenv) + haskell_stack # haskell version from stack (https://haskellstack.org/) + kubecontext # current kubernetes context (https://kubernetes.io/) + terraform # terraform workspace (https://www.terraform.io) + # terraform_version # terraform version (https://www.terraform.io) + aws # aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) + aws_eb_env # aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) + azure # azure account name (https://docs.microsoft.com/en-us/cli/azure) + gcloud # google cloud cli account and project (https://cloud.google.com/) + google_app_cred # google application credentials (https://cloud.google.com/docs/authentication/production) + toolbox # toolbox name (https://github.com/containers/toolbox) + context # user@hostname + nordvpn # nordvpn connection status, linux only (https://nordvpn.com/) + ranger # ranger shell (https://github.com/ranger/ranger) + yazi # yazi shell (https://github.com/sxyazi/yazi) + nnn # nnn shell (https://github.com/jarun/nnn) + lf # lf shell (https://github.com/gokcehan/lf) + xplr # xplr shell (https://github.com/sayanarijit/xplr) + vim_shell # vim shell indicator (:sh) + midnight_commander # midnight commander shell (https://midnight-commander.org/) + nix_shell # nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) + chezmoi_shell # chezmoi shell (https://www.chezmoi.io/) + # vpn_ip # virtual private network indicator + # load # CPU load + # disk_usage # disk usage + # ram # free RAM + # swap # used swap + todo # todo items (https://github.com/todotxt/todo.txt-cli) + timewarrior # timewarrior tracking status (https://timewarrior.net/) + taskwarrior # taskwarrior task count (https://taskwarrior.org/) + per_directory_history # Oh My Zsh per-directory-history local/global indicator + # cpu_arch # CPU architecture + time # current time + # =========================[ Line #2 ]========================= + newline + # ip # ip address and bandwidth usage for a specified network interface + # public_ip # public IP address + # proxy # system-wide http/https/ftp proxy + # battery # internal battery + # wifi # wifi speed + # example # example user-defined segment (see prompt_example function below) + ) + + # Defines character set used by powerlevel10k. It's best to let `p10k configure` set it for you. + typeset -g POWERLEVEL9K_MODE=powerline + # When set to `moderate`, some icons will have an extra space after them. This is meant to avoid + # icon overlap when using non-monospace fonts. When set to `none`, spaces are not added. + typeset -g POWERLEVEL9K_ICON_PADDING=none + + # Basic style options that define the overall look of your prompt. You probably don't want to + # change them. + typeset -g POWERLEVEL9K_BACKGROUND= # transparent background + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= # no surrounding whitespace + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' # separate segments with a space + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= # no end-of-line symbol + + # When set to true, icons appear before content on both sides of the prompt. When set + # to false, icons go after content. If empty or not set, icons go before content in the left + # prompt and after content in the right prompt. + # + # You can also override it for a specific segment: + # + # POWERLEVEL9K_STATUS_ICON_BEFORE_CONTENT=false + # + # Or for a specific segment in specific state: + # + # POWERLEVEL9K_DIR_NOT_WRITABLE_ICON_BEFORE_CONTENT=false + typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT=true + + # Add an empty line before each prompt. + typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=true + + # Connect left prompt lines with these symbols. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX= + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX= + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX= + # Connect right prompt lines with these symbols. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX= + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX= + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX= + + # The left end of left prompt. + typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= + # The right end of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL= + + # Ruler, a.k.a. the horizontal line before each prompt. If you set it to true, you'll + # probably want to set POWERLEVEL9K_PROMPT_ADD_NEWLINE=false above and + # POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' below. + typeset -g POWERLEVEL9K_SHOW_RULER=false + typeset -g POWERLEVEL9K_RULER_CHAR='─' # reasonable alternative: '·' + typeset -g POWERLEVEL9K_RULER_FOREGROUND=242 + + # Filler between left and right prompt on the first prompt line. You can set it to '·' or '─' + # to make it easier to see the alignment between left and right prompt and to separate prompt + # from command output. It serves the same purpose as ruler (see above) without increasing + # the number of prompt lines. You'll probably want to set POWERLEVEL9K_SHOW_RULER=false + # if using this. You might also like POWERLEVEL9K_PROMPT_ADD_NEWLINE=false for more compact + # prompt. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' + if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then + # The color of the filler. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=242 + # Add a space between the end of left prompt and the filler. + typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=' ' + # Add a space between the filler and the start of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL=' ' + # Start filler from the edge of the screen if there are no left segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}' + # End filler on the edge of the screen if there are no right segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}' + fi + + #################################[ os_icon: os identifier ]################################## + # OS identifier color. + typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND= + # Custom icon. + # typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='⭐' + + ################################[ prompt_char: prompt symbol ]################################ + # Green prompt symbol if the last command succeeded. + typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76 + # Red prompt symbol if the last command failed. + typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196 + # Default prompt symbol. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯' + # Prompt symbol in command vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮' + # Prompt symbol in visual vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V' + # Prompt symbol in overwrite vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶' + typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true + # No line terminator if prompt_char is the last segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='' + # No line introducer if prompt_char is the first segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= + + ##################################[ dir: current directory ]################################## + # Default current directory color. + typeset -g POWERLEVEL9K_DIR_FOREGROUND=31 + # If directory is too long, shorten some of its segments to the shortest possible unique + # prefix. The shortened directory can be tab-completed to the original. + typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique + # Replace removed segment suffixes with this symbol. + typeset -g POWERLEVEL9K_SHORTEN_DELIMITER= + # Color of the shortened directory segments. + typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=103 + # Color of the anchor directory segments. Anchor segments are never shortened. The first + # segment is always an anchor. + typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=39 + # Display anchor directory segments in bold. + typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true + # Don't shorten directories that contain any of these files. They are anchors. + local anchor_files=( + .bzr + .citc + .git + .hg + .node-version + .python-version + .go-version + .ruby-version + .lua-version + .java-version + .perl-version + .php-version + .tool-versions + .mise.toml + .shorten_folder_marker + .svn + .terraform + CVS + Cargo.toml + composer.json + go.mod + package.json + stack.yaml + ) + typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})" + # If set to "first" ("last"), remove everything before the first (last) subdirectory that contains + # files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is + # /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first) + # or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers + # and other directories don't. + # + # Optionally, "first" and "last" can be followed by ":" where is an integer. + # This moves the truncation point to the right (positive offset) or to the left (negative offset) + # relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0" + # respectively. + typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=false + # Don't shorten this many last directory segments. They are anchors. + typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1 + # Shorten directory if it's longer than this even if there is space for it. The value can + # be either absolute (e.g., '80') or a percentage of terminal width (e.g, '50%'). If empty, + # directory will be shortened only when prompt doesn't fit or when other parameters demand it + # (see POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS and POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT below). + # If set to `0`, directory will always be shortened to its minimum length. + typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least this + # many columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least + # COLUMNS * POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT * 0.01 columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50 + # If set to true, embed a hyperlink into the directory. Useful for quickly + # opening a directory in the file manager simply by clicking the link. + # Can also be handy when the directory is shortened, as it allows you to see + # the full directory that was used in previous commands. + typeset -g POWERLEVEL9K_DIR_HYPERLINK=false + + # Enable special styling for non-writable and non-existent directories. See POWERLEVEL9K_LOCK_ICON + # and POWERLEVEL9K_DIR_CLASSES below. + typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3 + + # The default icon shown next to non-writable and non-existent directories when + # POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3. + typeset -g POWERLEVEL9K_LOCK_ICON='∅' + + # POWERLEVEL9K_DIR_CLASSES allows you to specify custom icons and colors for different + # directories. It must be an array with 3 * N elements. Each triplet consists of: + # + # 1. A pattern against which the current directory ($PWD) is matched. Matching is done with + # extended_glob option enabled. + # 2. Directory class for the purpose of styling. + # 3. An empty string. + # + # Triplets are tried in order. The first triplet whose pattern matches $PWD wins. + # + # If POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3, non-writable and non-existent directories + # acquire class suffix _NOT_WRITABLE and NON_EXISTENT respectively. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_DIR_CLASSES=( + # '~/work(|/*)' WORK '' + # '~(|/*)' HOME '' + # '*' DEFAULT '') + # + # Whenever the current directory is ~/work or a subdirectory of ~/work, it gets styled with one + # of the following classes depending on its writability and existence: WORK, WORK_NOT_WRITABLE or + # WORK_NON_EXISTENT. + # + # Simply assigning classes to directories doesn't have any visible effects. It merely gives you an + # option to define custom colors and icons for different directory classes. + # + # # Styling for WORK. + # typeset -g POWERLEVEL9K_DIR_WORK_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NOT_WRITABLE. + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NON_EXISTENT. + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_ANCHOR_FOREGROUND=39 + # + # If a styling parameter isn't explicitly defined for some class, it falls back to the classless + # parameter. For example, if POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND is not set, it falls + # back to POWERLEVEL9K_DIR_FOREGROUND. + # + typeset -g POWERLEVEL9K_DIR_CLASSES=() + + # Custom prefix. + # typeset -g POWERLEVEL9K_DIR_PREFIX='%fin ' + + #####################################[ vcs: git status ]###################################### + # Branch icon. Set this parameter to '\UE0A0 ' for the popular Powerline branch icon. + typeset -g POWERLEVEL9K_VCS_BRANCH_ICON= + + # Untracked files icon. It's really a question mark, your font isn't broken. + # Change the value of this parameter to show a different icon. + typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?' + + # Formatter for Git status. + # + # Example output: master wip ⇣42⇡42 *42 merge ~42 +42 !42 ?42. + # + # You can edit the function to customize how Git status looks. + # + # VCS_STATUS_* parameters are set by gitstatus plugin. See reference: + # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh. + function my_git_formatter() { + emulate -L zsh + + if [[ -n $P9K_CONTENT ]]; then + # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from + # gitstatus plugin). VCS_STATUS_* parameters are not available in this case. + typeset -g my_git_format=$P9K_CONTENT + return + fi + + if (( $1 )); then + # Styling for up-to-date Git status. + local meta='%f' # default foreground + local clean='%76F' # green foreground + local modified='%178F' # yellow foreground + local untracked='%39F' # blue foreground + local conflicted='%196F' # red foreground + else + # Styling for incomplete and stale Git status. + local meta='%244F' # grey foreground + local clean='%244F' # grey foreground + local modified='%244F' # grey foreground + local untracked='%244F' # grey foreground + local conflicted='%244F' # grey foreground + fi + + local res + + if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then + local branch=${(V)VCS_STATUS_LOCAL_BRANCH} + # If local branch name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show local branch name in full without truncation, delete the next line. + (( $#branch > 32 )) && branch[13,-13]="…" # <-- this line + res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}" + fi + + if [[ -n $VCS_STATUS_TAG + # Show tag only if not on a branch. + # Tip: To always show tag, delete the next line. + && -z $VCS_STATUS_LOCAL_BRANCH # <-- this line + ]]; then + local tag=${(V)VCS_STATUS_TAG} + # If tag name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show tag name in full without truncation, delete the next line. + (( $#tag > 32 )) && tag[13,-13]="…" # <-- this line + res+="${meta}#${clean}${tag//\%/%%}" + fi + + # Display the current Git commit if there is no branch and no tag. + # Tip: To always display the current Git commit, delete the next line. + [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] && # <-- this line + res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}" + + # Show tracking branch name if it differs from local branch. + if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then + res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" + fi + + # Display "wip" if the latest commit's summary contains "wip" or "WIP". + if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then + res+=" ${modified}wip" + fi + + if (( VCS_STATUS_COMMITS_AHEAD || VCS_STATUS_COMMITS_BEHIND )); then + # ⇣42 if behind the remote. + (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}" + # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42. + (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" " + (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}" + elif [[ -n $VCS_STATUS_REMOTE_BRANCH ]]; then + # Tip: Uncomment the next line to display '=' if up to date with the remote. + # res+=" ${clean}=" + fi + + # ⇠42 if behind the push remote. + (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}" + (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" " + # ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42. + (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}" + # *42 if have stashes. + (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}" + # 'merge' if the repo is in an unusual state. + [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}" + # ~42 if have merge conflicts. + (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}" + # +42 if have staged changes. + (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}" + # !42 if have unstaged changes. + (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}" + # ?42 if have untracked files. It's really a question mark, your font isn't broken. + # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon. + # Remove the next line if you don't want to see untracked files at all. + (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}" + # "─" if the number of unstaged files is unknown. This can happen due to + # POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY (see below) being set to a non-negative number lower + # than the number of files in the Git index, or due to bash.showDirtyState being set to false + # in the repository config. The number of staged and untracked files may also be unknown + # in this case. + (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─" + + typeset -g my_git_format=$res + } + functions -M my_git_formatter 2>/dev/null + + # Don't count the number of unstaged, untracked and conflicted files in Git repositories with + # more than this many files in the index. Negative value means infinity. + # + # If you are working in Git repositories with tens of millions of files and seeing performance + # sagging, try setting POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY to a number lower than the output + # of `git ls-files | wc -l`. Alternatively, add `bash.showDirtyState = false` to the repository's + # config: `git config bash.showDirtyState false`. + typeset -g POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY=-1 + + # Don't show Git status in prompt for repositories whose workdir matches this pattern. + # For example, if set to '~', the Git repository at $HOME/.git will be ignored. + # Multiple patterns can be combined with '|': '~(|/foo)|/bar/baz/*'. + typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~' + + # Disable the default Git status formatting. + typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true + # Install our own Git status formatter. + typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}' + typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}' + # Enable counters for staged, unstaged, etc. + typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1 + + # Icon color. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR=76 + typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR=244 + # Custom icon. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_VCS_PREFIX='%fon ' + + # Show status of repositories of these types. You can add svn and/or hg if you are + # using them. If you do, your prompt may become slow even when your current directory + # isn't in an svn or hg repository. + typeset -g POWERLEVEL9K_VCS_BACKENDS=(git) + + # These settings are used for repositories other than Git or when gitstatusd fails and + # Powerlevel10k has to fall back to using vcs_info. + typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=178 + + ##########################[ status: exit code of the last command ]########################### + # Enable OK_PIPE, ERROR_PIPE and ERROR_SIGNAL status states to allow us to enable, disable and + # style them independently from the regular OK and ERROR state. + typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true + + # Status on success. No content, just an icon. No need to show it if prompt_char is enabled as + # it will signify success by turning green. + typeset -g POWERLEVEL9K_STATUS_OK=false + typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when some part of a pipe command fails but the overall exit status is zero. It may look + # like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when it's just an error code (e.g., '1'). No need to show it if prompt_char is enabled as + # it will signify error by turning red. + typeset -g POWERLEVEL9K_STATUS_ERROR=false + typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='✘' + + # Status when the last command was terminated by a signal. + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=160 + # Use terse signal names: "INT" instead of "SIGINT(2)". + typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='✘' + + # Status when some part of a pipe command fails and the overall exit status is also non-zero. + # It may look like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='✘' + + ###################[ command_execution_time: duration of the last command ]################### + # Show duration of the last command if takes at least this many seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3 + # Show this many fractional digits. Zero means round to seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0 + # Execution time color. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=101 + # Duration format: 1d 2h 3m 4s. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s' + # Custom icon. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PREFIX='%ftook ' + + #######################[ background_jobs: presence of background jobs ]####################### + # Don't show the number of background jobs. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false + # Background jobs color. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=70 + # Custom icon. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION='≡' + + #######################[ direnv: direnv status (https://direnv.net/) ]######################## + # Direnv color. + typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ asdf: asdf version manager (https://github.com/asdf-vm/asdf) ]############### + # Default asdf color. Only used to display tools for which there is no color override (see below). + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_FOREGROUND. + typeset -g POWERLEVEL9K_ASDF_FOREGROUND=66 + + # There are four parameters that can be used to hide asdf tools. Each parameter describes + # conditions under which a tool gets hidden. Parameters can hide tools but not unhide them. If at + # least one parameter decides to hide a tool, that tool gets hidden. If no parameter decides to + # hide a tool, it gets shown. + # + # Special note on the difference between POWERLEVEL9K_ASDF_SOURCES and + # POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW. Consider the effect of the following commands: + # + # asdf local python 3.8.1 + # asdf global python 3.8.1 + # + # After running both commands the current python version is 3.8.1 and its source is "local" as + # it takes precedence over "global". If POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW is set to false, + # it'll hide python version in this case because 3.8.1 is the same as the global version. + # POWERLEVEL9K_ASDF_SOURCES will hide python version only if the value of this parameter doesn't + # contain "local". + + # Hide tool versions that don't come from one of these sources. + # + # Available sources: + # + # - shell `asdf current` says "set by ASDF_${TOOL}_VERSION environment variable" + # - local `asdf current` says "set by /some/not/home/directory/file" + # - global `asdf current` says "set by /home/username/file" + # + # Note: If this parameter is set to (shell local global), it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SOURCES. + typeset -g POWERLEVEL9K_ASDF_SOURCES=(shell local global) + + # If set to false, hide tool versions that are the same as global. + # + # Note: The name of this parameter doesn't reflect its meaning at all. + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_PROMPT_ALWAYS_SHOW. + typeset -g POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW=false + + # If set to false, hide tool versions that are equal to "system". + # + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_SYSTEM. + typeset -g POWERLEVEL9K_ASDF_SHOW_SYSTEM=true + + # If set to non-empty value, hide tools unless there is a file matching the specified file pattern + # in the current directory, or its parent directory, or its grandparent directory, and so on. + # + # Note: If this parameter is set to empty value, it won't hide tools. + # Note: SHOW_ON_UPGLOB isn't specific to asdf. It works with all prompt segments. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_ON_UPGLOB. + # + # Example: Hide nodejs version when there is no package.json and no *.js files in the current + # directory, in `..`, in `../..` and so on. + # + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.js|package.json' + typeset -g POWERLEVEL9K_ASDF_SHOW_ON_UPGLOB= + + # Ruby version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUBY_FOREGROUND=168 + # typeset -g POWERLEVEL9K_ASDF_RUBY_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUBY_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Python version from asdf. + typeset -g POWERLEVEL9K_ASDF_PYTHON_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_PYTHON_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PYTHON_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Go version from asdf. + typeset -g POWERLEVEL9K_ASDF_GOLANG_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_GOLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_GOLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Node.js version from asdf. + typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_NODEJS_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Rust version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_RUST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUST_SHOW_ON_UPGLOB='*.foo|*.bar' + + # .NET Core version from asdf. + typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND=134 + # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_DOTNET_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Flutter version from asdf. + typeset -g POWERLEVEL9K_ASDF_FLUTTER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Lua version from asdf. + typeset -g POWERLEVEL9K_ASDF_LUA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_LUA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_LUA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Java version from asdf. + typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_JAVA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JAVA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Perl version from asdf. + typeset -g POWERLEVEL9K_ASDF_PERL_FOREGROUND=67 + # typeset -g POWERLEVEL9K_ASDF_PERL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PERL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Erlang version from asdf. + typeset -g POWERLEVEL9K_ASDF_ERLANG_FOREGROUND=125 + # typeset -g POWERLEVEL9K_ASDF_ERLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ERLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Elixir version from asdf. + typeset -g POWERLEVEL9K_ASDF_ELIXIR_FOREGROUND=129 + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Postgres version from asdf. + typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND=31 + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_SHOW_ON_UPGLOB='*.foo|*.bar' + + # PHP version from asdf. + typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND=99 + # typeset -g POWERLEVEL9K_ASDF_PHP_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PHP_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Haskell version from asdf. + typeset -g POWERLEVEL9K_ASDF_HASKELL_FOREGROUND=172 + # typeset -g POWERLEVEL9K_ASDF_HASKELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_HASKELL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Julia version from asdf. + typeset -g POWERLEVEL9K_ASDF_JULIA_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_JULIA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JULIA_SHOW_ON_UPGLOB='*.foo|*.bar' + + ##########[ nordvpn: nordvpn connection status, linux only (https://nordvpn.com/) ]########### + # NordVPN connection indicator color. + typeset -g POWERLEVEL9K_NORDVPN_FOREGROUND=39 + # Hide NordVPN connection indicator when not connected. + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION= + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION= + # Custom icon. + typeset -g POWERLEVEL9K_NORDVPN_VISUAL_IDENTIFIER_EXPANSION='nord' + + #################[ ranger: ranger shell (https://github.com/ranger/ranger) ]################## + # Ranger shell color. + typeset -g POWERLEVEL9K_RANGER_FOREGROUND=178 + # Custom icon. + typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='▲' + + ####################[ yazi: yazi shell (https://github.com/sxyazi/yazi) ]##################### + # Yazi shell color. + typeset -g POWERLEVEL9K_YAZI_FOREGROUND=178 + # Custom icon. + typeset -g POWERLEVEL9K_YAZI_VISUAL_IDENTIFIER_EXPANSION='▲' + + ######################[ nnn: nnn shell (https://github.com/jarun/nnn) ]####################### + # Nnn shell color. + typeset -g POWERLEVEL9K_NNN_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_NNN_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################[ lf: lf shell (https://github.com/gokcehan/lf) ]####################### + # lf shell color. + typeset -g POWERLEVEL9K_LF_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_LF_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################[ xplr: xplr shell (https://github.com/sayanarijit/xplr) ]################## + # xplr shell color. + typeset -g POWERLEVEL9K_XPLR_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_XPLR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########################[ vim_shell: vim shell indicator (:sh) ]########################### + # Vim shell indicator color. + typeset -g POWERLEVEL9K_VIM_SHELL_FOREGROUND=34 + # Custom icon. + # typeset -g POWERLEVEL9K_VIM_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######[ midnight_commander: midnight commander shell (https://midnight-commander.org/) ]###### + # Midnight Commander shell color. + typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ nix_shell: nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) ]## + # Nix shell color. + typeset -g POWERLEVEL9K_NIX_SHELL_FOREGROUND=74 + + # Display the icon of nix_shell if PATH contains a subdirectory of /nix/store. + # typeset -g POWERLEVEL9K_NIX_SHELL_INFER_FROM_PATH=false + + # Tip: If you want to see just the icon without "pure" and "impure", uncomment the next line. + # typeset -g POWERLEVEL9K_NIX_SHELL_CONTENT_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_NIX_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################[ chezmoi_shell: chezmoi shell (https://www.chezmoi.io/) ]################## + # chezmoi shell color. + typeset -g POWERLEVEL9K_CHEZMOI_SHELL_FOREGROUND=33 + # Custom icon. + # typeset -g POWERLEVEL9K_CHEZMOI_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ disk_usage: disk usage ]################################## + # Colors for different levels of disk usage. + typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND=35 + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND=220 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND=160 + # Thresholds for different levels of disk usage (percentage points). + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=90 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=95 + # If set to true, hide disk usage when below $POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL percent. + typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=false + # Custom icon. + # typeset -g POWERLEVEL9K_DISK_USAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ ram: free RAM ]####################################### + # RAM color. + typeset -g POWERLEVEL9K_RAM_FOREGROUND=66 + # Custom icon. + # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################################[ swap: used swap ]###################################### + # Swap color. + typeset -g POWERLEVEL9K_SWAP_FOREGROUND=96 + # Custom icon. + # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ load: CPU load ]###################################### + # Show average CPU load over this many last minutes. Valid values are 1, 5 and 15. + typeset -g POWERLEVEL9K_LOAD_WHICH=5 + # Load color when load is under 50%. + typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND=66 + # Load color when load is between 50% and 70%. + typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND=178 + # Load color when load is over 70%. + typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND=166 + # Custom icon. + # typeset -g POWERLEVEL9K_LOAD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ todo: todo items (https://github.com/todotxt/todo.txt-cli) ]################ + # Todo color. + typeset -g POWERLEVEL9K_TODO_FOREGROUND=110 + # Hide todo when the total number of tasks is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true + # Hide todo when the number of tasks after filtering is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false + + # Todo format. The following parameters are available within the expansion. + # + # - P9K_TODO_TOTAL_TASK_COUNT The total number of tasks. + # - P9K_TODO_FILTERED_TASK_COUNT The number of tasks after filtering. + # + # These variables correspond to the last line of the output of `todo.sh -p ls`: + # + # TODO: 24 of 42 tasks shown + # + # Here 24 is P9K_TODO_FILTERED_TASK_COUNT and 42 is P9K_TODO_TOTAL_TASK_COUNT. + # + # typeset -g POWERLEVEL9K_TODO_CONTENT_EXPANSION='$P9K_TODO_FILTERED_TASK_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TODO_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ timewarrior: timewarrior tracking status (https://timewarrior.net/) ]############ + # Timewarrior color. + typeset -g POWERLEVEL9K_TIMEWARRIOR_FOREGROUND=110 + # If the tracked task is longer than 24 characters, truncate and append "…". + # Tip: To always display tasks without truncation, delete the following parameter. + # Tip: To hide task names and display just the icon when time tracking is enabled, set the + # value of the following parameter to "". + typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}' + + # Custom icon. + # typeset -g POWERLEVEL9K_TIMEWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ taskwarrior: taskwarrior task count (https://taskwarrior.org/) ]############## + # Taskwarrior color. + typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=74 + + # Taskwarrior segment format. The following parameters are available within the expansion. + # + # - P9K_TASKWARRIOR_PENDING_COUNT The number of pending tasks: `task +PENDING count`. + # - P9K_TASKWARRIOR_OVERDUE_COUNT The number of overdue tasks: `task +OVERDUE count`. + # + # Zero values are represented as empty parameters. + # + # The default format: + # + # '${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT' + # + # typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='$P9K_TASKWARRIOR_PENDING_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######[ per_directory_history: Oh My Zsh per-directory-history local/global indicator ]####### + # Color when using local/global history. + typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_FOREGROUND=135 + typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_FOREGROUND=130 + + # Tip: Uncomment the next two lines to hide "local"/"global" text and leave just the icon. + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_CONTENT_EXPANSION='' + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_CONTENT_EXPANSION='' + + # Custom icon. + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################################[ cpu_arch: CPU architecture ]################################ + # CPU architecture color. + typeset -g POWERLEVEL9K_CPU_ARCH_FOREGROUND=172 + + # Hide the segment when on a specific CPU architecture. + # typeset -g POWERLEVEL9K_CPU_ARCH_X86_64_CONTENT_EXPANSION= + # typeset -g POWERLEVEL9K_CPU_ARCH_X86_64_VISUAL_IDENTIFIER_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_CPU_ARCH_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ context: user@hostname ]################################## + # Context color when running with privileges. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=178 + # Context color in SSH without privileges. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=180 + # Default context color (no privileges, no SSH). + typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=180 + + # Context format when running with privileges: bold user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%n@%m' + # Context format when in SSH without privileges: user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m' + # Default context format (no privileges, no SSH): user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n@%m' + + # Don't show context unless running with privileges or in SSH. + # Tip: Remove the next line to always show context. + typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + # typeset -g POWERLEVEL9K_CONTEXT_PREFIX='%fwith ' + + ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]### + # Python virtual environment color. + typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=37 + # Don't show Python version next to the virtual environment name. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false + # If set to "false", won't show virtualenv if pyenv is already shown. + # If set to "if-different", won't show virtualenv if it's the same as pyenv. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_WITH_PYENV=false + # Separate environment name from Python version only with a space. + typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ anaconda: conda environment (https://conda.io/) ]###################### + # Anaconda environment color. + typeset -g POWERLEVEL9K_ANACONDA_FOREGROUND=37 + + # Anaconda segment format. The following parameters are available within the expansion. + # + # - CONDA_PREFIX Absolute path to the active Anaconda/Miniconda environment. + # - CONDA_DEFAULT_ENV Name of the active Anaconda/Miniconda environment. + # - CONDA_PROMPT_MODIFIER Configurable prompt modifier (see below). + # - P9K_ANACONDA_PYTHON_VERSION Current python version (python --version). + # + # CONDA_PROMPT_MODIFIER can be configured with the following command: + # + # conda config --set env_prompt '({default_env}) ' + # + # The last argument is a Python format string that can use the following variables: + # + # - prefix The same as CONDA_PREFIX. + # - default_env The same as CONDA_DEFAULT_ENV. + # - name The last segment of CONDA_PREFIX. + # - stacked_env Comma-separated list of names in the environment stack. The first element is + # always the same as default_env. + # + # Note: '({default_env}) ' is the default value of env_prompt. + # + # The default value of POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION expands to $CONDA_PROMPT_MODIFIER + # without the surrounding parentheses, or to the last path component of CONDA_PREFIX if the former + # is empty. + typeset -g POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION='${${${${CONDA_PROMPT_MODIFIER#\(}% }%\)}:-${CONDA_PREFIX:t}}' + + # Custom icon. + # typeset -g POWERLEVEL9K_ANACONDA_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ pyenv: python environment (https://github.com/pyenv/pyenv) ]################ + # Pyenv color. + typeset -g POWERLEVEL9K_PYENV_FOREGROUND=37 + # Hide python version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global) + # If set to false, hide python version if it's the same as global: + # $(pyenv version-name) == $(pyenv global). + typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide python version if it's equal to "system". + typeset -g POWERLEVEL9K_PYENV_SHOW_SYSTEM=true + + # Pyenv segment format. The following parameters are available within the expansion. + # + # - P9K_CONTENT Current pyenv environment (pyenv version-name). + # - P9K_PYENV_PYTHON_VERSION Current python version (python --version). + # + # The default format has the following logic: + # + # 1. Display just "$P9K_CONTENT" if it's equal to "$P9K_PYENV_PYTHON_VERSION" or + # starts with "$P9K_PYENV_PYTHON_VERSION/". + # 2. Otherwise display "$P9K_CONTENT $P9K_PYENV_PYTHON_VERSION". + typeset -g POWERLEVEL9K_PYENV_CONTENT_EXPANSION='${P9K_CONTENT}${${P9K_CONTENT:#$P9K_PYENV_PYTHON_VERSION(|/*)}:+ $P9K_PYENV_PYTHON_VERSION}' + + # Custom icon. + # typeset -g POWERLEVEL9K_PYENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ goenv: go environment (https://github.com/syndbg/goenv) ]################ + # Goenv color. + typeset -g POWERLEVEL9K_GOENV_FOREGROUND=37 + # Hide go version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global) + # If set to false, hide go version if it's the same as global: + # $(goenv version-name) == $(goenv global). + typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide go version if it's equal to "system". + typeset -g POWERLEVEL9K_GOENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_GOENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ nodenv: node.js version from nodenv (https://github.com/nodenv/nodenv) ]########## + # Nodenv color. + typeset -g POWERLEVEL9K_NODENV_FOREGROUND=70 + # Hide node version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_NODENV_SOURCES=(shell local global) + # If set to false, hide node version if it's the same as global: + # $(nodenv version-name) == $(nodenv global). + typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide node version if it's equal to "system". + typeset -g POWERLEVEL9K_NODENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ nvm: node.js version from nvm (https://github.com/nvm-sh/nvm) ]############### + # Nvm color. + typeset -g POWERLEVEL9K_NVM_FOREGROUND=70 + # If set to false, hide node version if it's the same as default: + # $(nvm version current) == $(nvm version default). + typeset -g POWERLEVEL9K_NVM_PROMPT_ALWAYS_SHOW=false + # If set to false, hide node version if it's equal to "system". + typeset -g POWERLEVEL9K_NVM_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_NVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ nodeenv: node.js environment (https://github.com/ekalinin/nodeenv) ]############ + # Nodeenv color. + typeset -g POWERLEVEL9K_NODEENV_FOREGROUND=70 + # Don't show Node version next to the environment name. + typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false + # Separate environment name from Node version only with a space. + typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_NODEENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############################[ node_version: node.js version ]############################### + # Node version color. + typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND=70 + # Show node version only when in a directory tree containing package.json. + typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODE_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ go_version: go version (https://golang.org) ]######################## + # Go version color. + typeset -g POWERLEVEL9K_GO_VERSION_FOREGROUND=37 + # Show go version only when in a go project subdirectory. + typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_GO_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #################[ rust_version: rustc version (https://www.rust-lang.org) ]################## + # Rust version color. + typeset -g POWERLEVEL9K_RUST_VERSION_FOREGROUND=37 + # Show rust version only when in a rust project subdirectory. + typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_RUST_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ dotnet_version: .NET version (https://dotnet.microsoft.com) ]################ + # .NET version color. + typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND=134 + # Show .NET version only when in a .NET project subdirectory. + typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_DOTNET_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ php_version: php version (https://www.php.net/) ]###################### + # PHP version color. + typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND=99 + # Show PHP version only when in a PHP project subdirectory. + typeset -g POWERLEVEL9K_PHP_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHP_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ laravel_version: laravel php framework version (https://laravel.com/) ]########### + # Laravel version color. + typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND=161 + # Custom icon. + # typeset -g POWERLEVEL9K_LARAVEL_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ####################[ java_version: java version (https://www.java.com/) ]#################### + # Java version color. + typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND=32 + # Show java version only when in a java project subdirectory. + typeset -g POWERLEVEL9K_JAVA_VERSION_PROJECT_ONLY=true + # Show brief version. + typeset -g POWERLEVEL9K_JAVA_VERSION_FULL=false + # Custom icon. + # typeset -g POWERLEVEL9K_JAVA_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###[ package: name@version from package.json (https://docs.npmjs.com/files/package.json) ]#### + # Package color. + typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND=117 + # Package format. The following parameters are available within the expansion. + # + # - P9K_PACKAGE_NAME The value of `name` field in package.json. + # - P9K_PACKAGE_VERSION The value of `version` field in package.json. + # + # typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}@${P9K_PACKAGE_VERSION//\%/%%}' + # Custom icon. + # typeset -g POWERLEVEL9K_PACKAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ rbenv: ruby version from rbenv (https://github.com/rbenv/rbenv) ]############## + # Rbenv color. + typeset -g POWERLEVEL9K_RBENV_FOREGROUND=168 + # Hide ruby version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global) + # If set to false, hide ruby version if it's the same as global: + # $(rbenv version-name) == $(rbenv global). + typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide ruby version if it's equal to "system". + typeset -g POWERLEVEL9K_RBENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_RBENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ rvm: ruby version from rvm (https://rvm.io) ]######################## + # Rvm color. + typeset -g POWERLEVEL9K_RVM_FOREGROUND=168 + # Don't show @gemset at the end. + typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false + # Don't show ruby- at the front. + typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_RVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ fvm: flutter version management (https://github.com/leoafarias/fvm) ]############ + # Fvm color. + typeset -g POWERLEVEL9K_FVM_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_FVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ luaenv: lua version from luaenv (https://github.com/cehoffman/luaenv) ]########### + # Lua color. + typeset -g POWERLEVEL9K_LUAENV_FOREGROUND=32 + # Hide lua version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global) + # If set to false, hide lua version if it's the same as global: + # $(luaenv version-name) == $(luaenv global). + typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide lua version if it's equal to "system". + typeset -g POWERLEVEL9K_LUAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_LUAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ jenv: java version from jenv (https://github.com/jenv/jenv) ]################ + # Java color. + typeset -g POWERLEVEL9K_JENV_FOREGROUND=32 + # Hide java version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global) + # If set to false, hide java version if it's the same as global: + # $(jenv version-name) == $(jenv global). + typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide java version if it's equal to "system". + typeset -g POWERLEVEL9K_JENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_JENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ plenv: perl version from plenv (https://github.com/tokuhirom/plenv) ]############ + # Perl color. + typeset -g POWERLEVEL9K_PLENV_FOREGROUND=67 + # Hide perl version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global) + # If set to false, hide perl version if it's the same as global: + # $(plenv version-name) == $(plenv global). + typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide perl version if it's equal to "system". + typeset -g POWERLEVEL9K_PLENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PLENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ perlbrew: perl version from perlbrew (https://github.com/gugod/App-perlbrew) ]############ + # Perlbrew color. + typeset -g POWERLEVEL9K_PERLBREW_FOREGROUND=67 + # Show perlbrew version only when in a perl project subdirectory. + typeset -g POWERLEVEL9K_PERLBREW_PROJECT_ONLY=true + # Don't show "perl-" at the front. + typeset -g POWERLEVEL9K_PERLBREW_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_PERLBREW_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ phpenv: php version from phpenv (https://github.com/phpenv/phpenv) ]############ + # PHP color. + typeset -g POWERLEVEL9K_PHPENV_FOREGROUND=99 + # Hide php version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PHPENV_SOURCES=(shell local global) + # If set to false, hide php version if it's the same as global: + # $(phpenv version-name) == $(phpenv global). + typeset -g POWERLEVEL9K_PHPENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide php version if it's equal to "system". + typeset -g POWERLEVEL9K_PHPENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHPENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######[ scalaenv: scala version from scalaenv (https://github.com/scalaenv/scalaenv) ]####### + # Scala color. + typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND=160 + # Hide scala version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_SCALAENV_SOURCES=(shell local global) + # If set to false, hide scala version if it's the same as global: + # $(scalaenv version-name) == $(scalaenv global). + typeset -g POWERLEVEL9K_SCALAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide scala version if it's equal to "system". + typeset -g POWERLEVEL9K_SCALAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_SCALAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ haskell_stack: haskell version from stack (https://haskellstack.org/) ]########### + # Haskell color. + typeset -g POWERLEVEL9K_HASKELL_STACK_FOREGROUND=172 + # Hide haskell version if it doesn't come from one of these sources. + # + # shell: version is set by STACK_YAML + # local: version is set by stack.yaml up the directory tree + # global: version is set by the implicit global project (~/.stack/global-project/stack.yaml) + typeset -g POWERLEVEL9K_HASKELL_STACK_SOURCES=(shell local) + # If set to false, hide haskell version if it's the same as in the implicit global project. + typeset -g POWERLEVEL9K_HASKELL_STACK_ALWAYS_SHOW=true + # Custom icon. + # typeset -g POWERLEVEL9K_HASKELL_STACK_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]############# + # Show kubecontext only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show kubecontext. + typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc|istioctl|kogito|k9s|helmfile|flux|fluxctl|stern|kubeseal|skaffold|kubent|kubecolor|cmctl|sparkctl' + + # Kubernetes context classes for the purpose of using different colors, icons and expansions with + # different contexts. + # + # POWERLEVEL9K_KUBECONTEXT_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current kubernetes context gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_KUBECONTEXT_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_KUBECONTEXT_CLASSES defines the context class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current kubernetes context is "deathray-testing/default", its class is TEST + # because "deathray-testing/default" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=134 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='○' + + # Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext + # segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # Within the expansion the following parameters are always available: + # + # - P9K_CONTENT The content that would've been displayed if there was no content + # expansion defined. + # - P9K_KUBECONTEXT_NAME The current context's name. Corresponds to column NAME in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_CLUSTER The current context's cluster. Corresponds to column CLUSTER in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_NAMESPACE The current context's namespace. Corresponds to column NAMESPACE + # in the output of `kubectl config get-contexts`. If there is no + # namespace, the parameter is set to "default". + # - P9K_KUBECONTEXT_USER The current context's user. Corresponds to column AUTHINFO in the + # output of `kubectl config get-contexts`. + # + # If the context points to Google Kubernetes Engine (GKE) or Elastic Kubernetes Service (EKS), + # the following extra parameters are available: + # + # - P9K_KUBECONTEXT_CLOUD_NAME Either "gke" or "eks". + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT Account/project ID. + # - P9K_KUBECONTEXT_CLOUD_ZONE Availability zone. + # - P9K_KUBECONTEXT_CLOUD_CLUSTER Cluster. + # + # P9K_KUBECONTEXT_CLOUD_* parameters are derived from P9K_KUBECONTEXT_CLUSTER. For example, + # if P9K_KUBECONTEXT_CLUSTER is "gke_my-account_us-east1-a_my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=gke + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=my-account + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east1-a + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + # + # If P9K_KUBECONTEXT_CLUSTER is "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=eks + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=123456789012 + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east-1 + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION= + # Show P9K_KUBECONTEXT_CLOUD_CLUSTER if it's not empty and fall back to P9K_KUBECONTEXT_NAME. + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}' + # Append the current context's namespace if it's not "default". + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}' + + # Custom prefix. + # typeset -g POWERLEVEL9K_KUBECONTEXT_PREFIX='%fat ' + + ################[ terraform: terraform workspace (https://www.terraform.io) ]################# + # Don't show terraform workspace if it's literally "default". + typeset -g POWERLEVEL9K_TERRAFORM_SHOW_DEFAULT=false + # POWERLEVEL9K_TERRAFORM_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current terraform workspace gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_TERRAFORM_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_TERRAFORM_CLASSES defines the workspace class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' OTHER) + # + # If your current terraform workspace is "project_test", its class is TEST because "project_test" + # doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' OTHER) + typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_TERRAFORM_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ terraform_version: terraform version (https://www.terraform.io) ]############## + # Terraform version color. + typeset -g POWERLEVEL9K_TERRAFORM_VERSION_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_TERRAFORM_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ aws: aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) ]# + # Show aws only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show aws. + typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|cdk|terraform|pulumi|terragrunt' + + # POWERLEVEL9K_AWS_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current AWS profile gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_AWS_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_AWS_CLASSES defines the profile class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current AWS profile is "company_test", its class is TEST + # because "company_test" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_AWS_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_AWS_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_AWS_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND=208 + # typeset -g POWERLEVEL9K_AWS_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # AWS segment format. The following parameters are available within the expansion. + # + # - P9K_AWS_PROFILE The name of the current AWS profile. + # - P9K_AWS_REGION The region associated with the current AWS profile. + typeset -g POWERLEVEL9K_AWS_CONTENT_EXPANSION='${P9K_AWS_PROFILE//\%/%%}${P9K_AWS_REGION:+ ${P9K_AWS_REGION//\%/%%}}' + + #[ aws_eb_env: aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) ]# + # AWS Elastic Beanstalk environment color. + typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND=70 + # Custom icon. + typeset -g POWERLEVEL9K_AWS_EB_ENV_VISUAL_IDENTIFIER_EXPANSION='eb' + + ##########[ azure: azure account name (https://docs.microsoft.com/en-us/cli/azure) ]########## + # Show azure only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show azure. + typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi|terragrunt' + + # POWERLEVEL9K_AZURE_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current azure account name gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_AZURE_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_AZURE_CLASSES defines the account class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_AZURE_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' OTHER) + # + # If your current azure account is "company_test", its class is TEST because "company_test" + # doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_AZURE_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_AZURE_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_AZURE_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_AZURE_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' OTHER) + + # Azure account name color. + typeset -g POWERLEVEL9K_AZURE_OTHER_FOREGROUND=32 + # Custom icon. + # typeset -g POWERLEVEL9K_AZURE_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ gcloud: google cloud account and project (https://cloud.google.com/) ]########### + # Show gcloud only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show gcloud. + typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs|gsutil' + # Google cloud color. + typeset -g POWERLEVEL9K_GCLOUD_FOREGROUND=32 + + # Google cloud format. Change the value of POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION and/or + # POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION if the default is too verbose or not informative + # enough. You can use the following parameters in the expansions. Each of them corresponds to the + # output of `gcloud` tool. + # + # Parameter | Source + # -------------------------|-------------------------------------------------------------------- + # P9K_GCLOUD_CONFIGURATION | gcloud config configurations list --format='value(name)' + # P9K_GCLOUD_ACCOUNT | gcloud config get-value account + # P9K_GCLOUD_PROJECT_ID | gcloud config get-value project + # P9K_GCLOUD_PROJECT_NAME | gcloud projects describe $P9K_GCLOUD_PROJECT_ID --format='value(name)' + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced with '%%'. + # + # Obtaining project name requires sending a request to Google servers. This can take a long time + # and even fail. When project name is unknown, P9K_GCLOUD_PROJECT_NAME is not set and gcloud + # prompt segment is in state PARTIAL. When project name gets known, P9K_GCLOUD_PROJECT_NAME gets + # set and gcloud prompt segment transitions to state COMPLETE. + # + # You can customize the format, icon and colors of gcloud segment separately for states PARTIAL + # and COMPLETE. You can also hide gcloud in state PARTIAL by setting + # POWERLEVEL9K_GCLOUD_PARTIAL_VISUAL_IDENTIFIER_EXPANSION and + # POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION to empty. + typeset -g POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_ID//\%/%%}' + typeset -g POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_NAME//\%/%%}' + + # Send a request to Google (by means of `gcloud projects describe ...`) to obtain project name + # this often. Negative value disables periodic polling. In this mode project name is retrieved + # only when the current configuration, account or project id changes. + typeset -g POWERLEVEL9K_GCLOUD_REFRESH_PROJECT_NAME_SECONDS=60 + + # Custom icon. + # typeset -g POWERLEVEL9K_GCLOUD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ google_app_cred: google application credentials (https://cloud.google.com/docs/authentication/production) ]# + # Show google_app_cred only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show google_app_cred. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi|terragrunt' + + # Google application credentials classes for the purpose of using different colors, icons and + # expansions with different credentials. + # + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES is an array with even number of elements. The first + # element in each pair defines a pattern against which the current kubernetes context gets + # matched. More specifically, it's P9K_CONTENT prior to the application of context expansion + # (see below) that gets matched. If you unset all POWERLEVEL9K_GOOGLE_APP_CRED_*CONTENT_EXPANSION + # parameters, you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES defines the context class. Patterns are tried in order. + # The first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD + # '*:*test*:*' TEST + # '*' DEFAULT) + # + # If your current Google application credentials is "service_account deathray-testing x@y.com", + # its class is TEST because it doesn't match the pattern '* *prod* *' but does match '* *test* *'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_CONTENT_EXPANSION='$P9K_GOOGLE_APP_CRED_PROJECT_ID' + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD # These values are examples that are unlikely + # '*:*test*:*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_FOREGROUND=32 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by + # google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # You can use the following parameters in the expansion. Each of them corresponds to one of the + # fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS. + # + # Parameter | JSON key file field + # ---------------------------------+--------------- + # P9K_GOOGLE_APP_CRED_TYPE | type + # P9K_GOOGLE_APP_CRED_PROJECT_ID | project_id + # P9K_GOOGLE_APP_CRED_CLIENT_EMAIL | client_email + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced by '%%'. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}' + + ##############[ toolbox: toolbox name (https://github.com/containers/toolbox) ]############### + # Toolbox color. + typeset -g POWERLEVEL9K_TOOLBOX_FOREGROUND=178 + # Don't display the name of the toolbox if it matches fedora-toolbox-*. + typeset -g POWERLEVEL9K_TOOLBOX_CONTENT_EXPANSION='${P9K_TOOLBOX_NAME:#fedora-toolbox-*}' + # Custom icon. + # typeset -g POWERLEVEL9K_TOOLBOX_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + # typeset -g POWERLEVEL9K_TOOLBOX_PREFIX='%fin ' + + ###############################[ public_ip: public IP address ]############################### + # Public IP color. + typeset -g POWERLEVEL9K_PUBLIC_IP_FOREGROUND=94 + # Custom icon. + # typeset -g POWERLEVEL9K_PUBLIC_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ########################[ vpn_ip: virtual private network indicator ]######################### + # VPN IP color. + typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=81 + # When on VPN, show just an icon without the IP address. + # Tip: To display the private IP address when on VPN, remove the next line. + typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION= + # Regular expression for the VPN network interface. Run `ifconfig` or `ip -4 a show` while on VPN + # to see the name of the interface. + typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(gpd|wg|(.*tun)|tailscale)[0-9]*|(zt.*)' + # If set to true, show one segment per matching network interface. If set to false, show only + # one segment corresponding to the first matching network interface. + # Tip: If you set it to true, you'll probably want to unset POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION. + typeset -g POWERLEVEL9K_VPN_IP_SHOW_ALL=false + # Custom icon. + # typeset -g POWERLEVEL9K_VPN_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ ip: ip address and bandwidth usage for a specified network interface ]########### + # IP color. + typeset -g POWERLEVEL9K_IP_FOREGROUND=38 + # The following parameters are accessible within the expansion: + # + # Parameter | Meaning + # ----------------------+------------------------------------------- + # P9K_IP_IP | IP address + # P9K_IP_INTERFACE | network interface + # P9K_IP_RX_BYTES | total number of bytes received + # P9K_IP_TX_BYTES | total number of bytes sent + # P9K_IP_RX_BYTES_DELTA | number of bytes received since last prompt + # P9K_IP_TX_BYTES_DELTA | number of bytes sent since last prompt + # P9K_IP_RX_RATE | receive rate (since last prompt) + # P9K_IP_TX_RATE | send rate (since last prompt) + typeset -g POWERLEVEL9K_IP_CONTENT_EXPANSION='$P9K_IP_IP${P9K_IP_RX_RATE:+ %70F⇣$P9K_IP_RX_RATE}${P9K_IP_TX_RATE:+ %215F⇡$P9K_IP_TX_RATE}' + # Show information for the first network interface whose name matches this regular expression. + # Run `ifconfig` or `ip -4 a show` to see the names of all network interfaces. + typeset -g POWERLEVEL9K_IP_INTERFACE='[ew].*' + # Custom icon. + # typeset -g POWERLEVEL9K_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #########################[ proxy: system-wide http/https/ftp proxy ]########################## + # Proxy color. + typeset -g POWERLEVEL9K_PROXY_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_PROXY_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################################[ battery: internal battery ]################################# + # Show battery in red when it's below this level and not connected to power supply. + typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20 + typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=160 + # Show battery in green when it's charging or fully charged. + typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=70 + # Show battery in yellow when it's discharging. + typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=178 + # Battery pictograms going from low to high level of charge. + typeset -g POWERLEVEL9K_BATTERY_STAGES=('%K{232}▁' '%K{232}▂' '%K{232}▃' '%K{232}▄' '%K{232}▅' '%K{232}▆' '%K{232}▇' '%K{232}█') + # Don't show the remaining time to charge/discharge. + typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false + + #####################################[ wifi: wifi speed ]##################################### + # WiFi color. + typeset -g POWERLEVEL9K_WIFI_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use different colors and icons depending on signal strength ($P9K_WIFI_BARS). + # + # # Wifi colors and icons for different signal strength levels (low to high). + # typeset -g my_wifi_fg=(68 68 68 68 68) # <-- change these values + # typeset -g my_wifi_icon=('WiFi' 'WiFi' 'WiFi' 'WiFi' 'WiFi') # <-- change these values + # + # typeset -g POWERLEVEL9K_WIFI_CONTENT_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}$P9K_WIFI_LAST_TX_RATE Mbps' + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}${my_wifi_icon[P9K_WIFI_BARS+1]}' + # + # The following parameters are accessible within the expansions: + # + # Parameter | Meaning + # ----------------------+--------------- + # P9K_WIFI_SSID | service set identifier, a.k.a. network name + # P9K_WIFI_LINK_AUTH | authentication protocol such as "wpa2-psk" or "none"; empty if unknown + # P9K_WIFI_LAST_TX_RATE | wireless transmit rate in megabits per second + # P9K_WIFI_RSSI | signal strength in dBm, from -120 to 0 + # P9K_WIFI_NOISE | noise in dBm, from -120 to 0 + # P9K_WIFI_BARS | signal strength in bars, from 0 to 4 (derived from P9K_WIFI_RSSI and P9K_WIFI_NOISE) + + ####################################[ time: current time ]#################################### + # Current time color. + typeset -g POWERLEVEL9K_TIME_FOREGROUND=66 + # Format for the current time: 09:51:02. See `man 3 strftime`. + typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%I:%M:%S %p}' + # If set to true, time will update when you hit enter. This way prompts for the past + # commands will contain the start times of their commands as opposed to the default + # behavior where they contain the end times of their preceding commands. + typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false + # Custom icon. + typeset -g POWERLEVEL9K_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_TIME_PREFIX='%fat ' + + # Example of a user-defined prompt segment. Function prompt_example will be called on every + # prompt if `example` prompt segment is added to POWERLEVEL9K_LEFT_PROMPT_ELEMENTS or + # POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS. It displays an icon and orange text greeting the user. + # + # Type `p10k help segment` for documentation and a more sophisticated example. + function prompt_example() { + p10k segment -f 208 -i '⭐' -t 'hello, %n' + } + + # User-defined prompt segments may optionally provide an instant_prompt_* function. Its job + # is to generate the prompt segment for display in instant prompt. See + # https://github.com/romkatv/powerlevel10k#instant-prompt. + # + # Powerlevel10k will call instant_prompt_* at the same time as the regular prompt_* function + # and will record all `p10k segment` calls it makes. When displaying instant prompt, Powerlevel10k + # will replay these calls without actually calling instant_prompt_*. It is imperative that + # instant_prompt_* always makes the same `p10k segment` calls regardless of environment. If this + # rule is not observed, the content of instant prompt will be incorrect. + # + # Usually, you should either not define instant_prompt_* or simply call prompt_* from it. If + # instant_prompt_* is not defined for a segment, the segment won't be shown in instant prompt. + function instant_prompt_example() { + # Since prompt_example always makes the same `p10k segment` calls, we can call it from + # instant_prompt_example. This will give us the same `example` prompt segment in the instant + # and regular prompts. + prompt_example + } + + # User-defined prompt segments can be customized the same way as built-in segments. + # typeset -g POWERLEVEL9K_EXAMPLE_FOREGROUND=208 + # typeset -g POWERLEVEL9K_EXAMPLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt + # when accepting a command line. Supported values: + # + # - off: Don't change prompt when accepting a command line. + # - always: Trim down prompt when accepting a command line. + # - same-dir: Trim down prompt when accepting a command line unless this is the first command + # typed after changing current working directory. + typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always + + # Instant prompt mode. + # + # - off: Disable instant prompt. Choose this if you've tried instant prompt and found + # it incompatible with your zsh configuration files. + # - quiet: Enable instant prompt and don't print warnings when detecting console output + # during zsh initialization. Choose this if you've read and understood + # https://github.com/romkatv/powerlevel10k#instant-prompt. + # - verbose: Enable instant prompt and print a warning when detecting console output during + # zsh initialization. Choose this if you've never tried instant prompt, haven't + # seen the warning, or if you are unsure what this all means. + typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose + + # Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized. + # For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload + # can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you + # really need it. + typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true + + # If p10k is already loaded, reload configuration. + # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true. + (( ! $+functions[p10k] )) || p10k reload +} + +# Tell `p10k configure` which file it should overwrite. +typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a} + +(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]} +'builtin' 'unset' 'p10k_config_opts' diff --git a/roles/shell/files/.zshrc b/roles/shell/files/.zshrc index fae36f6..22a95b5 100644 --- a/roles/shell/files/.zshrc +++ b/roles/shell/files/.zshrc @@ -132,6 +132,7 @@ alias dc="cd ~/Documents/code" alias df="df -h" # disk usage human readable alias du="du -h" # directory usage human readable alias free="free -h" # memory usage human readable +alias sysinfo="/usr/local/bin/monitoring/sysinfo 2>/dev/null || echo 'sysinfo script not found'" # Process management alias ps="ps aux" @@ -143,6 +144,11 @@ alias ports="ss -tulpn" # open ports # Network information alias myip="curl -s http://ipecho.net/plain; echo" alias localip="ip route get 1.2.3.4 | awk '{print $7}'" +alias netinfo="/usr/local/bin/monitoring/netinfo 2>/dev/null || echo 'netinfo script not found'" + +# Software inventory - show what's installed on this system +alias showapps="$HOME/.local/bin/showapps" +alias apps="showapps" # Python alias py="python3" @@ -159,6 +165,10 @@ alias dcb="docker-compose build" alias dps="docker ps" alias di="docker images" +# IDE - suppress root warnings +alias code="code --no-sandbox --user-data-dir=/root/.vscode-root" +alias cursor="cursor --no-sandbox --disable-gpu-sandbox --appimage-extract-and-run --user-data-dir=/root/.cursor-root" + # Date and time alias now="date +'%Y-%m-%d %H:%M:%S'" alias today="date +'%Y-%m-%d'" @@ -175,3 +185,20 @@ alias own="sudo chown -R $USER:$USER" alias nfresh="rm -rf node_modules/ package-lock.json && npm install" +# >>> conda initialize >>> +# !! Contents within this block are managed by 'conda init' !! +if [ -f "$HOME/anaconda3/bin/conda" ]; then + __conda_setup="$('$HOME/anaconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)" + if [ $? -eq 0 ]; then + eval "$__conda_setup" + else + if [ -f "$HOME/anaconda3/etc/profile.d/conda.sh" ]; then + . "$HOME/anaconda3/etc/profile.d/conda.sh" + else + export PATH="$HOME/anaconda3/bin:$PATH" + fi + fi + unset __conda_setup +fi +# <<< conda initialize <<< + diff --git a/roles/shell/files/showapps.sh b/roles/shell/files/showapps.sh new file mode 100644 index 0000000..8a9c39e --- /dev/null +++ b/roles/shell/files/showapps.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Show installed software on this system +# Part of the Ansible shell role + +BOLD='\033[1m' +GREEN='\033[32m' +YELLOW='\033[33m' +RED='\033[31m' +RESET='\033[0m' + +echo -e "${BOLD}=== Installed Software ===${RESET}\n" + +echo -e "${YELLOW}Development:${RESET}" +for tool in git node python3 docker; do + if command -v $tool >/dev/null 2>&1; then + version=$(case $tool in + node) node --version 2>/dev/null ;; + python3) python3 --version 2>/dev/null | awk '{print $2}' ;; + git) git --version 2>/dev/null | awk '{print $3}' ;; + docker) docker --version 2>/dev/null | awk '{print $3}' | tr -d ',' ;; + esac) + printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version" + fi +done + +echo -e "\n${YELLOW}Data Science:${RESET}" +for tool in conda jupyter R; do + if command -v $tool >/dev/null 2>&1; then + version=$(case $tool in + conda) conda --version 2>/dev/null | awk '{print $2}' ;; + jupyter) echo "installed" ;; + R) R --version 2>/dev/null | head -1 | awk '{print $3}' ;; + esac) + printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version" + fi +done + +echo -e "\n${YELLOW}Editors:${RESET}" +for tool in cursor code vim nvim nano; do + command -v $tool >/dev/null 2>&1 && printf " ${GREEN}✓${RESET} %s\n" "$tool" +done + +echo -e "\n${YELLOW}CLI Tools:${RESET}" +for tool in tmux fzf htop btop jq yq rg fd; do + command -v $tool >/dev/null 2>&1 && printf " ${GREEN}✓${RESET} %s\n" "$tool" +done + +echo -e "\n${YELLOW}Shell:${RESET}" +echo -e " ${GREEN}✓${RESET} current: $SHELL" +[ -d "$HOME/.oh-my-zsh" ] && echo -e " ${GREEN}✓${RESET} oh-my-zsh" +[ -d "$HOME/.oh-my-zsh/custom/themes/powerlevel10k" ] && echo -e " ${GREEN}✓${RESET} powerlevel10k" + +echo "" + diff --git a/roles/shell/tasks/configure_user_shell.yml b/roles/shell/tasks/configure_user_shell.yml new file mode 100644 index 0000000..882f1ef --- /dev/null +++ b/roles/shell/tasks/configure_user_shell.yml @@ -0,0 +1,104 @@ +--- +# Configure shell for a single user +# Variable: current_user - the username to configure + +- name: "Get user information: {{ current_user }}" + ansible.builtin.getent: + database: passwd + key: "{{ current_user }}" + register: user_info + failed_when: false + +- name: "Set user home directory: {{ current_user }}" + ansible.builtin.set_fact: + user_home: "{{ user_info.ansible_facts.getent_passwd[current_user][4] }}" + when: user_info.ansible_facts.getent_passwd[current_user] is defined + +- name: Skip if user not found + ansible.builtin.debug: + msg: "User {{ current_user }} not found, skipping shell configuration" + when: user_info.ansible_facts.getent_passwd[current_user] is not defined + +- name: Configure shell environment + when: user_info.ansible_facts.getent_passwd[current_user] is defined + block: + - name: "Set zsh as default shell: {{ current_user }}" + ansible.builtin.user: + name: "{{ current_user }}" + shell: /usr/bin/zsh + become: true + + - name: "Install Oh My Zsh: {{ current_user }}" + become: true + become_user: "{{ current_user }}" + ansible.builtin.shell: sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)" "" --unattended + args: + creates: "{{ user_home }}/.oh-my-zsh" + + - name: "Clone Powerlevel10k theme: {{ current_user }}" + ansible.builtin.git: + repo: https://github.com/romkatv/powerlevel10k.git + dest: "{{ user_home }}/.oh-my-zsh/custom/themes/powerlevel10k" + version: master + depth: 1 + update: false + become: true + become_user: "{{ current_user }}" + + - name: "Install zsh plugins: {{ current_user }}" + ansible.builtin.git: + repo: "{{ item.repo }}" + dest: "{{ user_home }}/.oh-my-zsh/custom/plugins/{{ item.name }}" + version: master + depth: 1 + update: false + become: true + become_user: "{{ current_user }}" + loop: "{{ zsh_plugins }}" + + - name: "Deploy .zshrc: {{ current_user }}" + ansible.builtin.copy: + src: files/.zshrc + dest: "{{ user_home }}/.zshrc" + owner: "{{ current_user }}" + group: "{{ current_user }}" + mode: '0644' + become: true + + - name: "Deploy Powerlevel10k configuration: {{ current_user }}" + ansible.builtin.copy: + src: files/.p10k.zsh + dest: "{{ user_home }}/.p10k.zsh" + owner: "{{ current_user }}" + group: "{{ current_user }}" + mode: '0644' + become: true + + - name: "Ensure .local/bin directory exists: {{ current_user }}" + ansible.builtin.file: + path: "{{ user_home }}/.local/bin" + state: directory + owner: "{{ current_user }}" + group: "{{ current_user }}" + mode: '0755' + become: true + + - name: "Deploy showapps script: {{ current_user }}" + ansible.builtin.copy: + src: files/showapps.sh + dest: "{{ user_home }}/.local/bin/showapps" + owner: "{{ current_user }}" + group: "{{ current_user }}" + mode: '0755' + become: true + + - name: "Display post-installation instructions: {{ current_user }}" + ansible.builtin.debug: + msg: + - "=== Shell Configuration Complete for {{ current_user }} ===" + - "NOTE: Zsh has been set as the default shell." + - "To activate immediately, choose one of:" + - " 1. Log out and back in (recommended)" + - " 2. Run: exec zsh" + - " 3. Or simply run: zsh" + - "==========================================" \ No newline at end of file diff --git a/roles/shell/tasks/main.yml b/roles/shell/tasks/main.yml index 29d663a..3292dec 100644 --- a/roles/shell/tasks/main.yml +++ b/roles/shell/tasks/main.yml @@ -1,8 +1,4 @@ --- -- name: Set target user variable - ansible.builtin.set_fact: - shell_target_user: "{{ ansible_user | default(ansible_user_id) }}" - - name: Install shell packages ansible.builtin.apt: name: @@ -10,46 +6,17 @@ - tmux - fzf state: present - -- name: Install zsh plugins - ansible.builtin.git: - repo: "{{ item.repo }}" - dest: "/home/{{ shell_target_user }}/.oh-my-zsh/custom/plugins/{{ item.name }}" - version: master - depth: 1 - update: false become: true - become_user: "{{ shell_target_user }}" - loop: - - {name: "zsh-syntax-highlighting", repo: "https://github.com/zsh-users/zsh-syntax-highlighting.git"} - - {name: "zsh-autosuggestions", repo: "https://github.com/zsh-users/zsh-autosuggestions.git"} -- name: Set zsh as default shell for user - ansible.builtin.user: - name: "{{ shell_target_user }}" - shell: /usr/bin/zsh +# Merge shell_users and shell_additional_users +- name: Build complete user list + ansible.builtin.set_fact: + shell_all_users: "{{ (shell_users | default([ansible_user])) + (shell_additional_users | default([])) | unique }}" -- name: Install Oh My Zsh for user - become: true - become_user: "{{ shell_target_user }}" - ansible.builtin.shell: sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)" "" --unattended - args: - creates: "/home/{{ shell_target_user }}/.oh-my-zsh" - -- name: Clone Powerlevel10k theme - ansible.builtin.git: - repo: https://github.com/romkatv/powerlevel10k.git - dest: "/home/{{ shell_target_user }}/.oh-my-zsh/custom/themes/powerlevel10k" - version: master - depth: 1 - update: false - become: true - become_user: "{{ shell_target_user }}" - -- name: Deploy .zshrc for user - ansible.builtin.copy: - src: files/.zshrc - dest: "/home/{{ shell_target_user }}/.zshrc" - owner: "{{ shell_target_user }}" - group: "{{ shell_target_user }}" - mode: '0644' +# Configure shell for each user +- name: Configure shell for each user + ansible.builtin.include_tasks: configure_user_shell.yml + loop: "{{ shell_all_users }}" + loop_control: + loop_var: current_user + when: shell_all_users is defined and shell_all_users | length > 0 diff --git a/roles/snap/README.md b/roles/snap/README.md deleted file mode 100644 index 169dacb..0000000 --- a/roles/snap/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Role: snap - -## Description -Installs and configures Snap package manager and essential snap applications for development. - -## Requirements -- Ansible 2.9+ -- Ubuntu/Debian systems -- Internet access for snap store - -## Installed Snap Applications -- **Visual Studio Code**: Popular code editor -- **Cursor**: AI-powered code editor -- **yq**: YAML processor -- **btop**: Modern system monitor -- **bandwhich**: Network utilization monitor - -## Variables - -| Variable | Default | Description | -|----------|---------|-------------| -| `snap_install_vscode` | `true` | Install VS Code via snap | -| `snap_install_cursor` | `true` | Install Cursor editor | -| `snap_install_yq` | `true` | Install yq YAML processor | -| `snap_install_btop` | `true` | Install btop system monitor | - -## Dependencies -- `base` role (for system packages) - -## Example Playbook - -```yaml -- hosts: dev - roles: - - { role: snap, snap_install_cursor: false } -``` - -## Tags -- `snap`: All snap-related tasks -- `apps`: Application installations -- `editors`: Code editor installations - -## Notes -- Enables universe repository for snap support -- Waits for snap system to be ready before installing -- Some snaps may require additional permissions -- Classic confinement used for development tools \ No newline at end of file diff --git a/roles/snap/defaults/main.yml b/roles/snap/defaults/main.yml deleted file mode 100644 index ed97d53..0000000 --- a/roles/snap/defaults/main.yml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/roles/snap/handlers/main.yml b/roles/snap/handlers/main.yml deleted file mode 100644 index 9b2523e..0000000 --- a/roles/snap/handlers/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- name: Restart snapd - ansible.builtin.systemd: - name: snapd - state: restarted diff --git a/roles/snap/tasks/main.yml b/roles/snap/tasks/main.yml deleted file mode 100644 index 0a55b23..0000000 --- a/roles/snap/tasks/main.yml +++ /dev/null @@ -1,78 +0,0 @@ ---- -- name: Enable 'universe' repo (Ubuntu and Mint only) - ansible.builtin.command: add-apt-repository universe - when: ansible_facts['distribution'] in ["Ubuntu", "Linux Mint"] - changed_when: false - failed_when: false - -- name: Remove Mint's nosnap.pref block (Mint only) - ansible.builtin.file: - path: /etc/apt/preferences.d/nosnap.pref - state: absent - when: ansible_facts['distribution'] == "Linux Mint" - -- name: Install Snap daemon - ansible.builtin.apt: - name: snapd - state: present - when: ansible_facts['os_family'] == "Debian" - notify: restart snapd - -- name: Enable snapd socket on Debian - ansible.builtin.systemd: - name: snapd.socket - enabled: true - state: started - when: ansible_facts['distribution'] == "Debian" - -- name: Force Ansible to reload facts - ansible.builtin.setup: - -- name: Wait for snapd to be ready - ansible.builtin.command: snap wait system seed.loaded - register: snap_wait_result - until: snap_wait_result.rc == 0 - retries: 10 - delay: 5 - when: ansible_facts['os_family'] == "Debian" - failed_when: false - changed_when: false - -- name: Check if snap is working - ansible.builtin.command: snap list - register: snap_check - when: ansible_facts['os_family'] == "Debian" - failed_when: false - changed_when: false - -- name: Install VSCode IDE (Snap, all distros) - community.general.snap: - name: code - classic: true - state: present - when: - - ansible_facts['os_family'] == "Debian" - - snap_check is defined - - snap_check.rc == 0 - failed_when: false - -- name: Install Cursor (Snap, all distros) - community.general.snap: - name: cursor - state: present - when: - - ansible_facts['os_family'] == "Debian" - - snap_check is defined - - snap_check.rc == 0 - failed_when: false - -- name: Display snap installation status - ansible.builtin.debug: - msg: | - Snap check result: {{ snap_check.rc if snap_check is defined else 'not defined' }} - VSCode and Cursor installation may have failed if snap is not properly configured. - You may need to manually install these applications or troubleshoot snap on this host. - when: - - ansible_facts['os_family'] == "Debian" - - snap_check is defined - - snap_check.rc != 0 diff --git a/roles/ssh/defaults/main.yml b/roles/ssh/defaults/main.yml index a27e54f..da89ffc 100644 --- a/roles/ssh/defaults/main.yml +++ b/roles/ssh/defaults/main.yml @@ -2,14 +2,14 @@ # SSH server configuration ssh_port: 22 ssh_listen_addresses: ['0.0.0.0'] -ssh_permit_root_login: 'no' -ssh_password_authentication: 'no' +ssh_permit_root_login: 'yes' +ssh_password_authentication: 'yes' ssh_pubkey_authentication: 'yes' ssh_max_auth_tries: 3 ssh_client_alive_interval: 300 ssh_max_sessions: 10 ssh_allowed_users: [] # Restrict to specific users -ssh_allowed_groups: ['sudo', 'ssh'] +ssh_allowed_groups: ['sudo'] # Security hardening ssh_kex_algorithms: diff --git a/scripts/inventory.sh b/scripts/inventory.sh new file mode 100755 index 0000000..31a29b8 --- /dev/null +++ b/scripts/inventory.sh @@ -0,0 +1,147 @@ +#!/bin/bash +# Dynamic inventory script - lists all installed tools and services +# Can be run on any managed host + +# Colors +BOLD='\033[1m' +GREEN='\033[32m' +YELLOW='\033[33m' +BLUE='\033[34m' +RED='\033[31m' +RESET='\033[0m' + +echo -e "${BOLD}=== Installed Software Inventory ===${RESET}\n" + +# Function to check if command exists +cmd_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Function to get version +get_version() { + if cmd_exists "$1"; then + case "$1" in + node) node --version 2>/dev/null ;; + python3) python3 --version 2>/dev/null | awk '{print $2}' ;; + git) git --version 2>/dev/null | awk '{print $3}' ;; + docker) docker --version 2>/dev/null | awk '{print $3}' | tr -d ',' ;; + conda) conda --version 2>/dev/null | awk '{print $2}' ;; + jupyter) jupyter --version 2>/dev/null | head -1 | awk '{print $3}' ;; + R) R --version 2>/dev/null | head -1 | awk '{print $3}' ;; + yq) yq --version 2>/dev/null | awk '{print $NF}' ;; + btop) btop --version 2>/dev/null | head -1 | awk '{print $3}' ;; + cursor) echo "installed" ;; + *) echo "unknown" ;; + esac + fi +} + +# Development Tools +echo -e "${YELLOW}Development Tools:${RESET}" +for tool in git node python3; do + version=$(get_version $tool) + if [ -n "$version" ]; then + printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version" + else + printf " ${RED}✗${RESET} %-15s %s\n" "$tool" "not installed" + fi +done + +# Data Science +echo -e "\n${YELLOW}Data Science Stack:${RESET}" +for tool in conda jupyter R; do + version=$(get_version $tool) + if [ -n "$version" ]; then + printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version" + else + printf " ${RED}✗${RESET} %-15s %s\n" "$tool" "not installed" + fi +done + +# Check Jupyter service +if systemctl is-active --quiet jupyter-notebook 2>/dev/null; then + echo -e " ${GREEN}✓${RESET} Jupyter service running" +else + echo -e " ${RED}✗${RESET} Jupyter service not running" +fi + +# IDEs & Editors +echo -e "\n${YELLOW}IDEs & Editors:${RESET}" +for tool in cursor code; do + if cmd_exists $tool; then + printf " ${GREEN}✓${RESET} %-15s installed\n" "$tool" + else + printf " ${RED}✗${RESET} %-15s not installed\n" "$tool" + fi +done + +# Container Platform +echo -e "\n${YELLOW}Container Platform:${RESET}" +version=$(get_version docker) +if [ -n "$version" ]; then + printf " ${GREEN}✓${RESET} %-15s %s\n" "docker" "$version" + if cmd_exists docker-compose; then + compose_version=$(docker-compose --version 2>/dev/null | awk '{print $NF}') + printf " ${GREEN}✓${RESET} %-15s %s\n" "docker-compose" "$compose_version" + fi +else + printf " ${RED}✗${RESET} %-15s not installed\n" "docker" +fi + +# Monitoring Tools +echo -e "\n${YELLOW}Monitoring Tools:${RESET}" +for tool in htop btop iotop nethogs; do + if cmd_exists $tool; then + version=$(get_version $tool) + if [ "$version" = "unknown" ] || [ -z "$version" ]; then + printf " ${GREEN}✓${RESET} %-15s installed\n" "$tool" + else + printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version" + fi + else + printf " ${RED}✗${RESET} %-15s not installed\n" "$tool" + fi +done + +# CLI Utilities +echo -e "\n${YELLOW}CLI Utilities:${RESET}" +for tool in jq yq ripgrep fd; do + if cmd_exists $tool; then + version=$(get_version $tool) + if [ "$version" = "unknown" ] || [ -z "$version" ]; then + printf " ${GREEN}✓${RESET} %-15s installed\n" "$tool" + else + printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version" + fi + else + printf " ${RED}✗${RESET} %-15s not installed\n" "$tool" + fi +done + +# Shell +echo -e "\n${YELLOW}Shell Environment:${RESET}" +current_shell=$(basename "$SHELL") +printf " ${GREEN}✓${RESET} %-15s %s\n" "current shell" "$current_shell" + +if [ -d "$HOME/.oh-my-zsh" ]; then + printf " ${GREEN}✓${RESET} %-15s installed\n" "oh-my-zsh" +fi + +if [ -d "$HOME/.oh-my-zsh/custom/themes/powerlevel10k" ]; then + printf " ${GREEN}✓${RESET} %-15s installed\n" "powerlevel10k" +fi + +# Services +echo -e "\n${YELLOW}System Services:${RESET}" +for service in docker tailscaled fail2ban sshd; do + if systemctl is-active --quiet $service 2>/dev/null; then + printf " ${GREEN}✓${RESET} %-15s running\n" "$service" + elif systemctl list-unit-files | grep -q "^$service"; then + printf " ${BLUE}○${RESET} %-15s installed (not running)\n" "$service" + else + printf " ${RED}✗${RESET} %-15s not installed\n" "$service" + fi +done + +echo "" +