diff --git a/.ansible-lint b/.ansible-lint index 9a7f606..f871453 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -1,7 +1,14 @@ -# Ansible Lint Configuration --- -# Exclude patterns +# ansible-lint configuration +# +# We exclude inventory host/group vars because many contain vault-encrypted content +# that cannot be parsed without vault secrets in CI/dev environments. exclude_paths: + - inventories/production/host_vars/ + - inventories/production/group_vars/all/vault.yml + - inventories/production/group_vars/all/vault.example.yml + +# Exclude patterns - .cache/ - .github/ - .ansible/ diff --git a/.cursor/rules/project-rules.mdc b/.cursor/rules/project-rules.mdc new file mode 100644 index 0000000..1859ada --- /dev/null +++ b/.cursor/rules/project-rules.mdc @@ -0,0 +1,33 @@ +## Project rules (Ansible infrastructure repo) + +### Canonical documentation + +- Start here: `project-docs/index.md` +- Architecture: `project-docs/architecture.md` +- Standards: `project-docs/standards.md` +- Workflow: `project-docs/workflow.md` +- Decisions: `project-docs/decisions.md` + +### Repo structure (high level) + +- **Inventory**: `inventories/production/` +- **Playbooks**: `playbooks/` + - `playbooks/servers.yml`: server baseline + - `playbooks/workstations.yml`: workstation baseline + desktop apps on `desktop` group only + - `playbooks/app/*`: Proxmox app-project suite +- **Roles**: `roles/*` (standard Ansible role layout) + +### Key standards to follow + +- **YAML**: 2-space indentation; tasks must have `name:` +- **Modules**: prefer native modules; use FQCN (e.g., `ansible.builtin.*`, `community.general.*`) +- **Idempotency**: no “always-changed” shell tasks; use `changed_when:` / `creates:` / `removes:` +- **Secrets**: never commit plaintext; use Ansible Vault with `vault_`-prefixed vars +- **Makefile-first**: prefer `make ...` targets over raw `ansible-playbook` + +### Architectural decisions (must not regress) + +- Editor/IDE installation is **out of scope** for Ansible roles/playbooks. +- Monitoring is split: `monitoring_server` vs `monitoring_desktop`. +- Desktop applications run only for `desktop` group (via workstations playbook). + diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index f5d0867..5d7211e 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,7 +1,7 @@ --- name: CI -on: +"on": push: branches: [master] pull_request: @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 1 - + - name: Check if CI should be skipped id: check run: | @@ -53,7 +53,7 @@ jobs: SKIP=1 fi fi - + echo "skip=$SKIP" >> $GITHUB_OUTPUT echo "Branch: $BRANCH_NAME" echo "Commit: ${COMMIT_MSG:0:50}..." diff --git a/Makefile b/Makefile index d65bb56..5267281 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.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 +.PHONY: help bootstrap lint test check dev datascience inventory inventory-all local servers workstations clean status tailscale tailscale-check tailscale-dev tailscale-status create-vault create-vm monitoring .DEFAULT_GOAL := help ## Colors for output @@ -13,6 +13,8 @@ RESET := \033[0m PLAYBOOK_SITE := playbooks/site.yml PLAYBOOK_DEV := playbooks/development.yml PLAYBOOK_LOCAL := playbooks/local.yml +PLAYBOOK_SERVERS := playbooks/servers.yml +PLAYBOOK_WORKSTATIONS := playbooks/workstations.yml PLAYBOOK_MAINTENANCE := playbooks/maintenance.yml PLAYBOOK_TAILSCALE := playbooks/tailscale.yml PLAYBOOK_PROXMOX := playbooks/infrastructure/proxmox-vm.yml @@ -251,6 +253,28 @@ local: ## Run the local playbook on localhost @echo "$(YELLOW)Applying local playbook...$(RESET)" $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_LOCAL) -K +servers: ## Run baseline server playbook (usage: make servers [GROUP=services] [HOST=host1]) + @echo "$(YELLOW)Applying server baseline...$(RESET)" + @EXTRA=""; \ + if [ -n "$(HOST)" ]; then \ + $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_SERVERS) --limit $(HOST); \ + elif [ -n "$(GROUP)" ]; then \ + $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_SERVERS) -e target_group=$(GROUP); \ + else \ + $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_SERVERS); \ + fi + +workstations: ## Run workstation baseline (usage: make workstations [GROUP=dev] [HOST=dev01]) + @echo "$(YELLOW)Applying workstation baseline...$(RESET)" + @EXTRA=""; \ + if [ -n "$(HOST)" ]; then \ + $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_WORKSTATIONS) --limit $(HOST); \ + elif [ -n "$(GROUP)" ]; then \ + $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_WORKSTATIONS) -e target_group=$(GROUP); \ + else \ + $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_WORKSTATIONS); \ + fi + # Host-specific targets dev: ## Run on specific host (usage: make dev HOST=dev01) ifndef HOST @@ -381,7 +405,7 @@ shell-all: ## Configure shell on all shell_hosts (usage: make shell-all) apps: ## Install applications only @echo "$(YELLOW)Installing applications...$(RESET)" - $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags apps + $(ANSIBLE_PLAYBOOK) $(PLAYBOOK_WORKSTATIONS) --tags apps # Connectivity targets ping: auto-fallback ## Ping hosts with colored output (usage: make ping [GROUP=dev] [HOST=dev01]) diff --git a/README.md b/README.md index 751371e..71be170 100644 --- a/README.md +++ b/README.md @@ -77,4 +77,5 @@ ansible/ ## Documentation - **Guides**: `docs/guides/` -- **Reference**: `docs/reference/` \ No newline at end of file +- **Reference**: `docs/reference/` +- **Project docs (architecture/standards/workflow)**: `project-docs/index.md` \ No newline at end of file diff --git a/configure_app.yml b/configure_app.yml index 1fef14e..b634eb4 100644 --- a/configure_app.yml +++ b/configure_app.yml @@ -5,5 +5,3 @@ - name: Configure app project guests import_playbook: playbooks/app/configure_app.yml - - diff --git a/docs/guides/custom-roles.md b/docs/guides/custom-roles.md new file mode 100644 index 0000000..acda599 --- /dev/null +++ b/docs/guides/custom-roles.md @@ -0,0 +1,28 @@ +# Custom roles guide + +This repo is designed to be extended by adding new roles under `roles/`. + +## Role structure + +Follow the standard Ansible role layout: + +``` +roles// +├── defaults/main.yml +├── handlers/main.yml +├── tasks/main.yml +├── templates/ +├── files/ +└── README.md +``` + +## Where to wire a new role + +- Add it to the relevant playbook under `playbooks/` (or create a new playbook if it’s a major concern). +- Prefer tagging the role at inclusion time so `make ` targets can use `--tags`. + +## Standards (canonical) + +- `project-docs/standards.md` +- `project-docs/decisions.md` (add an ADR entry for significant changes) + diff --git a/docs/guides/monitoring.md b/docs/guides/monitoring.md new file mode 100644 index 0000000..6a7a4f1 --- /dev/null +++ b/docs/guides/monitoring.md @@ -0,0 +1,22 @@ +# Monitoring guide + +Monitoring is split by host type: + +- **Servers**: `roles/monitoring_server/` (includes `fail2ban`, sysstat tooling) +- **Desktops/workstations**: `roles/monitoring_desktop/` (desktop-oriented tooling) + +## Run monitoring only + +```bash +# Dry-run +make monitoring CHECK=true + +# Apply +make monitoring +``` + +## Notes + +- Desktop apps are installed only on the `desktop` group via `playbooks/workstations.yml`. +- If you need packet analysis tools, keep them opt-in (see `docs/reference/applications.md`). + diff --git a/docs/guides/security.md b/docs/guides/security.md new file mode 100644 index 0000000..2dd40a7 --- /dev/null +++ b/docs/guides/security.md @@ -0,0 +1,31 @@ +# Security hardening guide + +This repo’s “security” work is primarily implemented via roles and inventory defaults. + +## What runs where + +- **SSH hardening + firewall**: `roles/ssh/` +- **Baseline packages/security utilities**: `roles/base/` +- **Monitoring + intrusion prevention (servers)**: `roles/monitoring_server/` (includes `fail2ban`) +- **Secrets**: Ansible Vault in `inventories/production/group_vars/all/vault.yml` + +## Recommended flow + +```bash +# Dry-run first +make check + +# Apply only security-tagged roles +make security +``` + +## Secrets / Vault + +Use vault for anything sensitive: + +- Guide: `docs/guides/vault.md` + +## Canonical standards + +- `project-docs/standards.md` + diff --git a/docs/project-docs.md b/docs/project-docs.md new file mode 100644 index 0000000..4820406 --- /dev/null +++ b/docs/project-docs.md @@ -0,0 +1,13 @@ +# Project docs (canonical) + +Canonical project documentation lives in `project-docs/` (repo root): + +- Index: `project-docs/index.md` +- Overview: `project-docs/overview.md` +- Architecture: `project-docs/architecture.md` +- Standards: `project-docs/standards.md` +- Workflow: `project-docs/workflow.md` +- Decisions (ADRs): `project-docs/decisions.md` + +This file exists so users browsing `docs/` can quickly find the canonical project documentation. + diff --git a/docs/reference/applications.md b/docs/reference/applications.md index fed9515..a629b9b 100644 --- a/docs/reference/applications.md +++ b/docs/reference/applications.md @@ -54,10 +54,7 @@ Complete inventory of applications and tools deployed by Ansible playbooks. | zsh | Z shell | apt | shell | | tmux | Terminal multiplexer | apt | shell | | fzf | Fuzzy finder | apt | shell | -| oh-my-zsh | Zsh framework | git | shell | -| powerlevel10k | Zsh theme | git | shell | -| zsh-syntax-highlighting | Syntax highlighting | git | shell | -| zsh-autosuggestions | Command suggestions | git | shell | +| zsh aliases | Minimal alias set (sourced from ~/.zshrc) | file | shell | ### 📊 Monitoring Tools | Package | Description | Source | Role | @@ -84,16 +81,34 @@ Complete inventory of applications and tools deployed by Ansible playbooks. ### 🖱️ Desktop Applications | Package | Description | Source | Role | |---------|-------------|--------|------| -| brave-browser | Privacy-focused browser | brave | applications | -| libreoffice | Office suite | apt | applications | +| copyq | Clipboard manager (history/search) | apt | applications | | evince | PDF viewer | apt | applications | | redshift | Blue light filter | apt | applications | -### 📝 Code Editors -| Package | Description | Source | Role | -|---------|-------------|--------|------| -| code | Visual Studio Code | snap | snap | -| cursor | AI-powered editor | snap | snap | +## Nice-to-have apps (not installed by default) + +These are good add-ons depending on how you use your workstations. Keep them opt-in to avoid bloating baseline installs. + +### Desktop / UX + +- **flameshot**: screenshots + annotation +- **keepassxc**: local password manager (or use your preferred) +- **syncthing**: peer-to-peer file sync (if you want self-hosted sync) +- **remmina**: RDP/VNC client +- **mpv**: lightweight media player + +### Developer workstation helpers + +- **direnv**: per-project env var loading +- **shellcheck**: shell script linting +- **jq** / **yq**: JSON/YAML CLI tooling (already in base here, but listing for completeness) +- **ripgrep** / **fd-find**: fast search/find (already in base here) + +### Networking / diagnostics + +- **wireshark** (GUI) or **wireshark-common**: packet analysis (only if you need it) +- **iperf3**: bandwidth testing +- **dnsutils**: dig/nslookup tools ## Installation by Playbook @@ -103,7 +118,6 @@ Installs all roles for development machines: - Development environment - Docker platform - Shell configuration -- Desktop applications - Monitoring tools - Tailscale VPN @@ -112,7 +126,11 @@ Installs for local machine management: - Core system tools - Shell environment - Development basics -- Selected applications + +### `playbooks/workstations.yml` +Installs baseline for `dev:desktop:local`, and installs desktop apps only for the `desktop` group: +- Workstation baseline (dev + desktop + local) +- Desktop applications (desktop group only) ### `playbooks/maintenance.yml` Maintains existing installations: @@ -135,7 +153,6 @@ Maintains existing installations: | snap | Snap packages | snapd daemon | | docker | Docker repository | Docker GPG key + repo | | tailscale | Tailscale repository | Tailscale GPG key + repo | -| brave | Brave browser repository | Brave GPG key + repo | | git | Git repositories | Direct clone | ## Services Enabled diff --git a/docs/reference/architecture.md b/docs/reference/architecture.md index 54f9ba2..3c08909 100644 --- a/docs/reference/architecture.md +++ b/docs/reference/architecture.md @@ -1,263 +1,10 @@ -# Architecture Overview +# Architecture (canonical doc moved) -Technical architecture and design of the Ansible infrastructure management system. +The canonical architecture document is now: -## System Architecture +- `project-docs/architecture.md` -``` -┌─────────────────────────────────────────────────────────────┐ -│ Control Machine | -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ Ansible │ │ Makefile │ │ Vault │ │ -│ │ Engine │ │ Automation │ │ Secrets │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ -└─────────────────────┬───────────────────────────────────────┘ - │ SSH + Tailscale VPN - ┌─────────────────┴──────────────────────────────┐ - │ │ -┌───▼────────┐ ┌──────────────┐ ┌─────────────────▼────────┐ -│ Dev │ │ Service │ │ Infrastructure │ -│ Machines │ │ VMs │ │ VMs │ -├────────────┤ ├──────────────┤ ├──────────────────────────┤ -│ • dev01 │ │ • giteaVM │ │ • Proxmox Controller │ -│ • bottom │ │ • portainerVM│ │ • Future VMs │ -│ • desktop │ │ • homepageVM │ │ │ -└────────────┘ └──────────────┘ └──────────────────────────┘ -``` - -## Network Topology - -### Physical Network -- **LAN**: 192.168.1.0/24 (typical home/office network) -- **Proxmox Host**: Hypervisor for VM management -- **Physical Machines**: Direct network access - -### Tailscale Overlay Network -- **Mesh VPN**: Secure peer-to-peer connections -- **100.x.x.x**: Tailscale IP range -- **Zero-config**: Automatic NAT traversal -- **End-to-end encryption**: WireGuard protocol - -## Host Groups - -### Development (`dev`) -**Purpose**: Developer workstations and environments -- **Hosts**: dev01, bottom, debianDesktopVM -- **OS**: Debian/Ubuntu -- **Roles**: Full development stack - -### Services -**Purpose**: Self-hosted services and applications - -#### Gitea (`gitea`) -- **Host**: giteaVM -- **OS**: Alpine Linux (lightweight) -- **Service**: Git repository hosting - -#### Portainer (`portainer`) -- **Host**: portainerVM -- **OS**: Alpine Linux -- **Service**: Container management UI - -#### Homepage (`homepage`) -- **Host**: homepageVM -- **OS**: Debian -- **Service**: Service dashboard - -### Infrastructure (`ansible`) -**Purpose**: Ansible control and automation -- **Host**: Ansible controller VM -- **OS**: Ubuntu Server -- **Service**: Infrastructure automation - -### Local (`local`) -**Purpose**: Local machine management -- **Host**: localhost -- **Connection**: Local (no SSH) - -## Playbook Architecture - -### Core Playbooks - -```yaml -playbooks/development.yml # Development environment setup -├── roles/maintenance # System updates -├── roles/base # Core packages -├── roles/ssh # SSH hardening -├── roles/user # User management -├── roles/development # Dev tools -├── roles/shell # Shell config -├── roles/docker # Container platform -├── roles/applications # Desktop apps -├── roles/snap # Snap packages -├── roles/tailscale # VPN setup -├── roles/monitoring # Monitoring tools - -playbooks/local.yml # Local machine -├── roles/base -├── roles/shell -├── roles/development -└── roles/tailscale - -playbooks/maintenance.yml # System maintenance -└── roles/maintenance - -playbooks/tailscale.yml # VPN deployment -└── roles/tailscale - -playbooks/infrastructure/proxmox-vm.yml # KVM VM provisioning (controller VM, etc.) -└── roles/proxmox_vm - -playbooks/app/site.yml # Proxmox app stack (LXC-first) -├── playbooks/app/provision_vms.yml # Proxmox API provisioning (LXC/KVM) -└── playbooks/app/configure_app.yml # Guest OS + app configuration over SSH -``` - -### Role Dependencies - -``` -base -├── Required by: all other roles -├── Provides: core utilities, security tools -└── Dependencies: none - -ssh -├── Required by: secure access -├── Provides: hardened SSH, firewall -└── Dependencies: base - -user -├── Required by: system access -├── Provides: user accounts, sudo -└── Dependencies: base - -development -├── Required by: coding tasks -├── Provides: git, nodejs, python -└── Dependencies: base - -docker -├── Required by: containerization -├── Provides: Docker CE, compose -└── Dependencies: base - -tailscale -├── Required by: secure networking -├── Provides: mesh VPN -└── Dependencies: base -``` - -## Data Flow - -### Configuration Management -1. **Variables** → `inventories/production/group_vars/all/main.yml` -2. **Secrets** → `inventories/production/group_vars/all/vault.yml` (encrypted) -3. **Host Config** → `inventories/production/host_vars/.yml` -4. **Role Defaults** → roles/*/defaults/main.yml -5. **Tasks** → roles/*/tasks/main.yml -6. **Templates** → roles/*/templates/*.j2 -7. **Handlers** → roles/*/handlers/main.yml - -### Execution Flow -``` -make command - ↓ -Makefile target - ↓ -ansible-playbook - ↓ -Inventory + Variables - ↓ -Role execution - ↓ -Task processing - ↓ -Handler notification - ↓ -Result reporting -``` - -## Security Architecture - -### Defense in Depth - -#### Network Layer -- **Tailscale VPN**: Encrypted mesh network -- **UFW Firewall**: Default deny, explicit allow -- **SSH Hardening**: Key-only, rate limiting - -#### Application Layer -- **Fail2ban**: Intrusion prevention -- **Package signing**: GPG verification -- **Service isolation**: Docker containers - -#### Data Layer -- **Ansible Vault**: Encrypted secrets -- **SSH Keys**: Ed25519 cryptography - -### Access Control - -``` -User → SSH Key → Jump Host → Tailscale → Target Host - ↓ ↓ ↓ ↓ - Ed25519 Bastion WireGuard Firewall - Encryption Rules -``` - -## Storage Architecture - - -### Configuration Storage -``` -/etc/ # System configuration -/opt/ # Application data -/usr/local/ # Custom scripts -/var/log/ # Logs and audit trails -``` - -## Monitoring Architecture - -### System Monitoring -- **btop/htop**: Process monitoring -- **iotop**: I/O monitoring -- **nethogs**: Network per-process -- **Custom dashboards**: sysinfo, netinfo - -### Log Management -- **logwatch**: Daily summaries -- **journald**: System logs -- **fail2ban**: Security logs - -## Scalability Considerations - -### Horizontal Scaling -- Add hosts to inventory groups -- Parallel execution with ansible forks -- Role reusability across environments - -### Vertical Scaling -- Proxmox VM resource adjustment -- Docker resource limits -- Service-specific tuning - -## Technology Stack - -### Core Technologies -- **Ansible**: 2.9+ (Configuration management) -- **Python**: 3.x (Ansible runtime) -- **Jinja2**: Templating engine -- **YAML**: Configuration format - -### Target Platforms -- **Debian**: 11+ (Bullseye, Bookworm) -- **Ubuntu**: 20.04+ (Focal, Jammy, Noble) -- **Alpine**: 3.x (Lightweight containers) - -### Service Technologies -- **Docker**: Container runtime -- **Tailscale**: Mesh VPN -- **SystemD**: Service management -- **UFW**: Firewall management +This `docs/reference/architecture.md` file is kept as a pointer to avoid maintaining two competing sources of truth. ## Best Practices diff --git a/docs/reference/network.md b/docs/reference/network.md new file mode 100644 index 0000000..d8b5024 --- /dev/null +++ b/docs/reference/network.md @@ -0,0 +1,21 @@ +# Network reference + +## Overview + +This repo manages hosts reachable over your LAN and optionally over a Tailscale overlay network. + +## Physical network + +- Typical LAN: `192.168.1.0/24` (adjust for your environment) +- Inventory host addressing is defined in `inventories/production/hosts` + +## Tailscale overlay + +- Tailscale provides a mesh VPN (WireGuard-based) with `100.x.y.z` addresses. +- The repo installs/configures it via `playbooks/tailscale.yml` + `roles/tailscale/`. + +## References + +- Tailscale guide: `docs/guides/tailscale.md` +- Canonical architecture: `project-docs/architecture.md` + diff --git a/docs/reference/playbooks-and-tags.md b/docs/reference/playbooks-and-tags.md new file mode 100644 index 0000000..65bcbb5 --- /dev/null +++ b/docs/reference/playbooks-and-tags.md @@ -0,0 +1,281 @@ +# Playbooks & Tags Map + +This repo is organized around playbooks in `playbooks/` (plus a few thin wrapper playbooks in the repo root). + +This reference gives you: +- **Execution paths**: where each playbook “goes” (imports → roles → included tasks). +- **Tag paths**: what each tag actually selects, including Makefile shortcuts. + +--- + +## Playbook entrypoints (paths) + +### `site.yml` (wrapper) + +`site.yml` is a wrapper that delegates to `playbooks/site.yml`. + +```mermaid +flowchart TD + A[site.yml] --> B[playbooks/site.yml] +``` + +### `playbooks/site.yml` (dispatcher) + +This is a pure dispatcher: it imports other playbooks and assigns **top-level tags** per import. + +```mermaid +flowchart TD + S[playbooks/site.yml] -->|tags: maintenance| M[playbooks/maintenance.yml] + S -->|tags: development| D[playbooks/development.yml] + S -->|tags: tailscale| T[playbooks/tailscale.yml] + S -->|tags: app| A[playbooks/app/site.yml] +``` + +### `playbooks/maintenance.yml` + +```mermaid +flowchart TD + P[playbooks/maintenance.yml] --> R[role: maintenance] +``` + +- **Notes**: + - `pre_tasks`/`post_tasks` in the playbook are **untagged**; if you run with + `--tags maintenance`, only the `maintenance` role runs (the untagged pre/post tasks are skipped). + +### `playbooks/development.yml` + +Targets: `hosts: dev` + +```mermaid +flowchart TD + P[playbooks/development.yml] --> R1[role: maintenance] + P --> R2[role: base] + P --> R3[role: user] + P --> R4[role: ssh] + P --> R5[role: shell] + P --> R6[role: development] + P --> R7[role: datascience] + P --> R8[role: docker] + P --> R9[role: monitoring_desktop] + + %% role-internal paths that matter + R5 --> S1[roles/shell/tasks/main.yml] + S1 --> S2[include_tasks: roles/shell/tasks/configure_user_shell.yml] + + R8 --> D1[roles/docker/tasks/main.yml] + D1 --> D2[include_tasks: roles/docker/tasks/setup_gpg_key.yml] + D1 --> D3[include_tasks: roles/docker/tasks/setup_repo_*.yml] +``` + +- **Notes**: + - `pre_tasks` is **untagged**; if you run with tag filters, that apt-cache update is skipped unless you include + `--tags all` or also run untagged tasks (Ansible has `--skip-tags`/`--tags` behavior to be aware of). + +### `playbooks/local.yml` + +Targets: `hosts: localhost`, `connection: local` + +This is basically the same role stack as `playbooks/development.yml` (minus `datascience`), but applied locally. + +```mermaid +flowchart TD + P[playbooks/local.yml] --> R1[role: maintenance] + P --> R2[role: base] + P --> R3[role: user] + P --> R4[role: ssh] + P --> R5[role: shell] + P --> R6[role: development] + P --> R7[role: docker] + P --> R8[role: monitoring_desktop] +``` + +### `playbooks/servers.yml` + +Targets by default: `services:qa:ansible:tailscale` (override via `-e target_group=...`). + +```mermaid +flowchart TD + P[playbooks/servers.yml] --> R1[role: maintenance] + P --> R2[role: base] + P --> R3[role: user] + P --> R4[role: ssh] + P --> R5[role: shell] + P --> R6[role: docker] + P --> R7[role: monitoring_server] +``` + +### `playbooks/workstations.yml` + +Two plays: +- Workstation baseline for `dev:desktop:local` +- Desktop applications only for the `desktop` group + +```mermaid +flowchart TD + W[playbooks/workstations.yml] --> B1[play: workstation baseline] + B1 --> R1[role: maintenance] + B1 --> R2[role: base] + B1 --> R3[role: user] + B1 --> R4[role: ssh] + B1 --> R5[role: shell] + B1 --> R6[role: development] + B1 --> R7[role: datascience] + B1 --> R8[role: docker] + B1 --> R9[role: monitoring_desktop] + + W --> B2[play: desktop apps] + B2 --> A1[role: applications] +``` + +### `playbooks/shell.yml` + +```mermaid +flowchart TD + P[playbooks/shell.yml] --> R[role: shell] + R --> S1[roles/shell/tasks/main.yml] + S1 --> S2[include_tasks: roles/shell/tasks/configure_user_shell.yml] +``` + +### `playbooks/tailscale.yml` + +```mermaid +flowchart TD + P[playbooks/tailscale.yml] --> R[role: tailscale] + R --> T1[roles/tailscale/tasks/main.yml] + T1 -->|Debian| T2[include_tasks: roles/tailscale/tasks/debian.yml] + T1 -->|Alpine| T3[include_tasks: roles/tailscale/tasks/alpine.yml] +``` + +### `playbooks/infrastructure/proxmox-vm.yml` + +Creates an Ansible controller VM on Proxmox (local connection) via `role: proxmox_vm`. + +```mermaid +flowchart TD + P[playbooks/infrastructure/proxmox-vm.yml] --> R[role: proxmox_vm] + R --> M1[roles/proxmox_vm/tasks/main.yml] + M1 -->|proxmox_guest_type=lxc| L1[include_tasks: roles/proxmox_vm/tasks/lxc.yml] + M1 -->|else| K1[include_tasks: roles/proxmox_vm/tasks/kvm.yml] +``` + +### App project suite (`playbooks/app/*`) + +#### `playbooks/app/site.yml` (app dispatcher) + +```mermaid +flowchart TD + S[playbooks/app/site.yml] -->|tags: app,provision| P[playbooks/app/provision_vms.yml] + S -->|tags: app,configure| C[playbooks/app/configure_app.yml] +``` + +#### `playbooks/app/provision_vms.yml` (provision app guests) + +High-level loop: `project_key` → `env_item` → provision guest → add to dynamic inventory groups. + +```mermaid +flowchart TD + P[playbooks/app/provision_vms.yml] --> T1[include_tasks: playbooks/app/provision_one_guest.yml] + T1 --> T2[include_tasks: playbooks/app/provision_one_env.yml] + T2 --> R[include_role: proxmox_vm] + R --> M1[roles/proxmox_vm/tasks/main.yml] + M1 --> L1[roles/proxmox_vm/tasks/lxc.yml] + + T2 --> H[add_host groups] + H --> G1[app_all] + H --> G2[app_${project}_all] + H --> G3[app_${project}_${env}] +``` + +#### `playbooks/app/configure_app.yml` (configure app guests) + +Two phases: +1) **localhost** builds a dynamic inventory from `app_projects` (static IPs) +2) **app_all** (or `app_${project}_all`) configures each host + +```mermaid +flowchart TD + A[play: localhost build inventory] --> H[add_host groups] + H --> G1[app_all / app_${project}_*] + + B[play: app_all configure] --> OS[include_role: base_os] + B --> POTE[include_role: pote (only when app_project == 'pote')] + B --> APP[include_role: app_setup (when app_project != 'pote')] +``` + +#### `playbooks/app/proxmox_info.yml` + +Single local play that queries Proxmox and prints a filtered summary. + +#### `playbooks/app/ssh_client_config.yml` + +Single local play that optionally manages `~/.ssh/config` (gated by `manage_ssh_config`). + +--- + +## Tags map (what each tag hits) + +### Top-level dispatcher tags (`playbooks/site.yml`) + +- **maintenance**: runs `playbooks/maintenance.yml` +- **development**: runs `playbooks/development.yml` +- **tailscale**: runs `playbooks/tailscale.yml` +- **app**: runs `playbooks/app/site.yml` (and therefore app provision + configure) + +### Dev/local role tags (`playbooks/development.yml`, `playbooks/local.yml`) + +These playbooks tag roles directly: +- **maintenance** → `role: maintenance` +- **base** → `role: base` +- **security** → `role: base` + `role: ssh` (both are tagged `security` at role-inclusion) +- **user** → `role: user` +- **ssh** → `role: ssh` +- **shell** → `role: shell` +- **development** / **dev** → `role: development` +- **docker** → `role: docker` +- **monitoring** → `role: monitoring_desktop` +- **datascience** / **conda** / **jupyter** / **r** → `role: datascience` (development playbook only) +- **tailscale** / **vpn** → (currently commented out in dev/local) + +### Workstation + desktop apps tags (`playbooks/workstations.yml`) + +- **apps** / **applications** → `role: applications` (desktop group only) + +### App suite tags + +From `playbooks/app/site.yml` imports: +- **app**: everything in the app suite +- **provision**: `playbooks/app/provision_vms.yml` only +- **configure**: `playbooks/app/configure_app.yml` only + +Standalone app playbook tags (so `--tags ...` works when running them directly): +- `playbooks/app/provision_vms.yml`: **app**, **provision** +- `playbooks/app/configure_app.yml`: **app**, **configure** +- `playbooks/app/proxmox_info.yml`: **app**, **proxmox**, **info** +- `playbooks/app/ssh_client_config.yml`: **app**, **ssh-config** + +### Role-internal tags (task/block level) + +These are tags inside role task files (useful for targeting parts of a role even if the role itself isn’t included with that tag): +- `roles/datascience/tasks/main.yml`: + - **conda** + - **jupyter** + - **r**, **rstats** + +### Makefile tag shortcuts + +Make targets that apply `--tags`: +- `make datascience HOST=...` → `--tags datascience` +- `make security` → `--tags security` +- `make docker` → `--tags docker` +- `make shell` → `--tags shell` +- `make apps` → `--tags apps` +- `make monitoring` → `--tags monitoring` + +--- + +## Tag-filtering gotchas (important) + +- If you run with `--tags X`, **untagged** `pre_tasks`/`tasks`/`post_tasks` in a playbook are skipped. + - Example: `playbooks/maintenance.yml` has untagged `pre_tasks` and `post_tasks`. + `--tags maintenance` runs only the `maintenance` role, not the surrounding reporting steps. + diff --git a/docs/reference/security.md b/docs/reference/security.md new file mode 100644 index 0000000..117153a --- /dev/null +++ b/docs/reference/security.md @@ -0,0 +1,28 @@ +# Security reference + +## Overview + +Security in this repo is implemented via: +- hardened SSH + firewall defaults (`roles/ssh/`) +- baseline system configuration (`roles/base/`) +- monitoring/intrusion prevention on servers (`roles/monitoring_server/`) +- secrets handled via Ansible Vault (`inventories/production/group_vars/all/vault.yml`) + +## Recommended execution + +```bash +# Dry-run first +make check + +# Apply security-tagged tasks +make security +``` + +## Vault + +- Vault guide: `docs/guides/vault.md` + +## Canonical standards + +- `project-docs/standards.md` + diff --git a/inventories/production/host_vars/devGPU.yml b/inventories/production/host_vars/devGPU.yml index 0374445..ce1f58b 100644 --- a/inventories/production/host_vars/devGPU.yml +++ b/inventories/production/host_vars/devGPU.yml @@ -1,3 +1,4 @@ +--- ansible_become_password: root ansible_python_interpreter: /usr/bin/python3 @@ -9,7 +10,7 @@ shell_additional_users: - devuser01 - devuser02 - dev - + # Data Science configuration (datascience role) install_conda: true conda_install_path: "/root/anaconda3" @@ -21,10 +22,4 @@ 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_docs: true # Markdown/documentation +# IDE/editor tooling is intentionally not managed by Ansible in this repo. diff --git a/inventories/production/host_vars/git-ci-01.yml b/inventories/production/host_vars/git-ci-01.yml index 5e4549d..6980921 100644 --- a/inventories/production/host_vars/git-ci-01.yml +++ b/inventories/production/host_vars/git-ci-01.yml @@ -1,8 +1,8 @@ +--- # Configure sudo path for git-ci-01 # Sudo may not be in PATH for non-interactive shells ansible_become_exe: /usr/bin/sudo ansible_become_method: sudo # Alternative: if sudo is in a different location, update this -# ansible_become_exe: /usr/local/bin/sudo - +# ansible_become_exe: /usr/local/bin/sudo \ No newline at end of file diff --git a/inventories/production/host_vars/sonarqube-01.yml b/inventories/production/host_vars/sonarqube-01.yml index 1300d54..bf5a130 100644 --- a/inventories/production/host_vars/sonarqube-01.yml +++ b/inventories/production/host_vars/sonarqube-01.yml @@ -7,4 +7,3 @@ ansible_become_method: sudo # Configure shell for ladmin user shell_users: - ladmin - diff --git a/playbooks/app/configure_app.yml b/playbooks/app/configure_app.yml index 8891b58..bbb2576 100644 --- a/playbooks/app/configure_app.yml +++ b/playbooks/app/configure_app.yml @@ -12,6 +12,7 @@ hosts: localhost connection: local gather_facts: false + tags: ['app', 'configure'] vars: selected_projects: >- {{ @@ -61,6 +62,7 @@ }} become: true gather_facts: true + tags: ['app', 'configure'] tasks: - name: Build project/env effective variables @@ -130,5 +132,3 @@ app_env_vars: "{{ env_def.env_vars | default({}) }}" when: app_project != 'pote' - - diff --git a/playbooks/app/provision_one_env.yml b/playbooks/app/provision_one_env.yml index af9c8a2..0e13dc2 100644 --- a/playbooks/app/provision_one_env.yml +++ b/playbooks/app/provision_one_env.yml @@ -232,4 +232,6 @@ ansible_host: "{{ (env_def.ip | string).split('/')[0] }}" ansible_user: root app_project: "{{ project_key }}" - app_env: "{{ env_name }}" \ No newline at end of file + app_env: "{{ env_name }}" + +# EOF diff --git a/playbooks/app/provision_one_guest.yml b/playbooks/app/provision_one_guest.yml index 6a2abec..bdf9fdf 100644 --- a/playbooks/app/provision_one_guest.yml +++ b/playbooks/app/provision_one_guest.yml @@ -18,4 +18,6 @@ ansible.builtin.include_tasks: provision_one_env.yml loop: "{{ project_def.envs | dict2items }}" loop_control: - loop_var: env_item \ No newline at end of file + loop_var: env_item + +# EOF diff --git a/playbooks/app/provision_vms.yml b/playbooks/app/provision_vms.yml index 72cc3db..3b1f6b2 100644 --- a/playbooks/app/provision_vms.yml +++ b/playbooks/app/provision_vms.yml @@ -12,6 +12,7 @@ hosts: localhost connection: local gather_facts: false + tags: ['app', 'provision'] vars: selected_projects: >- {{ @@ -32,5 +33,3 @@ loop: "{{ selected_projects }}" loop_control: loop_var: project_key - - diff --git a/playbooks/app/proxmox_info.yml b/playbooks/app/proxmox_info.yml index e6d6984..fb9bffa 100644 --- a/playbooks/app/proxmox_info.yml +++ b/playbooks/app/proxmox_info.yml @@ -14,6 +14,7 @@ hosts: localhost connection: local gather_facts: false + tags: ['app', 'proxmox', 'info'] vars: selected_projects: >- {{ @@ -91,9 +92,7 @@ msg: | Proxmox: {{ proxmox_host }} (node={{ proxmox_node | default('any') }}, type={{ proxmox_info_type | default(proxmox_info_type_default) }}) Showing: {{ 'ALL guests' if (proxmox_info_all | default(proxmox_info_all_default) | bool) else ('app_projects for ' ~ (selected_projects | join(', '))) }} - + {% for g in (filtered_guests | sort(attribute='vmid')) %} - vmid={{ g.vmid }} type={{ g.id.split('/')[0] if g.id is defined else 'unknown' }} name={{ g.name | default('') }} node={{ g.node | default('') }} status={{ g.status | default('') }} {% endfor %} - - diff --git a/playbooks/app/site.yml b/playbooks/app/site.yml index e98a99e..041003c 100644 --- a/playbooks/app/site.yml +++ b/playbooks/app/site.yml @@ -11,5 +11,3 @@ - name: Configure guests import_playbook: configure_app.yml tags: ['app', 'configure'] - - diff --git a/playbooks/app/ssh_client_config.yml b/playbooks/app/ssh_client_config.yml index e66b1f9..1a75cb4 100644 --- a/playbooks/app/ssh_client_config.yml +++ b/playbooks/app/ssh_client_config.yml @@ -13,6 +13,7 @@ hosts: localhost connection: local gather_facts: false + tags: ['app', 'ssh-config'] vars: manage_ssh_config: "{{ manage_ssh_config | default(false) }}" ssh_config_path: "{{ lookup('ansible.builtin.env', 'HOME') + '/.ssh/config' }}" @@ -47,5 +48,3 @@ - app_projects[item.0] is defined - app_projects[item.0].envs[item.1] is defined - (app_projects[item.0].envs[item.1].ip | default('')) | length > 0 - - diff --git a/playbooks/development.yml b/playbooks/development.yml index 7b07e01..3e7c279 100644 --- a/playbooks/development.yml +++ b/playbooks/development.yml @@ -2,32 +2,18 @@ - name: Configure development environment hosts: dev become: true - strategy: free roles: - {role: maintenance, tags: ['maintenance']} - {role: base, tags: ['base', 'security']} - {role: user, tags: ['user']} - {role: ssh, tags: ['ssh', 'security']} - - {role: shell, tags: ['shell']} + - {role: shell, tags: ['shell'], shell_mode: full, shell_set_default_shell: true} - {role: development, tags: ['development', 'dev']} - {role: datascience, tags: ['datascience', 'conda', 'jupyter', 'r']} - {role: docker, tags: ['docker']} - - {role: applications, tags: ['applications', 'apps']} # - {role: tailscale, tags: ['tailscale', 'vpn']} - - {role: monitoring, tags: ['monitoring']} - - pre_tasks: - - name: Update apt cache - ansible.builtin.apt: - update_cache: true - ignore_errors: true - register: apt_update_result - - - name: Display apt update status - ansible.builtin.debug: - msg: "Apt cache update: {{ 'Success' if apt_update_result is succeeded else 'Failed - continuing anyway' }}" - when: ansible_debug_output | default(false) | bool + - {role: monitoring_desktop, tags: ['monitoring']} tasks: # Additional tasks can be added here if needed diff --git a/playbooks/local.yml b/playbooks/local.yml index ca60722..b09d7c7 100644 --- a/playbooks/local.yml +++ b/playbooks/local.yml @@ -12,14 +12,8 @@ - {role: shell, tags: ['shell']} - {role: development, tags: ['development', 'dev']} - {role: docker, tags: ['docker']} - - {role: applications, tags: ['applications', 'apps']} # - {role: tailscale, tags: ['tailscale', 'vpn']} - - {role: monitoring, tags: ['monitoring']} - - pre_tasks: - - name: Update apt cache - ansible.builtin.apt: - update_cache: true + - {role: monitoring_desktop, tags: ['monitoring']} tasks: - name: Display completion message diff --git a/playbooks/maintenance.yml b/playbooks/maintenance.yml index 62fd314..37dda31 100644 --- a/playbooks/maintenance.yml +++ b/playbooks/maintenance.yml @@ -22,12 +22,6 @@ Group: {{ group_names | join(', ') }} Skip reboot: {{ skip_reboot | default(false) | bool }} - - name: Update apt cache - ansible.builtin.apt: - update_cache: true - cache_valid_time: 3600 - when: maintenance_update_cache | bool - roles: - {role: maintenance, tags: ['maintenance']} diff --git a/playbooks/servers.yml b/playbooks/servers.yml new file mode 100644 index 0000000..66286c8 --- /dev/null +++ b/playbooks/servers.yml @@ -0,0 +1,27 @@ +--- +# Playbook: servers.yml +# Purpose: Baseline configuration for servers (no desktop apps, no IDE install) +# Targets: services + qa + ansible + tailscale (override with -e target_group=...) +# Tags: maintenance, base, security, user, ssh, shell, docker, monitoring +# Usage: +# ansible-playbook -i inventories/production playbooks/servers.yml +# ansible-playbook -i inventories/production playbooks/servers.yml -e target_group=services +# ansible-playbook -i inventories/production playbooks/servers.yml --limit jellyfin + +- name: Configure servers baseline + hosts: "{{ target_group | default('services:qa:ansible:tailscale') }}" + become: true + + roles: + - {role: maintenance, tags: ['maintenance']} + - {role: base, tags: ['base', 'security']} + - {role: user, tags: ['user']} + - {role: ssh, tags: ['ssh', 'security']} + - {role: shell, tags: ['shell']} + - {role: docker, tags: ['docker']} + - {role: monitoring_server, tags: ['monitoring']} + + tasks: + - name: Display completion message + ansible.builtin.debug: + msg: "Server baseline configuration completed successfully!" diff --git a/playbooks/shell.yml b/playbooks/shell.yml index 1cfb4d2..31da933 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) +# Purpose: Configure shell environment (minimal zsh + managed aliases) # Targets: all hosts # Tags: shell # Usage: make shell-all @@ -8,25 +8,12 @@ - name: Configure shell environment hosts: all become: true - strategy: free ignore_errors: true ignore_unreachable: true roles: - {role: shell, tags: ['shell']} - pre_tasks: - - name: Update apt cache - ansible.builtin.apt: - update_cache: true - ignore_errors: true - register: apt_update_result - - - name: Display apt update status - ansible.builtin.debug: - msg: "Apt cache update: {{ 'Success' if apt_update_result is succeeded else 'Failed - continuing anyway' }}" - when: ansible_debug_output | default(false) | bool - tasks: - name: Display completion message ansible.builtin.debug: diff --git a/playbooks/tailscale.yml b/playbooks/tailscale.yml index 8d46507..99be28f 100644 --- a/playbooks/tailscale.yml +++ b/playbooks/tailscale.yml @@ -9,12 +9,6 @@ # Override here if needed or pass via: --extra-vars "tailscale_auth_key=your_key" tailscale_auth_key: "{{ vault_tailscale_auth_key | default('') }}" - pre_tasks: - - name: Update package cache (Debian/Ubuntu) - ansible.builtin.apt: - update_cache: true - when: ansible_os_family == "Debian" - roles: - {role: tailscale, tags: ['tailscale', 'vpn']} diff --git a/playbooks/workstations.yml b/playbooks/workstations.yml new file mode 100644 index 0000000..2951e9a --- /dev/null +++ b/playbooks/workstations.yml @@ -0,0 +1,42 @@ +--- +# Playbook: workstations.yml +# Purpose: Workstation baseline (dev boxes + desktops). Desktop apps are applied only to the `desktop` group. +# Targets: dev + desktop + local (override with -e target_group=...) +# Tags: maintenance, base, security, user, ssh, shell, development, dev, datascience, docker, monitoring, apps +# +# Usage: +# ansible-playbook -i inventories/production playbooks/workstations.yml +# ansible-playbook -i inventories/production playbooks/workstations.yml -e target_group=dev +# ansible-playbook -i inventories/production playbooks/workstations.yml --tags apps + +- name: Configure workstation baseline + hosts: "{{ target_group | default('dev:desktop:local') }}" + become: true + + roles: + - {role: maintenance, tags: ['maintenance']} + - {role: base, tags: ['base', 'security']} + - {role: user, tags: ['user']} + - {role: ssh, tags: ['ssh', 'security']} + - {role: shell, tags: ['shell'], shell_mode: full, shell_set_default_shell: true} + - {role: development, tags: ['development', 'dev']} + - {role: datascience, tags: ['datascience', 'conda', 'jupyter', 'r']} + - {role: docker, tags: ['docker']} + - {role: monitoring_desktop, tags: ['monitoring']} + + tasks: + - name: Display completion message + ansible.builtin.debug: + msg: "Workstation baseline configuration completed successfully!" + +- name: Install desktop applications (desktop group only) + hosts: desktop + become: true + + roles: + - {role: applications, tags: ['applications', 'apps']} + + tasks: + - name: Display completion message + ansible.builtin.debug: + msg: "Desktop applications installed successfully!" diff --git a/project-docs/architecture.md b/project-docs/architecture.md new file mode 100644 index 0000000..c0c9402 --- /dev/null +++ b/project-docs/architecture.md @@ -0,0 +1,63 @@ +## Architecture + +### High-level map (modules and relationships) + +- **Inventory**: `inventories/production/` + - `hosts`: groups like `dev`, `desktop`, `services`, `qa`, `ansible`, `tailscale`, `local` + - `group_vars/all/main.yml`: shared configuration (including `app_projects`) + - `group_vars/all/vault.yml`: encrypted secrets (Ansible Vault) + - `host_vars/*`: per-host overrides (some encrypted) + +- **Playbooks**: `playbooks/` + - `playbooks/site.yml`: dispatcher (imports other playbooks) + - `playbooks/servers.yml`: baseline for servers (`services:qa:ansible:tailscale`) + - `playbooks/workstations.yml`: baseline for `dev:desktop:local` + desktop apps for `desktop` group only + - `playbooks/development.yml`: dev machines baseline (no desktop apps) + - `playbooks/local.yml`: localhost baseline (no desktop apps) + - `playbooks/app/*`: Proxmox app-project provisioning/configuration suite + +- **Roles**: `roles/*` + - Baseline/security: `base`, `user`, `ssh` + - Dev tooling: `development`, `datascience`, `docker` + - Shell: `shell` (minimal aliases-only) + - Monitoring split: + - `monitoring_server` (fail2ban + sysstat) + - `monitoring_desktop` (desktop-oriented monitoring tooling) + - Proxmox guests: `proxmox_vm` + - App guest configuration: `base_os`, `app_setup`, `pote` + +### Proxmox “app projects” flow (data model + execution) + +- **Data model**: `app_projects` in `inventories/production/group_vars/all/main.yml` + - Defines projects and per-env (`dev/qa/prod`) guest parameters (ip, branch, vmid, etc.) + +- **Provision**: `playbooks/app/provision_vms.yml` + - Loops `app_projects` → envs → calls `role: proxmox_vm` to create LXC guests + - Adds dynamic inventory groups: + - `app_all` + - `app__all` + - `app__` + +- **Configure**: `playbooks/app/configure_app.yml` + - Builds a dynamic inventory from `app_projects` (so it can run standalone) + - Applies: + - `role: base_os` (baseline OS for app guests) + - `role: app_setup` (deploy + systemd) or `role: pote` for the POTE project + +### Boundaries + +- **Inventory/vars** define desired state and credentials. +- **Playbooks** define “what path to run” (role ordering, target groups, tags). +- **Roles** implement actual host configuration (idempotent tasks, handlers). + +### External dependencies + +- **Ansible collections**: `collections/requirements.yml` +- **Ansible Vault**: `inventories/production/group_vars/all/vault.yml` +- **Proxmox API**: used by `community.proxmox.*` modules in provisioning + +### References + +- Playbook execution graphs and tags: `docs/reference/playbooks-and-tags.md` +- Legacy pointer (do not update): `docs/reference/architecture.md` → `project-docs/architecture.md` + diff --git a/project-docs/decisions.md b/project-docs/decisions.md new file mode 100644 index 0000000..4ba7aee --- /dev/null +++ b/project-docs/decisions.md @@ -0,0 +1,35 @@ +## Decisions (ADR-style) + +### 2025-12-31 — Do not manage IDE/editor installs in Ansible + +- **Context**: IDEs/editors are interactive, fast-moving, and often user-preference-driven. +- **Decision**: Keep editor installation (Cursor, VS Code, etc.) out of Ansible roles/playbooks. +- **Consequences**: + - Faster, more stable provisioning runs + - Less drift caused by UI tooling changes + - Editor setup is handled separately (manual or via dedicated tooling) + +### 2025-12-31 — Split monitoring into server vs workstation roles + +- **Context**: Servers and workstations have different needs (e.g., fail2ban/sysstat are server-centric; wireshark-common is workstation-centric). +- **Decision**: Create `monitoring_server` and `monitoring_desktop` roles and wire them into `servers.yml` / workstation playbooks. +- **Consequences**: + - Smaller install footprint on servers + - Clearer intent and faster runs + +### 2025-12-31 — Desktop applications are installed only on the `desktop` group + +- **Context**: Desktop apps should not be installed on headless servers or dev VMs by default. +- **Decision**: Run `role: applications` only in a `desktop`-scoped play (workstations playbook). +- **Consequences**: + - Reduced unnecessary package installs + - Less attack surface and fewer updates on non-desktop hosts + +### 2025-12-31 — Minimal shell role (aliases-only) + +- **Context**: Oh-my-zsh/theme/plugin cloning is slow and overwriting `.zshrc` is risky. +- **Decision**: `role: shell` now manages a small alias file and ensures it’s sourced; it does not overwrite `.zshrc`. +- **Consequences**: + - Much faster shell configuration + - Safer for servers and multi-user systems + diff --git a/project-docs/index.md b/project-docs/index.md new file mode 100644 index 0000000..2c8133f --- /dev/null +++ b/project-docs/index.md @@ -0,0 +1,33 @@ +## Project docs index + +Last updated: **2025-12-31** + +### Documents + +- **`project-docs/overview.md`** (updated 2025-12-31) + High-level goals, scope, and primary users for this Ansible infrastructure repo. + +- **`project-docs/architecture.md`** (updated 2025-12-31) + Architecture map: inventories, playbooks, roles, and the Proxmox app-project flow. + +- **`project-docs/standards.md`** (updated 2025-12-31) + Conventions for Ansible YAML, role structure, naming, vault usage, and linting. + +- **`project-docs/workflow.md`** (updated 2025-12-31) + How to run common tasks via `Makefile`, how to lint/test, and how to apply safely. + +- **`project-docs/decisions.md`** (updated 2025-12-31) + Short ADR-style notes for important architectural decisions. + +### Related docs (existing) + +- **Playbooks/tags map**: `docs/reference/playbooks-and-tags.md` +- **Applications inventory**: `docs/reference/applications.md` +- **Makefile reference**: `docs/reference/makefile.md` +- **Proxmox app project guides**: + - `docs/guides/app_stack_proxmox.md` + - `docs/guides/app_stack_execution_flow.md` + +Legacy pointers: +- `docs/reference/architecture.md` → `project-docs/architecture.md` + diff --git a/project-docs/overview.md b/project-docs/overview.md new file mode 100644 index 0000000..ee00f0e --- /dev/null +++ b/project-docs/overview.md @@ -0,0 +1,27 @@ +## Overview + +This repository manages infrastructure automation using **Ansible** for: +- Development machines (`dev`) +- Desktop machines (`desktop`) +- Service hosts (`services`, `qa`, `ansible`, `tailscale`) +- Proxmox-managed guests for “app projects” (LXC-first, with a KVM path) + +Primary entrypoint is the **Makefile** (`Makefile`) and playbooks under `playbooks/`. + +### Goals + +- **Predictable, repeatable provisioning** of hosts and Proxmox guests +- **Safe defaults**: avoid destructive automation; prefer guardrails and idempotency +- **Clear separation** between server vs workstation responsibilities +- **Secrets handled via Ansible Vault** (never commit plaintext credentials) + +### Non-goals + +- Automated decommission/destroy playbooks for infrastructure or guests +- Managing interactive IDE/editor installs (kept out of Ansible by design) + +### Target users + +- You (and collaborators) operating a small homelab / Proxmox environment +- Contributors extending roles/playbooks in a consistent style + diff --git a/project-docs/standards.md b/project-docs/standards.md new file mode 100644 index 0000000..790e15f --- /dev/null +++ b/project-docs/standards.md @@ -0,0 +1,49 @@ +## Standards + +### Ansible + YAML conventions + +- **Indentation**: 2 spaces (no tabs) +- **Task naming**: every task should include a clear `name:` +- **Play-level privilege**: prefer `become: true` at play level when most tasks need sudo +- **Modules**: + - Prefer native modules over `shell`/`command` + - Use **fully qualified collection names** (FQCN), e.g. `ansible.builtin.apt`, `community.general.ufw` +- **Handlers**: use handlers for restarts/reloads +- **Idempotency**: + - If `shell`/`command` is unavoidable, set `changed_when:` / `creates:` / `removes:` appropriately + +### Role structure + +Roles should follow: + +``` +roles// +├── defaults/main.yml +├── handlers/main.yml +├── tasks/main.yml +├── templates/ +├── files/ +└── README.md +``` + +### Variable naming + +- **snake_case** everywhere +- Vault-backed variables are prefixed with **`vault_`** + +### Secrets / Vault + +- Never commit plaintext secrets. +- Use Ansible Vault for credentials: + - `inventories/production/group_vars/all/vault.yml` (encrypted) +- Local vault password file is expected at `~/.ansible-vault-pass`. + +### Makefile-first workflow + +- Prefer `make ...` targets over direct `ansible-playbook` commands for consistency. + +### Linting + +- `ansible-lint` is the primary linter. +- `.ansible-lint` excludes vault-containing inventory paths to keep linting deterministic without vault secrets. + diff --git a/project-docs/workflow.md b/project-docs/workflow.md new file mode 100644 index 0000000..c605525 --- /dev/null +++ b/project-docs/workflow.md @@ -0,0 +1,86 @@ +## Workflow + +### Setup + +- Install dependencies (Python requirements, Node deps for docs, Ansible collections): + +```bash +make bootstrap +``` + +- Edit vault secrets: + +```bash +make edit-group-vault +``` + +### Validate (safe, local) + +- Syntax checks: + +```bash +make test-syntax +``` + +- Lint: + +```bash +make lint +``` + +### Common apply flows + +- **Servers baseline** (services + qa + ansible + tailscale): + +```bash +make servers +make servers GROUP=services +make servers HOST=jellyfin +``` + +- **Workstations baseline** (dev + desktop + local; desktop apps only on `desktop` group): + +```bash +make workstations +make workstations GROUP=dev +make apps +``` + +### Proxmox app projects + +End-to-end: + +```bash +make app PROJECT=projectA +``` + +Provision only / configure only: + +```bash +make app-provision PROJECT=projectA +make app-configure PROJECT=projectA +``` + +Inspect Proxmox guests: + +```bash +make proxmox-info PROJECT=projectA +make proxmox-info ALL=true +make proxmox-info TYPE=lxc +``` + +### Safety checks + +- Prefer `--check --diff` first: + +```bash +make check +``` + +### Debugging + +```bash +make debug +make verbose +``` + diff --git a/provision_vms.yml b/provision_vms.yml index 588857a..3d1c7af 100644 --- a/provision_vms.yml +++ b/provision_vms.yml @@ -5,5 +5,3 @@ - name: Provision app project guests import_playbook: playbooks/app/provision_vms.yml - - diff --git a/roles/app_setup/defaults/main.yml b/roles/app_setup/defaults/main.yml index 592abda..89b680b 100644 --- a/roles/app_setup/defaults/main.yml +++ b/roles/app_setup/defaults/main.yml @@ -37,4 +37,3 @@ app_frontend_start_cmd: "npm start" # Arbitrary environment variables for the env file app_env_vars: {} - diff --git a/roles/app_setup/handlers/main.yml b/roles/app_setup/handlers/main.yml index 53c971c..88b5b4a 100644 --- a/roles/app_setup/handlers/main.yml +++ b/roles/app_setup/handlers/main.yml @@ -4,5 +4,3 @@ - name: Reload systemd ansible.builtin.systemd: daemon_reload: true - - diff --git a/roles/app_setup/tasks/main.yml b/roles/app_setup/tasks/main.yml index 3485d38..2ed6eff 100644 --- a/roles/app_setup/tasks/main.yml +++ b/roles/app_setup/tasks/main.yml @@ -80,5 +80,3 @@ enabled: true state: started when: app_enable_frontend | bool - - diff --git a/roles/applications/README.md b/roles/applications/README.md index 2f096f3..c99f474 100644 --- a/roles/applications/README.md +++ b/roles/applications/README.md @@ -1,7 +1,7 @@ # Role: applications ## Description -Installs desktop applications for development and productivity including browsers, office suites, and utilities. +Installs a small set of desktop GUI applications (desktop group only via `playbooks/workstations.yml`). ## Requirements - Ansible 2.9+ @@ -9,8 +9,7 @@ Installs desktop applications for development and productivity including browser - Internet access for package downloads ## Installed Applications -- **Brave Browser**: Privacy-focused web browser -- **LibreOffice**: Complete office suite +- **CopyQ**: Clipboard manager (history, search, scripting) - **Evince**: PDF document viewer - **Redshift**: Blue light filter for eye comfort @@ -18,10 +17,7 @@ Installs desktop applications for development and productivity including browser | Variable | Default | Description | |----------|---------|-------------| -| `applications_install_brave` | `true` | Install Brave browser | -| `applications_install_libreoffice` | `true` | Install LibreOffice suite | -| `applications_install_evince` | `true` | Install PDF viewer | -| `applications_install_redshift` | `true` | Install blue light filter | +| `applications_desktop_packages` | `['copyq','evince','redshift']` | Desktop packages to install | ## Dependencies - `base` role (for package management) @@ -31,16 +27,13 @@ Installs desktop applications for development and productivity including browser ```yaml - hosts: desktop roles: - - { role: applications, applications_install_brave: false } + - role: applications ``` ## Tags - `applications`: All application installations - `apps`: Alias for applications -- `browser`: Browser installation only -- `office`: Office suite installation only ## Notes -- Adds external repositories for Brave browser - Requires desktop environment for GUI applications - Applications are installed system-wide \ No newline at end of file diff --git a/roles/applications/defaults/main.yml b/roles/applications/defaults/main.yml index ed97d53..4e3948e 100644 --- a/roles/applications/defaults/main.yml +++ b/roles/applications/defaults/main.yml @@ -1 +1,7 @@ --- + +# Desktop GUI applications to install (desktop group only via playbooks/workstations.yml) +applications_desktop_packages: + - copyq + - evince + - redshift diff --git a/roles/applications/tasks/main.yml b/roles/applications/tasks/main.yml index 46a0776..8254e90 100644 --- a/roles/applications/tasks/main.yml +++ b/roles/applications/tasks/main.yml @@ -3,109 +3,25 @@ ansible.builtin.package_facts: manager: apt -- name: Check if Brave browser is installed - ansible.builtin.command: brave-browser --version - register: applications_brave_check - ignore_errors: true - changed_when: false - failed_when: false - no_log: true - - name: Set installation conditions ansible.builtin.set_fact: - applications_desktop_apps_needed: "{{ ['redshift', 'libreoffice', 'evince'] | difference(ansible_facts.packages.keys()) | length > 0 }}" - applications_brave_needs_install: "{{ applications_brave_check.rc != 0 or 'brave-browser' not in ansible_facts.packages }}" - -- name: Check if Brave GPG key exists and is correct - ansible.builtin.shell: | - if [ -f /usr/share/keyrings/brave-browser-archive-keyring.gpg ]; then - if file /usr/share/keyrings/brave-browser-archive-keyring.gpg | grep -q "PGP"; then - echo "correct_key" - else - echo "wrong_key" - fi - else - echo "not_exists" - fi - register: brave_key_check - failed_when: false - when: applications_brave_needs_install - -- name: Check if Brave repository exists and is correct - ansible.builtin.shell: | - if [ -f /etc/apt/sources.list.d/brave-browser.list ]; then - if grep -q "deb \[signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg\]" /etc/apt/sources.list.d/brave-browser.list; then - echo "correct_config" - else - echo "wrong_config" - fi - else - echo "not_exists" - fi - register: brave_repo_check - failed_when: false - when: applications_brave_needs_install - -- name: Clean up duplicate Brave repository files - ansible.builtin.file: - path: "{{ item }}" - state: absent - loop: - - /etc/apt/sources.list.d/brave-browser.list - - /etc/apt/sources.list.d/brave-browser-release.sources - become: true - failed_when: false - when: - - applications_brave_needs_install - - brave_repo_check.stdout == "wrong_config" - -- name: Remove incorrect Brave GPG key - ansible.builtin.file: - path: /usr/share/keyrings/brave-browser-archive-keyring.gpg - state: absent - become: true - when: - - applications_brave_needs_install - - brave_key_check.stdout == "wrong_key" + applications_desktop_apps_needed: >- + {{ + (applications_desktop_packages | default([])) + | difference(ansible_facts.packages.keys()) + | length > 0 + }} - name: Install desktop applications ansible.builtin.apt: - name: - - redshift - - libreoffice - - evince + name: "{{ applications_desktop_packages }}" state: present when: applications_desktop_apps_needed -- name: Brave browser installation - when: applications_brave_needs_install - block: - - name: Download Brave APT key only if needed - ansible.builtin.get_url: - url: https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg - dest: /usr/share/keyrings/brave-browser-archive-keyring.gpg - mode: '0644' - when: brave_key_check.stdout in ["not_exists", "wrong_key"] - - - name: Add Brave repository only if needed - ansible.builtin.apt_repository: - repo: "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main" - filename: brave-browser - state: present - when: brave_repo_check.stdout in ["not_exists", "wrong_config"] - - - name: Install Brave browser - ansible.builtin.apt: - name: brave-browser - state: present - - name: Display application status ansible.builtin.debug: msg: - "Desktop apps needed: {{ applications_desktop_apps_needed }}" - - "Brave needed: {{ applications_brave_needs_install }}" - "Redshift: {{ 'Installed' if 'redshift' in ansible_facts.packages else 'Missing' }}" - - "LibreOffice: {{ 'Installed' if 'libreoffice' in ansible_facts.packages else 'Missing' }}" - "Evince: {{ 'Installed' if 'evince' in ansible_facts.packages else 'Missing' }}" - - "Brave: {{ applications_brave_check.stdout if applications_brave_check.rc == 0 else 'Not installed' }}" - when: ansible_debug_output | default(false) | bool \ No newline at end of file + when: ansible_debug_output | default(false) | bool diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml index 57f34bc..d865d20 100644 --- a/roles/base/tasks/main.yml +++ b/roles/base/tasks/main.yml @@ -1,4 +1,10 @@ --- +- name: Update apt cache (shared baseline) + ansible.builtin.apt: + update_cache: true + cache_valid_time: "{{ apt_cache_valid_time | default(3600) }}" + when: ansible_os_family == "Debian" + - name: Ensure Ansible remote_tmp directory exists with correct permissions ansible.builtin.file: path: /root/.ansible/tmp diff --git a/roles/base_os/defaults/main.yml b/roles/base_os/defaults/main.yml index a6dce34..fbb4cfb 100644 --- a/roles/base_os/defaults/main.yml +++ b/roles/base_os/defaults/main.yml @@ -29,4 +29,3 @@ base_os_user_ssh_public_key: "{{ appuser_ssh_public_key | default('') }}" # If true, create passwordless sudo for base_os_user. base_os_passwordless_sudo: true - diff --git a/roles/base_os/handlers/main.yml b/roles/base_os/handlers/main.yml index a39d467..346e7ca 100644 --- a/roles/base_os/handlers/main.yml +++ b/roles/base_os/handlers/main.yml @@ -4,5 +4,3 @@ - name: Reload ufw ansible.builtin.command: ufw reload changed_when: false - - diff --git a/roles/base_os/tasks/main.yml b/roles/base_os/tasks/main.yml index 49ad453..95fa7b3 100644 --- a/roles/base_os/tasks/main.yml +++ b/roles/base_os/tasks/main.yml @@ -38,28 +38,26 @@ when: base_os_passwordless_sudo | bool - name: Ensure UFW allows SSH - ansible.builtin.ufw: + community.general.ufw: rule: allow port: "{{ base_os_allow_ssh_port }}" proto: tcp - name: Ensure UFW allows backend port - ansible.builtin.ufw: + community.general.ufw: rule: allow port: "{{ base_os_backend_port }}" proto: tcp when: base_os_enable_backend | bool - name: Ensure UFW allows frontend port - ansible.builtin.ufw: + community.general.ufw: rule: allow port: "{{ base_os_frontend_port }}" proto: tcp when: base_os_enable_frontend | bool - name: Enable UFW (deny incoming by default) - ansible.builtin.ufw: + community.general.ufw: state: enabled policy: deny - - diff --git a/roles/datascience/defaults/main.yml b/roles/datascience/defaults/main.yml index 6f49a3d..a2d8d18 100644 --- a/roles/datascience/defaults/main.yml +++ b/roles/datascience/defaults/main.yml @@ -17,4 +17,3 @@ r_packages: - r-base - r-base-dev - r-recommended - diff --git a/roles/datascience/handlers/main.yml b/roles/datascience/handlers/main.yml index 1c317d3..31a39e3 100644 --- a/roles/datascience/handlers/main.yml +++ b/roles/datascience/handlers/main.yml @@ -5,4 +5,3 @@ state: restarted daemon_reload: true become: true - diff --git a/roles/datascience/meta/main.yml b/roles/datascience/meta/main.yml index 337e929..471eb65 100644 --- a/roles/datascience/meta/main.yml +++ b/roles/datascience/meta/main.yml @@ -1,4 +1,3 @@ --- dependencies: - role: base - diff --git a/roles/datascience/tasks/main.yml b/roles/datascience/tasks/main.yml index faa73cc..7d73f07 100644 --- a/roles/datascience/tasks/main.yml +++ b/roles/datascience/tasks/main.yml @@ -200,4 +200,3 @@ - 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 447beca..bed3c76 100644 --- a/roles/development/README.md +++ b/roles/development/README.md @@ -23,42 +23,12 @@ Installs core development tools and utilities for software development. This rol - **npm**: Node package manager (included with Node.js) - Configured from official NodeSource repository -### Code Editors -- **Cursor IDE**: AI-powered code editor (AppImage) - - Installed to `/usr/local/bin/cursor` - - Latest stable version from cursor.com - ## Variables ### Core Settings | Variable | Default | Description | |----------|---------|-------------| -| `install_cursor` | `true` | Install Cursor IDE | -| `install_cursor_extensions` | `false` | Install Cursor extensions | - -### Extension Groups -Enable specific extension groups based on your development needs: - -| 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 | - -### 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 +| `development_packages` | See defaults | Base packages installed by the role | ## Dependencies - `base` role (for core utilities) @@ -72,61 +42,17 @@ When `install_cursor_extensions: true`, these are always installed: - role: development ``` -### Python Data Science Machine +### Customize packages ```yaml -- hosts: datascience +- hosts: developers 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 + development_packages: + - git + - build-essential + - python3 + - python3-pip ``` ## Usage @@ -141,7 +67,6 @@ ansible-playbook playbooks/development.yml --limit dev01 --tags development ## Tags - `development`, `dev`: All development tasks -- `cursor`, `ide`: Cursor IDE installation only ## Post-Installation @@ -151,7 +76,6 @@ git --version node --version npm --version python3 --version -cursor --version ``` ### Node.js Usage @@ -163,28 +87,17 @@ npm install -g 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 +- **Total**: ~3-5 minutes ### Disk Space - **Node.js + npm**: ~100MB -- **Cursor IDE**: ~200MB - **Build tools**: ~50MB -- **Total**: ~350MB +- **Total**: ~150MB ## Integration @@ -217,17 +130,9 @@ 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) @@ -239,11 +144,10 @@ alias cursor="cursor --no-sandbox --disable-gpu-sandbox..." | Git | ✅ | - | | Node.js | ✅ | - | | Build Tools | ✅ | - | -| Cursor IDE | ✅ | - | | Anaconda | ❌ | ✅ | | Jupyter | ❌ | ✅ | | R Language | ❌ | ✅ | | Install Time | ~10 min | ~30-60 min | -| Disk Space | ~350MB | ~3GB | +| Disk Space | ~150MB | ~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 9aa4db1..e90f567 100644 --- a/roles/development/defaults/main.yml +++ b/roles/development/defaults/main.yml @@ -1,87 +1,9 @@ --- -# Development role defaults +# Development role defaults (IDEs intentionally not managed here). -# 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 +# Base packages for a lightweight dev foundation. +development_packages: + - git + - build-essential + - python3 + - python3-pip diff --git a/roles/development/tasks/main.yml b/roles/development/tasks/main.yml index 64102aa..153a055 100644 --- a/roles/development/tasks/main.yml +++ b/roles/development/tasks/main.yml @@ -1,13 +1,7 @@ --- - name: Install basic development packages ansible.builtin.apt: - name: - # Development tools - - git - # Build tools - - build-essential - - python3 - - python3-pip + name: "{{ development_packages }}" state: present become: true @@ -17,34 +11,41 @@ failed_when: false changed_when: false -- name: Check if NodeSource repository exists and is correct - ansible.builtin.shell: | - if [ -f /etc/apt/sources.list.d/nodesource.list ]; then - if grep -q "deb \[signed-by=/etc/apt/keyrings/nodesource.gpg\] https://deb.nodesource.com/node_22.x nodistro main" /etc/apt/sources.list.d/nodesource.list; then - echo "correct_config" - else - echo "wrong_config" - fi - else - echo "not_exists" - fi - register: nodesource_repo_check - failed_when: false +- name: Check NodeSource repository file presence + ansible.builtin.stat: + path: /etc/apt/sources.list.d/nodesource.list + register: nodesource_list_stat 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: | - if [ -f /etc/apt/keyrings/nodesource.gpg ]; then - if file /etc/apt/keyrings/nodesource.gpg | grep -q "PGP"; then - echo "correct_key" - else - echo "wrong_key" - fi - else - echo "not_exists" - fi - register: nodesource_key_check - failed_when: false +- name: Read NodeSource repository file + ansible.builtin.slurp: + src: /etc/apt/sources.list.d/nodesource.list + register: nodesource_list_slurp + when: + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') + - nodesource_list_stat.stat.exists | default(false) + +- name: Set NodeSource repository state + ansible.builtin.set_fact: + nodesource_repo_state: >- + {{ + 'not_exists' + if not (nodesource_list_stat.stat.exists | default(false)) + else ( + 'correct_config' + if ( + (nodesource_list_slurp.content | b64decode) + is search('^deb \\[signed-by=/etc/apt/keyrings/nodesource\\.gpg\\] https://deb\\.nodesource\\.com/node_22\\.x nodistro main', multiline=True) + ) + else 'wrong_config' + ) + }} + when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') + +- name: Check NodeSource GPG key presence + ansible.builtin.stat: + path: /etc/apt/keyrings/nodesource.gpg + register: nodesource_key_stat when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - name: Remove incorrect NodeSource repository @@ -54,16 +55,7 @@ become: true when: - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - - nodesource_repo_check.stdout == "wrong_config" - -- name: Remove incorrect NodeSource key - ansible.builtin.file: - path: /etc/apt/keyrings/nodesource.gpg - state: absent - become: true - when: - - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - - nodesource_key_check.stdout == "wrong_key" + - nodesource_repo_state == "wrong_config" - name: Create keyrings directory ansible.builtin.file: @@ -73,7 +65,7 @@ become: true when: - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - - nodesource_key_check.stdout in ["not_exists", "wrong_key"] + - not (nodesource_key_stat.stat.exists | default(false)) - name: Add NodeSource GPG key only if needed ansible.builtin.get_url: @@ -84,7 +76,7 @@ become: true when: - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - - nodesource_key_check.stdout in ["not_exists", "wrong_key"] + - not (nodesource_key_stat.stat.exists | default(false)) - name: Add NodeSource repository only if needed ansible.builtin.apt_repository: @@ -94,7 +86,7 @@ become: true when: - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') - - nodesource_repo_check.stdout in ["not_exists", "wrong_config"] + - nodesource_repo_state in ["not_exists", "wrong_config"] - name: Install Node.js 22 from NodeSource ansible.builtin.apt: @@ -111,92 +103,3 @@ - name: Display Node.js version ansible.builtin.debug: 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/docker/tasks/setup_gpg_key.yml b/roles/docker/tasks/setup_gpg_key.yml index 20a3817..90fb993 100644 --- a/roles/docker/tasks/setup_gpg_key.yml +++ b/roles/docker/tasks/setup_gpg_key.yml @@ -12,6 +12,7 @@ fi register: docker_key_check failed_when: false + changed_when: false - name: Remove incorrect Docker GPG key ansible.builtin.file: @@ -43,4 +44,3 @@ path: /tmp/docker.gpg state: absent when: docker_key_check.stdout in ["not_exists", "wrong_key"] - \ No newline at end of file diff --git a/roles/docker/tasks/setup_repo_debian.yml b/roles/docker/tasks/setup_repo_debian.yml index d83ba97..557314c 100644 --- a/roles/docker/tasks/setup_repo_debian.yml +++ b/roles/docker/tasks/setup_repo_debian.yml @@ -12,6 +12,7 @@ fi register: docker_repo_check failed_when: false + changed_when: false - name: Remove incorrect Docker repository ansible.builtin.file: @@ -26,4 +27,3 @@ state: present update_cache: true when: docker_repo_check.stdout in ["not_exists", "wrong_config"] - \ No newline at end of file diff --git a/roles/docker/tasks/setup_repo_linux_mint.yml b/roles/docker/tasks/setup_repo_linux_mint.yml index f49292c..8f3c1d0 100644 --- a/roles/docker/tasks/setup_repo_linux_mint.yml +++ b/roles/docker/tasks/setup_repo_linux_mint.yml @@ -20,6 +20,7 @@ fi register: docker_repo_check failed_when: false + changed_when: false - name: Remove incorrect Docker repository ansible.builtin.file: @@ -34,4 +35,3 @@ state: present update_cache: true when: docker_repo_check.stdout in ["not_exists", "wrong_config"] - \ No newline at end of file diff --git a/roles/docker/tasks/setup_repo_ubuntu.yml b/roles/docker/tasks/setup_repo_ubuntu.yml index 1ea73dd..87cbb29 100644 --- a/roles/docker/tasks/setup_repo_ubuntu.yml +++ b/roles/docker/tasks/setup_repo_ubuntu.yml @@ -12,6 +12,7 @@ fi register: docker_repo_check failed_when: false + changed_when: false - name: Remove incorrect Docker repository ansible.builtin.file: @@ -26,4 +27,3 @@ state: present update_cache: true when: docker_repo_check.stdout in ["not_exists", "wrong_config"] - \ No newline at end of file diff --git a/roles/monitoring_desktop/defaults/main.yml b/roles/monitoring_desktop/defaults/main.yml new file mode 100644 index 0000000..4af88fd --- /dev/null +++ b/roles/monitoring_desktop/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# Monitoring (desktop/workstation) role defaults +monitoring_desktop_install_btop: true +monitoring_desktop_install_wireshark_common: true +monitoring_desktop_create_scripts: true diff --git a/roles/monitoring_desktop/tasks/main.yml b/roles/monitoring_desktop/tasks/main.yml new file mode 100644 index 0000000..a33628a --- /dev/null +++ b/roles/monitoring_desktop/tasks/main.yml @@ -0,0 +1,131 @@ +--- +- name: Install monitoring packages (desktop/workstation) + ansible.builtin.apt: + name: + # System monitoring + - htop + - iotop + - nethogs + - iftop + - ncdu + - dstat + # Network monitoring + - nmap + - tcpdump + # Performance monitoring + - atop + # Desktop extras + - "{{ 'wireshark-common' if monitoring_desktop_install_wireshark_common | bool else omit }}" + state: present + +- name: Check if btop is available in apt + ansible.builtin.command: apt-cache policy btop + register: monitoring_desktop_btop_apt_check + changed_when: false + failed_when: false + when: monitoring_desktop_install_btop | bool + +- name: Install btop from apt if available (Debian 12+) + ansible.builtin.apt: + name: btop + state: present + update_cache: false + when: + - monitoring_desktop_install_btop | bool + - monitoring_desktop_btop_apt_check.rc == 0 + - "'Candidate:' in monitoring_desktop_btop_apt_check.stdout" + - "'(none)' not in monitoring_desktop_btop_apt_check.stdout" + failed_when: false + +- name: Install btop from binary if apt not available + when: + - monitoring_desktop_install_btop | bool + - monitoring_desktop_btop_apt_check.rc != 0 or "(none)" in monitoring_desktop_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: Create monitoring scripts directory + ansible.builtin.file: + path: /usr/local/bin/monitoring + state: directory + mode: '0755' + when: monitoring_desktop_create_scripts | bool + +- name: Deploy system monitoring script + ansible.builtin.copy: + content: | + #!/bin/bash + # System monitoring dashboard + echo "=== System Overview ===" + echo "Hostname: $(hostname)" + echo "Uptime: $(uptime -p)" + echo "Load: $(uptime | awk -F'load average:' '{print $2}')" + echo "" + echo "=== Memory ===" + free -h + echo "" + echo "=== Disk Usage ===" + df -h / /home 2>/dev/null | grep -v tmpfs + echo "" + echo "=== Top Processes ===" + ps aux --sort=-%cpu | head -6 + echo "" + echo "=== Network Connections ===" + ss -tuln | head -10 + echo "" + if command -v tailscale >/dev/null; then + echo "=== Tailscale Status ===" + tailscale status --peers=false 2>/dev/null || echo "Not connected" + fi + dest: /usr/local/bin/monitoring/sysinfo + mode: '0755' + when: monitoring_desktop_create_scripts | bool + +- name: Deploy network monitoring script + ansible.builtin.copy: + content: | + #!/bin/bash + # Network monitoring script + echo "=== Network Interface Status ===" + ip addr show | grep -E "(inet |state )" | grep -v 127.0.0.1 + echo "" + echo "=== Route Table ===" + ip route show + echo "" + echo "=== DNS Configuration ===" + cat /etc/resolv.conf | grep nameserver + echo "" + echo "=== Open Ports ===" + ss -tuln | grep LISTEN | sort + dest: /usr/local/bin/monitoring/netinfo + mode: '0755' + when: monitoring_desktop_create_scripts | bool diff --git a/roles/monitoring_server/defaults/main.yml b/roles/monitoring_server/defaults/main.yml new file mode 100644 index 0000000..cde13c8 --- /dev/null +++ b/roles/monitoring_server/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# Monitoring (server) role defaults +monitoring_server_install_btop: true +monitoring_server_enable_sysstat: true +monitoring_server_create_scripts: true diff --git a/roles/monitoring_server/handlers/main.yml b/roles/monitoring_server/handlers/main.yml new file mode 100644 index 0000000..4fee7fa --- /dev/null +++ b/roles/monitoring_server/handlers/main.yml @@ -0,0 +1,11 @@ +--- +- name: restart fail2ban + ansible.builtin.systemd: + name: fail2ban + state: restarted + +- name: restart sysstat + ansible.builtin.systemd: + name: sysstat + state: restarted + enabled: true diff --git a/roles/monitoring_server/tasks/main.yml b/roles/monitoring_server/tasks/main.yml new file mode 100644 index 0000000..3ee7362 --- /dev/null +++ b/roles/monitoring_server/tasks/main.yml @@ -0,0 +1,148 @@ +--- +- name: Install monitoring packages (server) + ansible.builtin.apt: + name: + # System monitoring + - htop + - iotop + - nethogs + - iftop + - ncdu + - dstat + # Log monitoring / security + - logwatch + - fail2ban + # Network monitoring + - nmap + - tcpdump + # Performance monitoring + - sysstat + - atop + state: present + +- name: Check if btop is available in apt + ansible.builtin.command: apt-cache policy btop + register: monitoring_server_btop_apt_check + changed_when: false + failed_when: false + when: monitoring_server_install_btop | bool + +- name: Install btop from apt if available (Debian 12+) + ansible.builtin.apt: + name: btop + state: present + update_cache: false + when: + - monitoring_server_install_btop | bool + - monitoring_server_btop_apt_check.rc == 0 + - "'Candidate:' in monitoring_server_btop_apt_check.stdout" + - "'(none)' not in monitoring_server_btop_apt_check.stdout" + failed_when: false + +- name: Install btop from binary if apt not available + when: + - monitoring_server_install_btop | bool + - monitoring_server_btop_apt_check.rc != 0 or "(none)" in monitoring_server_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: + src: jail.local.j2 + dest: /etc/fail2ban/jail.local + mode: '0644' + notify: restart fail2ban + +- name: Enable sysstat data collection + ansible.builtin.lineinfile: + path: /etc/default/sysstat + regexp: '^ENABLED=' + line: 'ENABLED="true"' + notify: restart sysstat + when: monitoring_server_enable_sysstat | bool + +- name: Create monitoring scripts directory + ansible.builtin.file: + path: /usr/local/bin/monitoring + state: directory + mode: '0755' + when: monitoring_server_create_scripts | bool + +- name: Deploy system monitoring script + ansible.builtin.copy: + content: | + #!/bin/bash + # System monitoring dashboard + echo "=== System Overview ===" + echo "Hostname: $(hostname)" + echo "Uptime: $(uptime -p)" + echo "Load: $(uptime | awk -F'load average:' '{print $2}')" + echo "" + echo "=== Memory ===" + free -h + echo "" + echo "=== Disk Usage ===" + df -h / /home 2>/dev/null | grep -v tmpfs + echo "" + echo "=== Top Processes ===" + ps aux --sort=-%cpu | head -6 + echo "" + echo "=== Network Connections ===" + ss -tuln | head -10 + echo "" + if command -v tailscale >/dev/null; then + echo "=== Tailscale Status ===" + tailscale status --peers=false 2>/dev/null || echo "Not connected" + fi + dest: /usr/local/bin/monitoring/sysinfo + mode: '0755' + when: monitoring_server_create_scripts | bool + +- name: Deploy network monitoring script + ansible.builtin.copy: + content: | + #!/bin/bash + # Network monitoring script + echo "=== Network Interface Status ===" + ip addr show | grep -E "(inet |state )" | grep -v 127.0.0.1 + echo "" + echo "=== Route Table ===" + ip route show + echo "" + echo "=== DNS Configuration ===" + cat /etc/resolv.conf | grep nameserver + echo "" + echo "=== Open Ports ===" + ss -tuln | grep LISTEN | sort + dest: /usr/local/bin/monitoring/netinfo + mode: '0755' + when: monitoring_server_create_scripts | bool diff --git a/roles/monitoring_server/templates/jail.local.j2 b/roles/monitoring_server/templates/jail.local.j2 new file mode 100644 index 0000000..36b8dee --- /dev/null +++ b/roles/monitoring_server/templates/jail.local.j2 @@ -0,0 +1,34 @@ +[DEFAULT] +# Ban hosts for 1 hour +bantime = 3600 +# Check for repeated failures for 10 minutes +findtime = 600 +# Allow 3 failures before banning +maxretry = 3 + +# Email notifications (uncomment and configure if needed) +destemail = idobkin@gmail.com +sender = idobkin@gmail.com +action = %(action_mwl)s + +[sshd] +enabled = true +port = ssh +filter = sshd +logpath = /var/log/auth.log +maxretry = 3 + +[apache] +enabled = false +port = http,https +filter = apache-auth +logpath = /var/log/apache2/error.log +maxretry = 3 + +[nginx-http-auth] +enabled = false +port = http,https +filter = nginx-http-auth +logpath = /var/log/nginx/error.log +maxretry = 3 + diff --git a/roles/proxmox_vm/tasks/kvm.yml b/roles/proxmox_vm/tasks/kvm.yml index 5f52f3e..22d522b 100644 --- a/roles/proxmox_vm/tasks/kvm.yml +++ b/roles/proxmox_vm/tasks/kvm.yml @@ -78,5 +78,3 @@ Storage: {{ vm_storage }}:{{ vm_disk_size }} Network: {{ vm_network_bridge }} Status: {{ vm_creation_result.msg | default('Created') }} - - diff --git a/roles/shell/README.md b/roles/shell/README.md index ac66c93..7e0f9dc 100644 --- a/roles/shell/README.md +++ b/roles/shell/README.md @@ -1,7 +1,10 @@ ### Role: shell ## Description -Configures modern shell environment with zsh, Oh My Zsh, Powerlevel10k theme, and useful plugins. Can be configured for multiple users on the same host. +Configures shell in one of two modes: + +- **minimal**: aliases-only (safe for servers; does not overwrite `~/.zshrc`) +- **full**: installs Oh My Zsh + Powerlevel10k + plugins and deploys a managed `~/.zshrc` (intended for developer machines) ## Requirements - Ansible 2.9+ @@ -12,25 +15,23 @@ Configures modern shell environment with zsh, Oh My Zsh, Powerlevel10k theme, an ### 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 +- **oh-my-zsh / powerlevel10k**: only in `shell_mode=full` ### Configuration Files -- `.zshrc`: Custom zsh configuration with conda support -- `.p10k.zsh`: Powerlevel10k theme configuration +- `~/.zsh_aliases_ansible`: Managed aliases file (sourced from `~/.zshrc`) +- `~/.zshrc`: appended in `minimal` mode; fully managed in `full` mode +- `~/.p10k.zsh`: only in `shell_mode=full` ## Variables | Variable | Default | Description | |----------|---------|-------------| | `shell_users` | `[ansible_user]` | List of users to configure zsh for | -| `zsh_plugins` | See defaults/main.yml | List of zsh plugins to install | +| `shell_packages` | `['zsh','tmux','fzf']` | Packages installed by the role | +| `shell_mode` | `minimal` | `minimal` (aliases-only) or `full` (oh-my-zsh + p10k + managed zshrc) | +| `shell_set_default_shell` | `false` | If true, set login shell to `/usr/bin/zsh` | ## Dependencies None @@ -44,6 +45,16 @@ None - role: shell ``` +### Full Zsh for developer machines +```yaml +- hosts: dev + roles: + - role: shell + vars: + shell_mode: full + shell_set_default_shell: true +``` + ### Configure Multiple Users ```yaml - hosts: servers @@ -90,19 +101,14 @@ make dev HOST=devGPU --tags shell ## Post-Installation ### For Each Configured User -The shell configuration is immediately active. Users can: +The aliases are immediately available in new shells. Users can: -1. **Customize Powerlevel10k**: - ```bash - p10k configure - ``` - -2. **Reload Configuration**: +1. **Reload Configuration**: ```bash source ~/.zshrc ``` -3. **View Available Aliases**: +2. **View Available Aliases**: ```bash alias # List all aliases ``` @@ -110,27 +116,12 @@ The shell configuration is immediately active. Users can: ## 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). +The role installs a small, server-safe alias set in `~/.zsh_aliases_ansible`. ## Notes -- 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 +- Existing `.zshrc` files are **not** overwritten - The role skips users that don't exist on the system ## Troubleshooting @@ -143,16 +134,13 @@ 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. +If `shell_mode=full`, ensure the host has outbound internet access to fetch the installer and clone git repos. ### Powerlevel10k Not Loading -Verify the theme is cloned: -```bash -ls ~/.oh-my-zsh/custom/themes/powerlevel10k -``` +Only applies to `shell_mode=full`. Verify `~/.p10k.zsh` exists and the theme repo is present under `~/.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`. +`shell_mode=full` includes a minimal “initialize conda if present” block. Conda installation is still handled by the `datascience` role. ## Integration @@ -174,29 +162,21 @@ roles: ## 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) +- Only an aliases file is managed; no remote scripts/themes are downloaded. - 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 +- Installation time: seconds per user (copy + lineinfile) +- Disk space: negligible ## Customization ### Adding Custom Aliases -Edit `roles/shell/files/.zshrc` and add your aliases. +Edit `roles/shell/files/ansible_aliases.zsh` and re-run the role. ### Adding More Plugins -Update `roles/shell/defaults/main.yml`: -```yaml -zsh_plugins: - - name: "my-plugin" - repo: "https://github.com/user/my-plugin.git" -``` +Out of scope for this role (keep it fast/minimal). ### Custom Theme -Replace Powerlevel10k in tasks if desired, or users can run `p10k configure` to customize. +Out of scope for this role. diff --git a/roles/shell/defaults/main.yml b/roles/shell/defaults/main.yml index 6391145..8a8bc8d 100644 --- a/roles/shell/defaults/main.yml +++ b/roles/shell/defaults/main.yml @@ -15,7 +15,40 @@ shell_users: # - ladmin shell_additional_users: [] -# Zsh plugins to install +# Shell configuration mode: +# - minimal: aliases-only (safe for servers; does not overwrite ~/.zshrc) +# - full: install oh-my-zsh + powerlevel10k + plugins and deploy managed ~/.zshrc +shell_mode: minimal + +# Packages installed for all modes. +shell_packages_common: + - zsh + - tmux + - fzf + +# Extra packages for full mode. +shell_packages_full_extra: + - git + +# Effective package list. +shell_packages: "{{ shell_packages_common + (shell_packages_full_extra if shell_mode == 'full' else []) }}" + +# If true, change users' login shell to zsh. +shell_set_default_shell: false + +# Path (relative to the user's home) for the managed aliases file. +shell_aliases_filename: ".zsh_aliases_ansible" + +# Line added to ~/.zshrc to source the managed aliases file. +shell_zshrc_source_line: '[ -f "$HOME/{{ shell_aliases_filename }}" ] && source "$HOME/{{ shell_aliases_filename }}"' + +# Full mode settings +shell_install_oh_my_zsh: "{{ shell_mode == 'full' }}" +shell_install_powerlevel10k: "{{ shell_mode == 'full' }}" +shell_install_plugins: "{{ shell_mode == 'full' }}" +shell_deploy_managed_zshrc: "{{ shell_mode == 'full' }}" + +# Zsh plugins cloned into oh-my-zsh custom plugins (full mode only). zsh_plugins: - name: "zsh-syntax-highlighting" repo: "https://github.com/zsh-users/zsh-syntax-highlighting.git" diff --git a/roles/shell/files/.p10k.zsh b/roles/shell/files/.p10k.zsh deleted file mode 100644 index 90e212e..0000000 --- a/roles/shell/files/.p10k.zsh +++ /dev/null @@ -1,1719 +0,0 @@ -# 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 deleted file mode 100644 index 1718b50..0000000 --- a/roles/shell/files/.zshrc +++ /dev/null @@ -1,224 +0,0 @@ -typeset -g POWERLEVEL9K_INSTANT_PROMPT=quiet - -# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc. -# Initialization code that may require console input (password prompts, [y/n] -# confirmations, etc.) must go above this block; everything else may go below. -if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then - source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" -fi - -# If you come from bash you might have to change your $PATH. -# export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH - -# Path to your Oh My Zsh installation. -export ZSH="$HOME/.oh-my-zsh" - -# Set name of the theme to load --- if set to "random", it will -# load a random theme each time Oh My Zsh is loaded, in which case, -# to know which specific one was loaded, run: echo $RANDOM_THEME -# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes -ZSH_THEME="powerlevel10k/powerlevel10k" - -# Set list of themes to pick from when loading at random -# Setting this variable when ZSH_THEME=random will cause zsh to load -# a theme from this variable instead of looking in $ZSH/themes/ -# If set to an empty array, this variable will have no effect. -# ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" ) - -# Uncomment the following line to use case-sensitive completion. -# CASE_SENSITIVE="true" - -# Uncomment the following line to use hyphen-insensitive completion. -# Case-sensitive completion must be off. _ and - will be interchangeable. -# HYPHEN_INSENSITIVE="true" - -# Uncomment one of the following lines to change the auto-update behavior -# zstyle ':omz:update' mode disabled # disable automatic updates -# zstyle ':omz:update' mode auto # update automatically without asking -# zstyle ':omz:update' mode reminder # just remind me to update when it's time - -# Uncomment the following line to change how often to auto-update (in days). -# zstyle ':omz:update' frequency 13 - -# Uncomment the following line if pasting URLs and other text is messed up. -# DISABLE_MAGIC_FUNCTIONS="true" - -# Uncomment the following line to disable colors in ls. -# DISABLE_LS_COLORS="true" - -# Uncomment the following line to disable auto-setting terminal title. -# DISABLE_AUTO_TITLE="true" - -# Uncomment the following line to enable command auto-correction. -# ENABLE_CORRECTION="true" - -# Uncomment the following line to display red dots whilst waiting for completion. -# You can also set it to another string to have that shown instead of the default red dots. -# e.g. COMPLETION_WAITING_DOTS="%F{yellow}waiting...%f" -# Caution: this setting can cause issues with multiline prompts in zsh < 5.7.1 (see #5765) -# COMPLETION_WAITING_DOTS="true" - -# Uncomment the following line if you want to disable marking untracked files -# under VCS as dirty. This makes repository status check for large repositories -# much, much faster. -# DISABLE_UNTRACKED_FILES_DIRTY="true" - -# Uncomment the following line if you want to change the command execution time -# stamp shown in the history command output. -# You can set one of the optional three formats: -# "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd" -# or set a custom format using the strftime function format specifications, -# see 'man strftime' for details. -# HIST_STAMPS="mm/dd/yyyy" - -# Would you like to use another custom folder than $ZSH/custom? -# ZSH_CUSTOM=/path/to/new-custom-folder - -# Which plugins would you like to load? -# Standard plugins can be found in $ZSH/plugins/ -# Custom plugins may be added to $ZSH_CUSTOM/plugins/ -# Example format: plugins=(rails git textmate ruby lighthouse) -# Add wisely, as too many plugins slow down shell startup. -plugins=(git sudo z colored-man-pages fzf zsh-syntax-highlighting zsh-autosuggestions web-search copypath) - -source $ZSH/oh-my-zsh.sh - -# User configuration - -# export MANPATH="/usr/local/man:$MANPATH" - -# You may need to manually set your language environment -# export LANG=en_US.UTF-8 - -# Preferred editor for local and remote sessions -# if [[ -n $SSH_CONNECTION ]]; then -# export EDITOR='vim' -# else -# export EDITOR='nvim' -# fi - -# Compilation flags -# export ARCHFLAGS="-arch $(uname -m)" - -# Set personal aliases, overriding those provided by Oh My Zsh libs, -# plugins, and themes. Aliases can be placed here, though Oh My Zsh -# users are encouraged to define aliases within a top-level file in -# the $ZSH_CUSTOM folder, with .zsh extension. Examples: -# - $ZSH_CUSTOM/aliases.zsh -# - $ZSH_CUSTOM/macos.zsh -# For a full list of active aliases, run `alias`. -# -# Example aliases -# alias zshconfig="mate ~/.zshrc" -# alias ohmyzsh="mate ~/.oh-my-zsh" - -# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh. -[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh -[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh - -alias reload="source ~/.zshrc && echo 'ZSH config reloaded from ~/.zshrc'" -alias editrc="nano ~/.zshrc" -alias c="clear" -alias ls="ls --color=auto" - -alias ..="cd .." -alias ...="cd ../.." -alias ....="cd ../../.." -alias cd..="cd .." -alias h="cd ~" -alias dc="cd ~/Documents/code" - -# System information -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" -alias cpu="lscpu" -alias top="btop" -alias mem="free -m" -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" -alias pip="pip3" -alias venv="python3 -m venv" -alias activate="source venv/bin/activate" - -# Docker -alias d="docker" -alias dc="docker-compose" -alias dcu="docker-compose up -d" -alias dcd="docker-compose down" -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'" - -# Package management (Debian/Ubuntu) -alias update="sudo apt update && sudo apt upgrade -y" -alias install="sudo apt install" -alias remove="sudo apt remove" -alias search="apt search" - -# Permissions and ownership -alias chmox="chmod +x" -alias own="sudo chown -R $USER:$USER" - -alias nfresh="rm -rf node_modules/ package-lock.json && npm install" - -# SSH aliases for Ansible hosts -alias ssh-gitea="ssh gitea@10.0.30.169" -alias ssh-portainer="ssh ladmin@10.0.30.69" -alias ssh-homepage="ssh homepage@10.0.30.12" -alias ssh-vaultwarden="ssh root@100.100.19.11" -alias ssh-vaultwarden-fallback="ssh root@10.0.10.142" -alias ssh-dev01="ssh ladmin@10.0.30.105" -alias ssh-bottom="ssh beast@10.0.10.156" -alias ssh-debian="ssh user@10.0.10.206" -alias ssh-devGPU="ssh root@10.0.30.63" -alias ssh-ansible="ssh master@10.0.10.157" -alias ssh-tailscale="ssh ladmin@100.66.218.53" -alias ssh-caddy="ssh root@100.117.106.18" -alias ssh-caddy-fallback="ssh root@10.0.10.50" -alias ssh-jellyfin="ssh root@100.104.109.45" -alias ssh-jellyfin-fallback="ssh root@10.0.10.232" -alias ssh-listmonk="ssh root@100.73.190.115" -alias ssh-listmonk-fallback="ssh root@10.0.10.149" -alias ssh-desktop="ssh beast@100.117.34.106" - -# >>> 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/ansible_aliases.zsh b/roles/shell/files/ansible_aliases.zsh new file mode 100644 index 0000000..ae01d6b --- /dev/null +++ b/roles/shell/files/ansible_aliases.zsh @@ -0,0 +1,38 @@ +# Managed by Ansible (role: shell) +# +# This file is intentionally small and safe for servers. +# It is sourced from ~/.zshrc by the shell role. + +alias reload="source ~/.zshrc && echo 'ZSH config reloaded from ~/.zshrc'" +alias editrc="nano ~/.zshrc" +alias c="clear" +alias ls="ls --color=auto" + +alias ..="cd .." +alias ...="cd ../.." +alias ....="cd ../../.." +alias cd..="cd .." +alias h="cd ~" + +# System information +alias df="df -h" +alias du="du -h" +alias free="free -h" +alias ports="ss -tulpn" +alias myip="curl -s http://ipecho.net/plain; echo" + +# Package management (Debian/Ubuntu) +alias update="sudo apt update && sudo apt upgrade -y" +alias install="sudo apt install" +alias remove="sudo apt remove" +alias search="apt search" + +# Docker (if installed) +alias d="docker" +alias dps="docker ps" +alias di="docker images" + +# Python +alias py="python3" +alias pip="pip3" + diff --git a/roles/shell/files/p10k.zsh b/roles/shell/files/p10k.zsh new file mode 100644 index 0000000..6dc8cb4 --- /dev/null +++ b/roles/shell/files/p10k.zsh @@ -0,0 +1,19 @@ +# Minimal Powerlevel10k config (lean-ish). Users can customize later via `p10k configure`. + +typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=( + dir + vcs + newline + prompt_char +) + +typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=( + status + command_execution_time + background_jobs + time +) + +typeset -g POWERLEVEL9K_MODE='nerdfont-complete' +typeset -g POWERLEVEL9K_PROMPT_ON_NEWLINE=true + diff --git a/roles/shell/files/showapps.sh b/roles/shell/files/showapps.sh index 8a9c39e..f492e2d 100644 --- a/roles/shell/files/showapps.sh +++ b/roles/shell/files/showapps.sh @@ -36,7 +36,7 @@ for tool in conda jupyter R; do done echo -e "\n${YELLOW}Editors:${RESET}" -for tool in cursor code vim nvim nano; do +for tool in vim nvim nano; do command -v $tool >/dev/null 2>&1 && printf " ${GREEN}✓${RESET} %s\n" "$tool" done diff --git a/roles/shell/files/zshrc.full b/roles/shell/files/zshrc.full new file mode 100644 index 0000000..09404b0 --- /dev/null +++ b/roles/shell/files/zshrc.full @@ -0,0 +1,42 @@ +typeset -g POWERLEVEL9K_INSTANT_PROMPT=quiet + +# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc. +if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then + source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" +fi + +# Path to your Oh My Zsh installation. +export ZSH="$HOME/.oh-my-zsh" + +# Theme +ZSH_THEME="powerlevel10k/powerlevel10k" + +# Plugins +plugins=(git sudo z colored-man-pages fzf zsh-syntax-highlighting zsh-autosuggestions web-search copypath) + +source $ZSH/oh-my-zsh.sh + +# Powerlevel10k config +[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh + +# fzf integration (if installed by user) +[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh + +# Source Ansible-managed aliases (safe, small) +[ -f "$HOME/.zsh_aliases_ansible" ] && source "$HOME/.zsh_aliases_ansible" + +# Conda auto-init (only if conda exists) +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 + diff --git a/roles/shell/tasks/configure_user_shell.yml b/roles/shell/tasks/configure_user_shell.yml index 882f1ef..96a51d9 100644 --- a/roles/shell/tasks/configure_user_shell.yml +++ b/roles/shell/tasks/configure_user_shell.yml @@ -22,11 +22,40 @@ - name: Configure shell environment when: user_info.ansible_facts.getent_passwd[current_user] is defined block: - - name: "Set zsh as default shell: {{ current_user }}" + - name: "Optionally set zsh as default shell: {{ current_user }}" ansible.builtin.user: name: "{{ current_user }}" shell: /usr/bin/zsh become: true + when: shell_set_default_shell | bool + + - name: "Install managed zsh aliases file: {{ current_user }}" + ansible.builtin.copy: + src: files/ansible_aliases.zsh + dest: "{{ user_home }}/{{ shell_aliases_filename }}" + owner: "{{ current_user }}" + group: "{{ current_user }}" + mode: "0644" + become: true + + - name: "Ensure ~/.zshrc exists (do not overwrite): {{ current_user }}" + ansible.builtin.file: + path: "{{ user_home }}/.zshrc" + state: touch + owner: "{{ current_user }}" + group: "{{ current_user }}" + mode: "0644" + become: true + when: not (shell_deploy_managed_zshrc | bool) + + - name: "Ensure ~/.zshrc sources managed aliases: {{ current_user }}" + ansible.builtin.lineinfile: + path: "{{ user_home }}/.zshrc" + line: "{{ shell_zshrc_source_line }}" + state: present + insertafter: EOF + become: true + when: not (shell_deploy_managed_zshrc | bool) - name: "Install Oh My Zsh: {{ current_user }}" become: true @@ -34,6 +63,8 @@ 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" + changed_when: false + when: shell_install_oh_my_zsh | bool - name: "Clone Powerlevel10k theme: {{ current_user }}" ansible.builtin.git: @@ -44,6 +75,9 @@ update: false become: true become_user: "{{ current_user }}" + when: + - shell_install_powerlevel10k | bool + - shell_install_oh_my_zsh | bool - name: "Install zsh plugins: {{ current_user }}" ansible.builtin.git: @@ -55,24 +89,33 @@ become: true become_user: "{{ current_user }}" loop: "{{ zsh_plugins }}" + when: + - shell_install_plugins | bool + - shell_install_oh_my_zsh | bool - - name: "Deploy .zshrc: {{ current_user }}" + - name: "Deploy managed .zshrc (full mode): {{ current_user }}" ansible.builtin.copy: - src: files/.zshrc + src: files/zshrc.full dest: "{{ user_home }}/.zshrc" owner: "{{ current_user }}" group: "{{ current_user }}" - mode: '0644' + mode: "0644" + backup: true become: true + when: shell_deploy_managed_zshrc | bool - - name: "Deploy Powerlevel10k configuration: {{ current_user }}" + - name: "Deploy Powerlevel10k config (full mode): {{ current_user }}" ansible.builtin.copy: - src: files/.p10k.zsh + src: files/p10k.zsh dest: "{{ user_home }}/.p10k.zsh" owner: "{{ current_user }}" group: "{{ current_user }}" - mode: '0644' + mode: "0644" + backup: true become: true + when: + - shell_install_powerlevel10k | bool + - shell_deploy_managed_zshrc | bool - name: "Ensure .local/bin directory exists: {{ current_user }}" ansible.builtin.file: @@ -96,9 +139,8 @@ 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 + - "Aliases installed: {{ user_home }}/{{ shell_aliases_filename }}" + - >- + Mode: {{ shell_mode | default('minimal') }} ({{ 'managed ~/.zshrc deployed' if (shell_deploy_managed_zshrc | bool) else 'aliases-only appended to ~/.zshrc' }}) + - "If you want zsh as default login shell, set: shell_set_default_shell=true" + - "==========================================" diff --git a/roles/shell/tasks/main.yml b/roles/shell/tasks/main.yml index 0c94b94..431214a 100644 --- a/roles/shell/tasks/main.yml +++ b/roles/shell/tasks/main.yml @@ -1,11 +1,7 @@ --- - name: Install shell packages ansible.builtin.apt: - name: - - zsh - - tmux - - fzf - - git + name: "{{ shell_packages }}" state: present become: true diff --git a/roles/tailscale/tasks/debian.yml b/roles/tailscale/tasks/debian.yml index 4b51cdf..777677f 100644 --- a/roles/tailscale/tasks/debian.yml +++ b/roles/tailscale/tasks/debian.yml @@ -18,6 +18,7 @@ fi register: tailscale_key_check failed_when: false + changed_when: false when: tailscale_version_check.rc != 0 - name: Check if Tailscale repository exists and is correct @@ -33,6 +34,7 @@ fi register: tailscale_repo_check failed_when: false + changed_when: false when: tailscale_version_check.rc != 0 - name: Remove incorrect Tailscale GPG key diff --git a/scripts/inventory.sh b/scripts/inventory.sh index 31a29b8..6028581 100755 --- a/scripts/inventory.sh +++ b/scripts/inventory.sh @@ -30,7 +30,6 @@ get_version() { 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 @@ -65,16 +64,6 @@ 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) diff --git a/site.yml b/site.yml index 7433e65..7f3e27a 100644 --- a/site.yml +++ b/site.yml @@ -7,5 +7,3 @@ - name: Main site import_playbook: playbooks/site.yml - -