From baf3e3de097571652072db1c123e22a33c890844 Mon Sep 17 00:00:00 2001 From: ilia Date: Wed, 31 Dec 2025 23:13:03 -0500 Subject: [PATCH 01/14] Refactor playbooks: servers/workstations, split monitoring, improve shell --- .ansible-lint | 11 +- .cursor/rules/project-rules.mdc | 33 + .gitea/workflows/ci.yml | 6 +- Makefile | 28 +- README.md | 3 +- configure_app.yml | 2 - docs/guides/custom-roles.md | 28 + docs/guides/monitoring.md | 22 + docs/guides/security.md | 31 + docs/project-docs.md | 13 + docs/reference/applications.md | 45 +- docs/reference/architecture.md | 261 +-- docs/reference/network.md | 21 + docs/reference/playbooks-and-tags.md | 281 +++ docs/reference/security.md | 28 + inventories/production/host_vars/devGPU.yml | 8 +- playbooks/app/configure_app.yml | 4 +- playbooks/app/provision_one_env.yml | 4 +- playbooks/app/provision_one_guest.yml | 4 +- playbooks/app/provision_vms.yml | 3 +- playbooks/app/proxmox_info.yml | 5 +- playbooks/app/site.yml | 2 - playbooks/app/ssh_client_config.yml | 3 +- playbooks/development.yml | 37 +- playbooks/local.yml | 8 +- playbooks/maintenance.yml | 6 - playbooks/servers.yml | 27 + playbooks/shell.yml | 36 +- playbooks/tailscale.yml | 6 - playbooks/workstations.yml | 42 + project-docs/architecture.md | 63 + project-docs/decisions.md | 35 + project-docs/index.md | 33 + project-docs/overview.md | 27 + project-docs/standards.md | 49 + project-docs/workflow.md | 86 + provision_vms.yml | 2 - roles/app_setup/defaults/main.yml | 1 - roles/app_setup/handlers/main.yml | 2 - roles/app_setup/tasks/main.yml | 2 - roles/applications/README.md | 15 +- roles/applications/defaults/main.yml | 6 + roles/applications/tasks/main.yml | 123 +- roles/base/tasks/main.yml | 6 + roles/base_os/defaults/main.yml | 1 - roles/base_os/handlers/main.yml | 2 - roles/base_os/tasks/main.yml | 10 +- roles/development/README.md | 118 +- roles/development/defaults/main.yml | 92 +- roles/development/tasks/main.yml | 220 +-- roles/monitoring_desktop/defaults/main.yml | 5 + roles/monitoring_desktop/tasks/main.yml | 131 ++ roles/monitoring_server/defaults/main.yml | 5 + roles/monitoring_server/handlers/main.yml | 11 + roles/monitoring_server/tasks/main.yml | 148 ++ .../monitoring_server/templates/jail.local.j2 | 34 + roles/proxmox_vm/tasks/kvm.yml | 2 - roles/shell/README.md | 90 +- roles/shell/defaults/main.yml | 35 +- roles/shell/files/.p10k.zsh | 1719 ----------------- roles/shell/files/.zshrc | 220 --- roles/shell/files/ansible_aliases.zsh | 38 + roles/shell/files/p10k.zsh | 19 + roles/shell/files/showapps.sh | 2 +- roles/shell/files/zshrc.full | 42 + roles/shell/tasks/configure_user_shell.yml | 67 +- roles/shell/tasks/main.yml | 6 +- scripts/inventory.sh | 11 - site.yml | 2 - 69 files changed, 1563 insertions(+), 2925 deletions(-) create mode 100644 .cursor/rules/project-rules.mdc create mode 100644 docs/guides/custom-roles.md create mode 100644 docs/guides/monitoring.md create mode 100644 docs/guides/security.md create mode 100644 docs/project-docs.md create mode 100644 docs/reference/network.md create mode 100644 docs/reference/playbooks-and-tags.md create mode 100644 docs/reference/security.md create mode 100644 playbooks/servers.yml create mode 100644 playbooks/workstations.yml create mode 100644 project-docs/architecture.md create mode 100644 project-docs/decisions.md create mode 100644 project-docs/index.md create mode 100644 project-docs/overview.md create mode 100644 project-docs/standards.md create mode 100644 project-docs/workflow.md create mode 100644 roles/monitoring_desktop/defaults/main.yml create mode 100644 roles/monitoring_desktop/tasks/main.yml create mode 100644 roles/monitoring_server/defaults/main.yml create mode 100644 roles/monitoring_server/handlers/main.yml create mode 100644 roles/monitoring_server/tasks/main.yml create mode 100644 roles/monitoring_server/templates/jail.local.j2 delete mode 100644 roles/shell/files/.p10k.zsh delete mode 100644 roles/shell/files/.zshrc create mode 100644 roles/shell/files/ansible_aliases.zsh create mode 100644 roles/shell/files/p10k.zsh create mode 100644 roles/shell/files/zshrc.full diff --git a/.ansible-lint b/.ansible-lint index eb092b8..6b0e9a7 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/ - .gitea/ 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 1654acb..d723e44 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,7 +1,7 @@ --- name: CI -on: +"on": push: branches: [master] pull_request: @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 1 - + - name: Check if CI should be skipped id: check run: | @@ -54,7 +54,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 b427cfb..01a4327 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 [SUDO=true] [SSH_PASS=true]) ifndef HOST @@ -398,7 +422,7 @@ shell-all: ## Configure shell on all 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 18c5444..d18d568 100644 --- a/inventories/production/host_vars/devGPU.yml +++ b/inventories/production/host_vars/devGPU.yml @@ -22,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/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 94c2807..8fbc5ca 100644 --- a/playbooks/development.yml +++ b/playbooks/development.yml @@ -2,7 +2,6 @@ - name: Configure development environment hosts: dev become: true - strategy: free roles: - {role: timeshift, tags: ['timeshift', 'snapshot']} # Create snapshot before changes @@ -10,44 +9,12 @@ - {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: Remove NodeSource repository completely (fix GPG errors) - ansible.builtin.shell: | - # Remove NodeSource repository file - rm -f /etc/apt/sources.list.d/nodesource.list - # Remove NodeSource key file - rm -f /etc/apt/keyrings/nodesource.gpg - # Remove from sources.list if present - sed -i '/nodesource/d' /etc/apt/sources.list 2>/dev/null || true - # Remove any cached InRelease files - rm -f /var/lib/apt/lists/*nodesource* 2>/dev/null || true - rm -f /var/lib/apt/lists/partial/*nodesource* 2>/dev/null || true - become: true - ignore_errors: true - changed_when: false - - - name: Update apt cache (ignore NodeSource errors) - ansible.builtin.shell: | - apt-get update 2>&1 | grep -v "nodesource\|NO_PUBKEY.*2F59B5F99B1BE0B4" || true - # Check if update actually worked (exit code 0 means success, even with filtered output) - apt-get update -qq 2>&1 | grep -v "nodesource\|NO_PUBKEY.*2F59B5F99B1BE0B4" > /dev/null && exit 0 || exit 0 - become: true - ignore_errors: true - register: apt_update_result - changed_when: false - - - 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 e1da177..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,46 +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: Check if NodeSource repository exists - ansible.builtin.stat: - path: /etc/apt/sources.list.d/nodesource.list - register: nodesource_repo_file - failed_when: false - - - name: Check if NodeSource GPG key exists - ansible.builtin.stat: - path: /etc/apt/keyrings/nodesource.gpg - register: nodesource_key_file - failed_when: false - - - name: Remove incorrectly configured NodeSource repository - ansible.builtin.file: - path: /etc/apt/sources.list.d/nodesource.list - state: absent - become: true - when: - - nodesource_repo_file.stat.exists - - not (nodesource_key_file.stat.exists and nodesource_key_file.stat.size > 0) - - - 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 eb47ec6..8254e90 100644 --- a/roles/applications/tasks/main.yml +++ b/roles/applications/tasks/main.yml @@ -1,136 +1,27 @@ --- -- name: Remove NodeSource repository to prevent GPG errors - ansible.builtin.shell: | - # Remove NodeSource repository file - rm -f /etc/apt/sources.list.d/nodesource.list - # Remove NodeSource key file - rm -f /etc/apt/keyrings/nodesource.gpg - # Remove from sources.list if present - sed -i '/nodesource/d' /etc/apt/sources.list 2>/dev/null || true - # Remove any cached InRelease files - rm -f /var/lib/apt/lists/*nodesource* 2>/dev/null || true - rm -f /var/lib/apt/lists/partial/*nodesource* 2>/dev/null || true - become: true - ignore_errors: true - changed_when: false - - name: Check if applications are already installed 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 - changed_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 - changed_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 - update_cache: false - when: brave_repo_check.stdout in ["not_exists", "wrong_config"] - - - name: Update apt cache after adding Brave repository (ignore NodeSource errors) - ansible.builtin.shell: | - apt-get update 2>&1 | grep -v "nodesource\|NO_PUBKEY.*2F59B5F99B1BE0B4" || true - become: true - ignore_errors: true - 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 diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml index 57b61f8..5f3cc9f 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/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 835af57..c5c45d9 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,58 +11,51 @@ failed_when: false changed_when: false -- name: Remove NodeSource repository to fix GPG errors (always run first) - ansible.builtin.shell: | - # Remove NodeSource repository file to prevent GPG errors - rm -f /etc/apt/sources.list.d/nodesource.list - # Remove NodeSource key file - rm -f /etc/apt/keyrings/nodesource.gpg - # Clean apt cache to remove GPG errors - apt-get update 2>&1 | grep -v "NO_PUBKEY\|nodesource\|W:" || true - become: true - ignore_errors: true - changed_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: Skip NodeSource setup if Node.js is already installed +- 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: - skip_nodesource: "{{ node_version_check.rc == 0 }}" + 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 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 - changed_when: false # noqa command-instead-of-module +- 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 + ansible.builtin.file: + path: /etc/apt/sources.list.d/nodesource.list + state: absent + become: true when: - - not skip_nodesource | default(false) - - (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 - changed_when: false # noqa command-instead-of-module - when: - - not skip_nodesource | default(false) - - (node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')) - + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') + - nodesource_repo_state == "wrong_config" - name: Create keyrings directory ansible.builtin.file: @@ -77,11 +64,8 @@ mode: '0755' become: true when: - - not skip_nodesource | default(false) - - (node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')) - - nodesource_key_check is defined - - nodesource_key_check.stdout is defined - - nodesource_key_check.stdout in ["not_exists", "wrong_key"] + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') + - not (nodesource_key_stat.stat.exists | default(false)) - name: Import NodeSource GPG key into apt keyring ansible.builtin.shell: | @@ -99,11 +83,8 @@ fi become: true when: - - not skip_nodesource | default(false) - - (node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')) - - nodesource_key_check is defined - - nodesource_key_check.stdout is defined - - nodesource_key_check.stdout in ["not_exists", "wrong_key"] + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') + - not (nodesource_key_stat.stat.exists | default(false)) - name: Add NodeSource repository only if needed ansible.builtin.apt_repository: @@ -112,23 +93,8 @@ update_cache: false become: true when: - - not skip_nodesource | default(false) - - (node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')) - - nodesource_repo_check is defined - - nodesource_repo_check.stdout is defined - - nodesource_repo_check.stdout in ["not_exists", "wrong_config"] - -- name: Update apt cache after adding NodeSource repository - ansible.builtin.apt: - update_cache: true - become: true - ignore_errors: true - when: - - not skip_nodesource | default(false) - - (node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')) - - nodesource_repo_check is defined - - nodesource_repo_check.stdout is defined - - nodesource_repo_check.stdout in ["not_exists", "wrong_config"] + - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') + - nodesource_repo_state in ["not_exists", "wrong_config"] - name: Install Node.js 22 from NodeSource ansible.builtin.apt: @@ -136,7 +102,6 @@ state: present become: true when: - - not skip_nodesource | default(false) - (node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')) - name: Verify Node.js installation @@ -147,92 +112,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/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 fd33fe8..0000000 --- a/roles/shell/files/.zshrc +++ /dev/null @@ -1,220 +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" - -# 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 dfdd331..7313032 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,9 @@ 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" + - "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" + - "If zsh was set as the default shell, log out/in or run: exec zsh" - "==========================================" 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/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 - - -- 2.49.1 From 98e0fc0bed31da6468492b5b41e09709395dbde8 Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 12:10:45 -0500 Subject: [PATCH 02/14] Fix lint regressions after rebase --- roles/development/tasks/main.yml | 4 +-- roles/docker/tasks/main.yml | 11 -------- roles/docker/tasks/setup_repo_linux_mint.yml | 29 ++------------------ 3 files changed, 5 insertions(+), 39 deletions(-) diff --git a/roles/development/tasks/main.yml b/roles/development/tasks/main.yml index c5c45d9..28ee38f 100644 --- a/roles/development/tasks/main.yml +++ b/roles/development/tasks/main.yml @@ -71,8 +71,6 @@ ansible.builtin.shell: | # Ensure keyrings directory exists mkdir -p /etc/apt/keyrings - # Remove any existing broken key - rm -f /etc/apt/keyrings/nodesource.gpg # Download and convert key to binary format for signed-by curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg chmod 644 /etc/apt/keyrings/nodesource.gpg @@ -81,6 +79,8 @@ echo "ERROR: Key file is not valid PGP format" exit 1 fi + args: + creates: /etc/apt/keyrings/nodesource.gpg become: true when: - node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22') diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index efd397c..01ada1b 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -1,14 +1,4 @@ --- -- name: Remove NodeSource repository to prevent GPG errors - ansible.builtin.shell: | - # Remove NodeSource repository file to prevent GPG errors during apt cache update - rm -f /etc/apt/sources.list.d/nodesource.list - # Remove NodeSource key file - rm -f /etc/apt/keyrings/nodesource.gpg - become: true - ignore_errors: true - changed_when: false - - name: Debug distribution information ansible.builtin.debug: msg: @@ -21,7 +11,6 @@ - name: Check if Docker is already installed ansible.builtin.command: docker --version register: docker_check - ignore_errors: true changed_when: false failed_when: false no_log: true diff --git a/roles/docker/tasks/setup_repo_linux_mint.yml b/roles/docker/tasks/setup_repo_linux_mint.yml index 26f6203..17bdea1 100644 --- a/roles/docker/tasks/setup_repo_linux_mint.yml +++ b/roles/docker/tasks/setup_repo_linux_mint.yml @@ -29,21 +29,6 @@ become: true when: docker_repo_check.stdout == "wrong_config" -- name: Remove NodeSource repository completely before adding Docker repo - ansible.builtin.shell: | - # Remove NodeSource repository file - rm -f /etc/apt/sources.list.d/nodesource.list - # Remove NodeSource key file - rm -f /etc/apt/keyrings/nodesource.gpg - # Remove from sources.list if present - sed -i '/nodesource/d' /etc/apt/sources.list 2>/dev/null || true - # Remove any cached InRelease files - rm -f /var/lib/apt/lists/*nodesource* 2>/dev/null || true - rm -f /var/lib/apt/lists/partial/*nodesource* 2>/dev/null || true - become: true - ignore_errors: true - changed_when: false - - name: Add Docker repository for Linux Mint (using Ubuntu base) only if needed ansible.builtin.apt_repository: repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ docker_ubuntu_codename }} stable" @@ -51,16 +36,8 @@ update_cache: false when: docker_repo_check.stdout in ["not_exists", "wrong_config"] -- name: Update apt cache after adding Docker repository (ignore NodeSource errors) - ansible.builtin.shell: | - apt-get update 2>&1 | grep -v "nodesource\|NO_PUBKEY.*2F59B5F99B1BE0B4" || true - # Verify update succeeded for non-nodesource repos - if apt-get update 2>&1 | grep -q "E:"; then - # If there are real errors (not just nodesource), fail - if ! apt-get update 2>&1 | grep -q "nodesource"; then - exit 1 - fi - fi +- name: Update apt cache after adding Docker repository + ansible.builtin.apt: + update_cache: true become: true - ignore_errors: true when: docker_repo_check.stdout in ["not_exists", "wrong_config"] -- 2.49.1 From a176dd23654b4f07b7ce2a37c7673f3c845065ab Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 12:19:07 -0500 Subject: [PATCH 03/14] Makefile: avoid vault errors when detecting current host --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 01a4327..e578222 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,8 @@ ANSIBLE_ARGS := --vault-password-file ~/.ansible-vault-pass ## Auto-detect current host to exclude from remote operations CURRENT_IP := $(shell hostname -I | awk '{print $$1}') -CURRENT_HOST := $(shell ansible-inventory --list | jq -r '._meta.hostvars | to_entries[] | select(.value.ansible_host == "$(CURRENT_IP)") | .key' 2>/dev/null | head -1) +# NOTE: inventory parsing may require vault secrets. Keep this best-effort and silent in CI. +CURRENT_HOST := $(shell ansible-inventory --list --vault-password-file ~/.ansible-vault-pass 2>/dev/null | jq -r '._meta.hostvars | to_entries[] | select(.value.ansible_host == "$(CURRENT_IP)") | .key' 2>/dev/null | head -1) EXCLUDE_CURRENT := $(if $(CURRENT_HOST),--limit '!$(CURRENT_HOST)',) help: ## Show this help message -- 2.49.1 From ffd4165d24f824fb51116a54ec81ef81a6890a70 Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 12:39:36 -0500 Subject: [PATCH 04/14] CI: validate playbooks with CI inventory; install collections --- .gitea/workflows/ci.yml | 67 +++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index d723e44..fe65518 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -107,6 +107,10 @@ jobs: - name: Install Ansible and linting tools run: pip3 install --no-cache-dir ansible ansible-lint yamllint + - name: Install Ansible collections + run: | + ansible-galaxy collection install -r collections/requirements.yml + - name: Validate YAML syntax run: | echo "Checking YAML syntax..." @@ -115,10 +119,7 @@ jobs: done - name: Run ansible-lint - run: | - # Skip vault-encrypted files and playbooks that require vault passwords - ansible-lint --skip-list vault,internal-error || true - continue-on-error: true + run: ansible-lint secret-scanning: needs: skip-ci-check @@ -230,6 +231,7 @@ jobs: - name: Validate vault files are encrypted run: | echo "Checking for Ansible Vault files..." + # Intentionally skip *.example files: they are plaintext templates. vault_files=$(find . -name "*vault*.yml" -o -name "*vault*.yaml" | grep -v ".git" | grep -v ".example" || true) if [ -z "$vault_files" ]; then echo "No vault files found" @@ -275,29 +277,56 @@ jobs: - name: Install Ansible run: pip3 install --no-cache-dir ansible - - name: Dry-run playbooks + - name: Install Ansible collections run: | - echo "Running dry-run tests on playbooks..." + ansible-galaxy collection install -r collections/requirements.yml + + - name: Validate playbooks (CI inventory, no vault) + run: | + set -e + echo "Validating playbooks against a CI-only localhost inventory (no vault required)..." + cat > /tmp/ci-inventory.ini <<'EOF' + [dev] + localhost ansible_connection=local + + [desktop] + localhost ansible_connection=local + + [services] + localhost ansible_connection=local + + [qa] + localhost ansible_connection=local + + [ansible] + localhost ansible_connection=local + + [tailscale] + localhost ansible_connection=local + + [local] + localhost ansible_connection=local + EOF + failed=0 - for playbook in playbooks/*.yml; do - if [ -f "$playbook" ]; then - echo "Testing $playbook..." - if ansible-playbook "$playbook" --syntax-check --list-tasks > /dev/null 2>&1; then - echo "✓ $playbook syntax is valid" - else - echo "✗ $playbook has syntax errors" - failed=1 - fi + for playbook in playbooks/*.yml site.yml configure_app.yml provision_vms.yml; do + [ -f "$playbook" ] || continue + echo "Testing $playbook..." + if ansible-playbook -i /tmp/ci-inventory.ini "$playbook" --syntax-check --list-tasks; then + echo "✓ $playbook validated (syntax-check + list-tasks)" + else + echo "✗ $playbook failed validation (syntax-check/list-tasks)" + failed=1 fi done + if [ $failed -eq 1 ]; then - echo "❌ Some playbooks have syntax errors!" - echo "Note: This may be expected if playbooks require inventory/vault, but syntax errors should still be fixed." + echo "❌ Some playbooks failed CI validation." + echo "This should not require production inventory or vault secrets." exit 1 else - echo "✅ All playbooks passed syntax check" + echo "✅ All playbooks passed CI validation" fi - continue-on-error: true container-scan: needs: skip-ci-check -- 2.49.1 From 66d9f416c4076cf36c31095dcd7903b3cba51d08 Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 12:48:56 -0500 Subject: [PATCH 05/14] CI: improve Trivy dependency scan output --- .gitea/workflows/ci.yml | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index fe65518..cff8faf 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -155,8 +155,45 @@ jobs: - name: Check out code uses: actions/checkout@v4 - - name: Scan dependencies - run: trivy fs --scanners vuln,secret --exit-code 0 . + - name: Show dependency manifests (debug) + run: | + set -e + echo "Repo root:" + ls -la + echo "" + echo "Common dependency manifests:" + ls -la package.json package-lock.json requirements.txt pyproject.toml poetry.lock Pipfile Pipfile.lock 2>/dev/null || true + echo "" + echo "Count of lock/manifests found:" + find . -maxdepth 3 -type f \( \ + -name "package-lock.json" -o \ + -name "pnpm-lock.yaml" -o \ + -name "yarn.lock" -o \ + -name "requirements.txt" -o \ + -name "pyproject.toml" -o \ + -name "poetry.lock" -o \ + -name "Pipfile.lock" \ + \) | wc -l + + - name: Dependency vulnerability scan (Trivy) + run: | + trivy fs \ + --scanners vuln \ + --severity HIGH,CRITICAL \ + --ignore-unfixed \ + --timeout 10m \ + --skip-dirs .git,node_modules \ + --exit-code 0 \ + . + + - name: Secret scan (Trivy) + run: | + trivy fs \ + --scanners secret \ + --timeout 10m \ + --skip-dirs .git,node_modules \ + --exit-code 0 \ + . sast-scan: needs: skip-ci-check -- 2.49.1 From b69284f1c43b2d89927f600f6e7e499ea547953f Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 12:59:18 -0500 Subject: [PATCH 06/14] CI: enforce license check and ignore private root package --- .gitea/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index cff8faf..eb323a7 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -236,11 +236,12 @@ jobs: - name: Check npm licenses run: | if [ -f "package.json" ]; then - license-checker --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;ISC;BSD-2-Clause' || true + npm ci + # Exclude the repo itself (private=true packages are treated as UNLICENSED by license-checker). + license-checker --excludePrivatePackages --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;ISC;BSD-2-Clause' else echo "No package.json found, skipping license check" fi - continue-on-error: true vault-check: needs: skip-ci-check -- 2.49.1 From e0996642bc1e9eaa4b3c68270490c9696429abe5 Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 13:03:47 -0500 Subject: [PATCH 07/14] CI: fix triggers/conditions; make Sonar non-blocking --- .gitea/workflows/ci.yml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index eb323a7..cd95abf 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,7 +1,7 @@ --- name: CI -"on": +on: push: branches: [master] pull_request: @@ -62,10 +62,8 @@ jobs: lint-and-test: needs: skip-ci-check - if: needs.skip-ci-check.outputs.should-skip != '1' runs-on: ubuntu-latest - # Skip push events for non-master branches (they'll be covered by PR events) - if: github.event_name == 'pull_request' || github.ref == 'refs/heads/master' + if: needs.skip-ci-check.outputs.should-skip != '1' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/master') container: image: node:20-bullseye steps: @@ -84,10 +82,8 @@ jobs: ansible-validation: needs: skip-ci-check - if: needs.skip-ci-check.outputs.should-skip != '1' runs-on: ubuntu-latest - # Skip push events for non-master branches (they'll be covered by PR events) - if: github.event_name == 'pull_request' || github.ref == 'refs/heads/master' + if: needs.skip-ci-check.outputs.should-skip != '1' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/master') container: image: ubuntu:22.04 steps: @@ -440,7 +436,7 @@ jobs: sonar-analysis: needs: skip-ci-check - if: needs.skip-ci-check.outputs.should-skip != '1' + if: needs.skip-ci-check.outputs.should-skip != '1' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/master') runs-on: ubuntu-latest container: image: ubuntu:22.04 @@ -534,9 +530,8 @@ jobs: run: | echo "Checking SonarQube connectivity..." if [ -z "$SONAR_HOST_URL" ] || [ -z "$SONAR_TOKEN" ]; then - echo "❌ ERROR: SONAR_HOST_URL or SONAR_TOKEN secrets are not set!" - echo "Please configure them in: Repository Settings → Actions → Secrets" - exit 1 + echo "⚠️ Skipping SonarQube analysis: SONAR_HOST_URL or SONAR_TOKEN secrets are not set." + exit 0 fi echo "✓ Secrets are configured" echo "SonarQube URL: ${SONAR_HOST_URL}" @@ -544,12 +539,17 @@ jobs: if curl -f -s -o /dev/null -w "%{http_code}" "${SONAR_HOST_URL}/api/system/status" | grep -q "200"; then echo "✓ SonarQube server is reachable" else - echo "⚠️ Warning: Could not verify SonarQube server connectivity" + echo "⚠️ Warning: Could not verify SonarQube server connectivity (continuing anyway)" fi - name: Run SonarScanner run: | echo "Starting SonarQube analysis..." + if [ -z "$SONAR_HOST_URL" ] || [ -z "$SONAR_TOKEN" ]; then + echo "Skipping SonarQube analysis: secrets not set." + exit 0 + fi + if ! sonar-scanner \ -Dsonar.projectKey=ansible \ -Dsonar.sources=. \ @@ -570,7 +570,8 @@ jobs: echo " → Grant this permission in SonarQube user settings" echo "" echo "Check SonarQube logs for more details." - exit 1 + # Do not fail CI on Sonar auth/project setup issues. + exit 0 fi continue-on-error: true -- 2.49.1 From 0322279ab0fc628da8996d9f666caaeed8fb85ab Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 13:09:29 -0500 Subject: [PATCH 08/14] CI: avoid vault requirement in lint/tests --- .gitea/workflows/ci.yml | 71 +++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index cd95abf..9cd24c3 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -96,6 +96,31 @@ jobs: - name: Check out code uses: actions/checkout@v4 + - name: Configure CI Ansible (no vault, localhost inventory) + run: | + set -e + cat > /tmp/ci-inventory.ini <<'EOF' + [all] + localhost ansible_connection=local + EOF + + cat > /tmp/ci-ansible.cfg <<'EOF' + [defaults] + inventory = /tmp/ci-inventory.ini + roles_path = roles + host_key_checking = False + stdout_callback = yaml + bin_ansible_callbacks = True + retry_files_enabled = False + interpreter_python = auto_silent + forks = 10 + pipelining = True + EOF + + echo "ANSIBLE_CONFIG=/tmp/ci-ansible.cfg" >> "$GITHUB_ENV" + echo "ANSIBLE_INVENTORY=/tmp/ci-inventory.ini" >> "$GITHUB_ENV" + echo "ANSIBLE_VAULT_PASSWORD_FILE=/dev/null" >> "$GITHUB_ENV" + - name: Install Python and dependencies run: | apt-get update && apt-get install -y python3 python3-pip @@ -304,21 +329,9 @@ jobs: - name: Check out code uses: actions/checkout@v4 - - name: Install Python and dependencies - run: | - apt-get update && apt-get install -y python3 python3-pip - - - name: Install Ansible - run: pip3 install --no-cache-dir ansible - - - name: Install Ansible collections - run: | - ansible-galaxy collection install -r collections/requirements.yml - - - name: Validate playbooks (CI inventory, no vault) + - name: Configure CI Ansible (no vault, localhost inventory) run: | set -e - echo "Validating playbooks against a CI-only localhost inventory (no vault required)..." cat > /tmp/ci-inventory.ini <<'EOF' [dev] localhost ansible_connection=local @@ -342,6 +355,38 @@ jobs: localhost ansible_connection=local EOF + cat > /tmp/ci-ansible.cfg <<'EOF' + [defaults] + inventory = /tmp/ci-inventory.ini + roles_path = roles + host_key_checking = False + stdout_callback = yaml + bin_ansible_callbacks = True + retry_files_enabled = False + interpreter_python = auto_silent + forks = 10 + pipelining = True + EOF + + echo "ANSIBLE_CONFIG=/tmp/ci-ansible.cfg" >> "$GITHUB_ENV" + echo "ANSIBLE_INVENTORY=/tmp/ci-inventory.ini" >> "$GITHUB_ENV" + echo "ANSIBLE_VAULT_PASSWORD_FILE=/dev/null" >> "$GITHUB_ENV" + + - name: Install Python and dependencies + run: | + apt-get update && apt-get install -y python3 python3-pip + + - name: Install Ansible + run: pip3 install --no-cache-dir ansible + + - name: Install Ansible collections + run: | + ansible-galaxy collection install -r collections/requirements.yml + + - name: Validate playbooks (CI inventory, no vault) + run: | + set -e + echo "Validating playbooks against a CI-only localhost inventory (no vault required)..." failed=0 for playbook in playbooks/*.yml site.yml configure_app.yml provision_vms.yml; do [ -f "$playbook" ] || continue -- 2.49.1 From 5ad985d9f8101ffdc32586a086dedb230aea9f47 Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 13:11:34 -0500 Subject: [PATCH 09/14] Update dependencies in package.json and package-lock.json - Upgrade markdownlint-cli2 to version 0.20.0 - Update various dependencies to their latest versions, ensuring compatibility with Node.js 20.x - Add new dependencies such as string-width and strip-ansi for improved functionality This change enhances the development environment by keeping dependencies up to date and ensuring compliance with the latest Node.js version requirements. --- package-lock.json | 316 +++++++++++++++++++++++++--------------------- package.json | 4 +- 2 files changed, 175 insertions(+), 145 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0313618..dfec431 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "devDependencies": { "markdown-link-check": "^3.13.7", - "markdownlint-cli2": "^0.18.1" + "markdownlint-cli2": "^0.20.0" }, "engines": { "node": ">=20.0.0", @@ -56,61 +56,61 @@ } }, "node_modules/@oozcitak/dom": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz", - "integrity": "sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-2.0.2.tgz", + "integrity": "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==", "dev": true, "license": "MIT", "dependencies": { - "@oozcitak/infra": "1.0.8", - "@oozcitak/url": "1.0.4", - "@oozcitak/util": "8.3.8" + "@oozcitak/infra": "^2.0.2", + "@oozcitak/url": "^3.0.0", + "@oozcitak/util": "^10.0.0" }, "engines": { - "node": ">=8.0" + "node": ">=20.0" } }, "node_modules/@oozcitak/infra": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.8.tgz", - "integrity": "sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-2.0.2.tgz", + "integrity": "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==", "dev": true, "license": "MIT", "dependencies": { - "@oozcitak/util": "8.3.8" + "@oozcitak/util": "^10.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=20.0" } }, "node_modules/@oozcitak/url": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.4.tgz", - "integrity": "sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-3.0.0.tgz", + "integrity": "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==", "dev": true, "license": "MIT", "dependencies": { - "@oozcitak/infra": "1.0.8", - "@oozcitak/util": "8.3.8" + "@oozcitak/infra": "^2.0.2", + "@oozcitak/util": "^10.0.0" }, "engines": { - "node": ">=8.0" + "node": ">=20.0" } }, "node_modules/@oozcitak/util": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", - "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-10.0.0.tgz", + "integrity": "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.0" + "node": ">=20.0" } }, "node_modules/@sindresorhus/merge-streams": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", - "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, "license": "MIT", "engines": { @@ -168,6 +168,19 @@ "node": ">= 14" } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -196,9 +209,9 @@ "license": "MIT" }, "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", + "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", "dev": true, "license": "MIT", "engines": { @@ -316,13 +329,13 @@ } }, "node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/css-select": { @@ -596,9 +609,9 @@ } }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { @@ -618,6 +631,19 @@ "node": ">=8" } }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-uri": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", @@ -647,21 +673,21 @@ } }, "node_modules/globby": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", - "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-15.0.0.tgz", + "integrity": "sha512-oB4vkQGqlMl682wL1IlWd02tXCbquGWM4voPEI85QmNKCaw8zGTm1f1rubFgkg3Eli2PtKlFgrnmUqasbQWlkw==", "dev": true, "license": "MIT", "dependencies": { - "@sindresorhus/merge-streams": "^2.1.0", + "@sindresorhus/merge-streams": "^4.0.0", "fast-glob": "^3.3.3", - "ignore": "^7.0.3", + "ignore": "^7.0.5", "path-type": "^6.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.3.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -762,9 +788,9 @@ } }, "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, "license": "MIT", "engines": { @@ -866,9 +892,9 @@ } }, "node_modules/is-relative-url": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-4.0.0.tgz", - "integrity": "sha512-PkzoL1qKAYXNFct5IKdKRH/iBQou/oCC85QhXj6WKtUQBliZ4Yfd3Zk27RHu9KQG8r6zgvAA2AQKC9p+rqTszg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-4.1.0.tgz", + "integrity": "sha512-vhIXKasjAuxS7n+sdv7pJQykEAgS+YU8VBQOENXwo/VZpOHDgBBsIbHo7zFKaWBjYWF4qxERdhbPRRtFAeJKfg==", "dev": true, "license": "MIT", "dependencies": { @@ -882,9 +908,9 @@ } }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -902,9 +928,9 @@ "license": "MIT" }, "node_modules/katex": { - "version": "0.16.22", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", - "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", + "version": "0.16.27", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.27.tgz", + "integrity": "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==", "dev": true, "funding": [ "https://opencollective.com/katex", @@ -929,17 +955,17 @@ } }, "node_modules/link-check": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/link-check/-/link-check-5.4.0.tgz", - "integrity": "sha512-0Pf4xBVUnwJdbDgpBlhHNmWDtbVjHTpIFs+JaBuIsC9PKRxjv4KMGCO2Gc8lkVnqMf9B/yaNY+9zmMlO5MyToQ==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/link-check/-/link-check-5.5.1.tgz", + "integrity": "sha512-GrtE4Zp/FBduvElmad375NrPeMYnKwNt9rH/TDG/rbQbHL0QVC4S/cEPVKZ0CkhXlVuiK+/5flGpRxQzoLbjEA==", "dev": true, "license": "ISC", "dependencies": { - "is-relative-url": "^4.0.0", + "is-relative-url": "^4.1.0", "ms": "^2.1.3", "needle": "^3.3.1", - "node-email-verifier": "^2.0.0", - "proxy-agent": "^6.4.0" + "node-email-verifier": "^3.4.1", + "proxy-agent": "^6.5.0" } }, "node_modules/linkify-it": { @@ -981,41 +1007,41 @@ } }, "node_modules/markdown-link-check": { - "version": "3.13.7", - "resolved": "https://registry.npmjs.org/markdown-link-check/-/markdown-link-check-3.13.7.tgz", - "integrity": "sha512-Btn3HU8s2Uyh1ZfzmyZEkp64zp2+RAjwfQt1u4swq2Xa6w37OW0T2inQZrkSNVxDSa2jSN2YYhw/JkAp5jF1PQ==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/markdown-link-check/-/markdown-link-check-3.14.2.tgz", + "integrity": "sha512-DPJ+itd3D5fcfXD5s1i53lugH0Z/h80kkQxlYCBh8tFwEZGhyVgDcLl0rnKlWssAVDAmSmcbePpHpMEY+JcMMQ==", "dev": true, "license": "ISC", "dependencies": { "async": "^3.2.6", - "chalk": "^5.3.0", - "commander": "^13.1.0", - "link-check": "^5.4.0", - "markdown-link-extractor": "^4.0.2", + "chalk": "^5.6.2", + "commander": "^14.0.2", + "link-check": "^5.5.1", + "markdown-link-extractor": "^4.0.3", "needle": "^3.3.1", "progress": "^2.0.3", - "proxy-agent": "^6.4.0", - "xmlbuilder2": "^3.1.1" + "proxy-agent": "^6.5.0", + "xmlbuilder2": "^4.0.0" }, "bin": { "markdown-link-check": "markdown-link-check" } }, "node_modules/markdown-link-extractor": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/markdown-link-extractor/-/markdown-link-extractor-4.0.2.tgz", - "integrity": "sha512-5cUOu4Vwx1wenJgxaudsJ8xwLUMN7747yDJX3V/L7+gi3e4MsCm7w5nbrDQQy8nEfnl4r5NV3pDXMAjhGXYXAw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/markdown-link-extractor/-/markdown-link-extractor-4.0.3.tgz", + "integrity": "sha512-aEltJiQ4/oC0h6Jbw/uuATGSHZPkcH8DIunNH1A0e+GSFkvZ6BbBkdvBTVfIV8r6HapCU3yTd0eFdi3ZeM1eAQ==", "dev": true, "license": "ISC", "dependencies": { "html-link-extractor": "^1.0.5", - "marked": "^12.0.1" + "marked": "^17.0.0" } }, "node_modules/markdownlint": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz", - "integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.40.0.tgz", + "integrity": "sha512-UKybllYNheWac61Ia7T6fzuQNDZimFIpCg2w6hHjgV1Qu0w1TV0LlSgryUGzM0bkKQCBhy2FDhEELB73Kb0kAg==", "dev": true, "license": "MIT", "dependencies": { @@ -1026,7 +1052,8 @@ "micromark-extension-gfm-footnote": "2.1.0", "micromark-extension-gfm-table": "2.1.1", "micromark-extension-math": "3.1.0", - "micromark-util-types": "2.0.2" + "micromark-util-types": "2.0.2", + "string-width": "8.1.0" }, "engines": { "node": ">=20" @@ -1036,18 +1063,18 @@ } }, "node_modules/markdownlint-cli2": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.18.1.tgz", - "integrity": "sha512-/4Osri9QFGCZOCTkfA8qJF+XGjKYERSHkXzxSyS1hd3ZERJGjvsUao2h4wdnvpHp6Tu2Jh/bPHM0FE9JJza6ng==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.20.0.tgz", + "integrity": "sha512-esPk+8Qvx/f0bzI7YelUeZp+jCtFOk3KjZ7s9iBQZ6HlymSXoTtWGiIRZP05/9Oy2ehIoIjenVwndxGtxOIJYQ==", "dev": true, "license": "MIT", "dependencies": { - "globby": "14.1.0", - "js-yaml": "4.1.0", + "globby": "15.0.0", + "js-yaml": "4.1.1", "jsonc-parser": "3.3.1", "markdown-it": "14.1.0", - "markdownlint": "0.38.0", - "markdownlint-cli2-formatter-default": "0.0.5", + "markdownlint": "0.40.0", + "markdownlint-cli2-formatter-default": "0.0.6", "micromatch": "4.0.8" }, "bin": { @@ -1061,9 +1088,9 @@ } }, "node_modules/markdownlint-cli2-formatter-default": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.5.tgz", - "integrity": "sha512-4XKTwQ5m1+Txo2kuQ3Jgpo/KmnG+X90dWt4acufg6HVGadTUG5hzHF/wssp9b5MBYOMCnZ9RMPaU//uHsszF8Q==", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.6.tgz", + "integrity": "sha512-VVDGKsq9sgzu378swJ0fcHfSicUnMxnL8gnLm/Q4J/xsNJ4e5bA6lvAz7PCzIl0/No0lHyaWdqVD2jotxOSFMQ==", "dev": true, "license": "MIT", "funding": { @@ -1074,16 +1101,16 @@ } }, "node_modules/marked": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", - "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.1.tgz", + "integrity": "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==", "dev": true, "license": "MIT", "bin": { "marked": "bin/marked.js" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/mdurl": { @@ -1688,17 +1715,17 @@ } }, "node_modules/node-email-verifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-email-verifier/-/node-email-verifier-2.0.0.tgz", - "integrity": "sha512-AHcppjOH2KT0mxakrxFMOMjV/gOVMRpYvnJUkNfgF9oJ3INdVmqcMFJ5TlM8elpTPwt6A7bSp1IMnnWcxGom/Q==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/node-email-verifier/-/node-email-verifier-3.4.1.tgz", + "integrity": "sha512-69JMeWgEUrCji+dOLULirdSoosRxgAq2y+imfmHHBGvgTwyTKqvm65Ls3+W30DCIWMrYj5kKVb/DHTQDK7OVwQ==", "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3", - "validator": "^13.11.0" + "validator": "^13.15.15" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/nth-check": { @@ -1958,11 +1985,11 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz", + "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0" }, "node_modules/slash": { "version": "5.1.0", @@ -2029,12 +2056,38 @@ "node": ">=0.10.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", "dev": true, - "license": "BSD-3-Clause" + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -2064,9 +2117,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.15.0.tgz", - "integrity": "sha512-7oZJCPvvMvTd0OlqWsIxTuItTpJBpU1tcbVl24FMn3xt3+VSunwUasmfPJRE57oNO1KsZ4PgA1xTdAX4hq8NyQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", + "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", "dev": true, "license": "MIT", "engines": { @@ -2087,9 +2140,9 @@ } }, "node_modules/validator": { - "version": "13.15.15", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", - "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", "dev": true, "license": "MIT", "engines": { @@ -2100,6 +2153,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", "dev": true, "license": "MIT", "dependencies": { @@ -2120,43 +2174,19 @@ } }, "node_modules/xmlbuilder2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.1.1.tgz", - "integrity": "sha512-WCSfbfZnQDdLQLiMdGUQpMxxckeQ4oZNMNhLVkcekTu7xhD4tuUDyAPoY8CwXvBYE6LwBHd6QW2WZXlOWr1vCw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-4.0.3.tgz", + "integrity": "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==", "dev": true, "license": "MIT", "dependencies": { - "@oozcitak/dom": "1.15.10", - "@oozcitak/infra": "1.0.8", - "@oozcitak/util": "8.3.8", - "js-yaml": "3.14.1" + "@oozcitak/dom": "^2.0.2", + "@oozcitak/infra": "^2.0.2", + "@oozcitak/util": "^10.0.0", + "js-yaml": "^4.1.1" }, "engines": { - "node": ">=12.0" - } - }, - "node_modules/xmlbuilder2/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/xmlbuilder2/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "node": ">=20.0" } } } diff --git a/package.json b/package.json index 0739a0d..50e1c95 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "markdown-link-check": "^3.13.7", - "markdownlint-cli2": "^0.18.1" + "markdownlint-cli2": "^0.20.0" }, "engines": { "node": ">=20.0.0", @@ -25,4 +25,4 @@ "devops" ], "license": "MIT" -} \ No newline at end of file +} -- 2.49.1 From c5f01d27de7ce65584d62d135299670d35279db9 Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 13:12:31 -0500 Subject: [PATCH 10/14] CI: fix vault file detection; remove plaintext vault --- .gitea/workflows/ci.yml | 9 +++++++-- inventories/production/host_vars/devGPU/vault.yml | 2 -- 2 files changed, 7 insertions(+), 4 deletions(-) delete mode 100644 inventories/production/host_vars/devGPU/vault.yml diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9cd24c3..e931796 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -291,7 +291,11 @@ jobs: run: | echo "Checking for Ansible Vault files..." # Intentionally skip *.example files: they are plaintext templates. - vault_files=$(find . -name "*vault*.yml" -o -name "*vault*.yaml" | grep -v ".git" | grep -v ".example" || true) + # Only treat conventional vault files as "must be encrypted": + # - vault.yml / vault.yaml + # - vault_*.yml / vault_*.yaml + # Avoid false-positives like host_vars/vaultwardenVM.yml (host name contains "vault"). + vault_files=$(find . \( -name "vault.yml" -o -name "vault.yaml" -o -name "vault_*.yml" -o -name "vault_*.yaml" \) | grep -v ".git" | grep -v ".example" || true) if [ -z "$vault_files" ]; then echo "No vault files found" exit 0 @@ -300,7 +304,8 @@ jobs: for vault_file in $vault_files; do echo "Checking $vault_file..." # Check if file starts with ANSIBLE_VAULT header (doesn't require password) - if head -n 1 "$vault_file" | grep -q "^\$ANSIBLE_VAULT"; then + # Some vault files may start with '---' (YAML document start) on line 1. + if head -n 5 "$vault_file" | grep -q "^\$ANSIBLE_VAULT"; then echo "✓ $vault_file is properly encrypted (has vault header)" else echo "✗ ERROR: $vault_file does not have ANSIBLE_VAULT header - may be unencrypted!" diff --git a/inventories/production/host_vars/devGPU/vault.yml b/inventories/production/host_vars/devGPU/vault.yml deleted file mode 100644 index 9993973..0000000 --- a/inventories/production/host_vars/devGPU/vault.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -vault_devgpu_become_password: root -- 2.49.1 From 84ad3fb56a907262856c4248631078e1fdd7e5b7 Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 13:16:13 -0500 Subject: [PATCH 11/14] inventory: drop become password for root devGPU --- inventories/production/host_vars/devGPU.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/inventories/production/host_vars/devGPU.yml b/inventories/production/host_vars/devGPU.yml index d18d568..602be26 100644 --- a/inventories/production/host_vars/devGPU.yml +++ b/inventories/production/host_vars/devGPU.yml @@ -1,6 +1,4 @@ --- -ansible_become_password: "{{ vault_devgpu_become_password }}" - ansible_python_interpreter: /usr/bin/python3 # Shell configuration -- 2.49.1 From a8c83d6efd8b277327da853c67e38c33d55776fc Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 16:33:06 -0500 Subject: [PATCH 12/14] CI: fix vault/markdown/license errors - Remove ANSIBLE_VAULT_PASSWORD_FILE env (invalid /dev/null) - Fix markdown table spacing in docs/reference/*.md - Whitelist Python-2.0 license (argparse dependency) --- .gitea/workflows/ci.yml | 4 +--- docs/reference/applications.md | 20 ++++++++++---------- docs/reference/makefile.md | 22 +++++++++++----------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index e931796..3601891 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -119,7 +119,6 @@ jobs: echo "ANSIBLE_CONFIG=/tmp/ci-ansible.cfg" >> "$GITHUB_ENV" echo "ANSIBLE_INVENTORY=/tmp/ci-inventory.ini" >> "$GITHUB_ENV" - echo "ANSIBLE_VAULT_PASSWORD_FILE=/dev/null" >> "$GITHUB_ENV" - name: Install Python and dependencies run: | @@ -259,7 +258,7 @@ jobs: if [ -f "package.json" ]; then npm ci # Exclude the repo itself (private=true packages are treated as UNLICENSED by license-checker). - license-checker --excludePrivatePackages --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;ISC;BSD-2-Clause' + license-checker --excludePrivatePackages --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;ISC;BSD-2-Clause;Python-2.0' else echo "No package.json found, skipping license check" fi @@ -375,7 +374,6 @@ jobs: echo "ANSIBLE_CONFIG=/tmp/ci-ansible.cfg" >> "$GITHUB_ENV" echo "ANSIBLE_INVENTORY=/tmp/ci-inventory.ini" >> "$GITHUB_ENV" - echo "ANSIBLE_VAULT_PASSWORD_FILE=/dev/null" >> "$GITHUB_ENV" - name: Install Python and dependencies run: | diff --git a/docs/reference/applications.md b/docs/reference/applications.md index a629b9b..da58c5e 100644 --- a/docs/reference/applications.md +++ b/docs/reference/applications.md @@ -6,7 +6,7 @@ Complete inventory of applications and tools deployed by Ansible playbooks. ### 🔧 System Tools | Package | Description | Source | Role | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | curl | Command line HTTP client | apt | base | | wget | Network downloader | apt | base | | unzip | Archive extraction | apt | base | @@ -20,7 +20,7 @@ Complete inventory of applications and tools deployed by Ansible playbooks. ### 🔒 Security Tools | Package | Description | Source | Role | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | ufw | Uncomplicated Firewall | apt | ssh | | fail2ban | Intrusion prevention | apt | monitoring | | openssh-server | SSH daemon | apt | ssh | @@ -31,7 +31,7 @@ Complete inventory of applications and tools deployed by Ansible playbooks. ### 💻 Development Tools | Package | Description | Source | Role | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | git | Version control | apt | development | | nodejs | JavaScript runtime | apt | development | | npm | Node package manager | apt | development | @@ -41,7 +41,7 @@ Complete inventory of applications and tools deployed by Ansible playbooks. ### 🐳 Container Platform | Package | Description | Source | Role | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | docker-ce | Docker Community Edition | docker | docker | | docker-ce-cli | Docker CLI | docker | docker | | containerd.io | Container runtime | docker | docker | @@ -50,7 +50,7 @@ Complete inventory of applications and tools deployed by Ansible playbooks. ### 🖥️ Shell Environment | Package | Description | Source | Role | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | zsh | Z shell | apt | shell | | tmux | Terminal multiplexer | apt | shell | | fzf | Fuzzy finder | apt | shell | @@ -58,7 +58,7 @@ Complete inventory of applications and tools deployed by Ansible playbooks. ### 📊 Monitoring Tools | Package | Description | Source | Role | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | htop | Process viewer | apt | monitoring | | btop | Modern system monitor | snap | monitoring | | iotop | I/O monitor | apt | monitoring | @@ -74,13 +74,13 @@ Complete inventory of applications and tools deployed by Ansible playbooks. ### 🌐 Network Tools | Package | Description | Source | Role | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | tailscale | Mesh VPN client | tailscale | tailscale | | tailscaled | Tailscale daemon | tailscale | tailscale | ### 🖱️ Desktop Applications | Package | Description | Source | Role | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | copyq | Clipboard manager (history/search) | apt | applications | | evince | PDF viewer | apt | applications | | redshift | Blue light filter | apt | applications | @@ -148,7 +148,7 @@ Maintains existing installations: ## Package Sources | Source | Description | Configuration | -|--------|-------------|---------------| +| -------- | ------------- | --------------- | | apt | Debian/Ubuntu packages | System default | | snap | Snap packages | snapd daemon | | docker | Docker repository | Docker GPG key + repo | @@ -158,7 +158,7 @@ Maintains existing installations: ## Services Enabled | Service | Description | Management | -|---------|-------------|------------| +| --------- | ------------- | ------------ | | docker | Container runtime | systemctl | | tailscaled | VPN daemon | systemctl | | ufw | Firewall | systemctl | diff --git a/docs/reference/makefile.md b/docs/reference/makefile.md index 7b71075..04d1a02 100644 --- a/docs/reference/makefile.md +++ b/docs/reference/makefile.md @@ -6,7 +6,7 @@ Complete reference for all available `make` commands in the Ansible project. ### Setup & Testing | Command | Description | Usage | -|---------|-------------|-------| +| --------- | ------------- | ------- | | `help` | Show all available commands | `make help` | | `bootstrap` | Install required collections and dependencies | `make bootstrap` | | `test` | Run all tests (lint + syntax check) | `make test` | @@ -17,7 +17,7 @@ Complete reference for all available `make` commands in the Ansible project. ### Deployment | Command | Description | Usage | -|---------|-------------|-------| +| --------- | ------------- | ------- | | `apply` | Run development playbook on all dev hosts | `make apply` | | `local` | Run local playbook on localhost | `make local` | | `dev` | Run on specific host | `make dev HOST=dev01` | @@ -27,7 +27,7 @@ Complete reference for all available `make` commands in the Ansible project. ### System Maintenance | Command | Description | Usage | -|---------|-------------|-------| +| --------- | ------------- | ------- | | `maintenance` | Run system maintenance | `make maintenance [GROUP=dev] [HOST=dev01]` | | | | Options: `SERIAL=1 CHECK=true VERBOSE=true` | | `maintenance-dev` | Run maintenance on dev group | `make maintenance-dev` | @@ -37,7 +37,7 @@ Complete reference for all available `make` commands in the Ansible project. ### Security & Networking | Command | Description | Usage | -|---------|-------------|-------| +| --------- | ------------- | ------- | | `security` | Run only security-related roles | `make security` | | `tailscale` | Install Tailscale on all machines | `make tailscale` | | `tailscale-check` | Check Tailscale installation (dry-run) | `make tailscale-check` | @@ -46,7 +46,7 @@ Complete reference for all available `make` commands in the Ansible project. ### Applications & Tools | Command | Description | Usage | -|---------|-------------|-------| +| --------- | ------------- | ------- | | `docker` | Install/configure Docker only | `make docker` | | `shell` | Configure shell only | `make shell` | | `apps` | Install applications only | `make apps` | @@ -56,9 +56,9 @@ Complete reference for all available `make` commands in the Ansible project. ### VM & Host Management | Command | Description | Usage | -|---------|-------------|-------| +| --------- | ------------- | ------- | | `create-vm` | Create Ansible controller VM on Proxmox | `make create-vm` | -| `proxmox-info` | Show Proxmox guest info (LXC/VM) | `make proxmox-info [PROJECT=projectA] [ALL=true] [TYPE=lxc\|qemu\|all]` | +| `proxmox-info` | Show Proxmox guest info (LXC/VM) | `make proxmox-info [PROJECT=projectA] [ALL=true] [TYPE=lxc/qemu/all]` | | `app-provision` | Provision app project guests on Proxmox | `make app-provision PROJECT=projectA` | | `app-configure` | Configure OS + app on project guests | `make app-configure PROJECT=projectA` | | `app` | Provision + configure app project guests | `make app PROJECT=projectA` | @@ -69,7 +69,7 @@ Complete reference for all available `make` commands in the Ansible project. ### SSH & Vault Management | Command | Description | Usage | -|---------|-------------|-------| +| --------- | ------------- | ------- | | `copy-ssh-key` | Copy SSH key to specific host | `make copy-ssh-key HOST=giteaVM` | | `create-vault` | Create encrypted vault file | `make create-vault` | | `edit-vault` | Edit encrypted host vars | `make edit-vault HOST=dev01` | @@ -79,7 +79,7 @@ Complete reference for all available `make` commands in the Ansible project. ### Debugging & Cleanup | Command | Description | Usage | -|---------|-------------|-------| +| --------- | ------------- | ------- | | `debug` | Run with debug output enabled | `make debug` | | `verbose` | Run with verbose output | `make verbose` | | `clean` | Clean up ansible artifacts | `make clean` | @@ -90,7 +90,7 @@ Complete reference for all available `make` commands in the Ansible project. Many commands accept these optional variables: | Variable | Description | Example | -|----------|-------------|---------| +| ---------- | ------------- | --------- | | `HOST` | Target specific host | `HOST=dev01` | | `GROUP` | Target specific group | `GROUP=dev` | | `CHECK` | Run in check mode (dry-run) | `CHECK=true` | @@ -170,7 +170,7 @@ make debug The Makefile respects these environment variables: | Variable | Description | Default | -|----------|-------------|---------| +| ---------- | ------------- | --------- | | `ANSIBLE_CONFIG` | Ansible configuration file | `./ansible.cfg` | | `ANSIBLE_VAULT_PASSWORD_FILE` | Vault password file | `~/.ansible-vault-pass` | | `ANSIBLE_HOST_KEY_CHECKING` | SSH host key checking | `False` | -- 2.49.1 From 64f7273ddd9d5167091072b28fc5393e58f7577c Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 21:48:27 -0500 Subject: [PATCH 13/14] CI: fix Jinja2 recursion and license check - Fix recursive loop in configure_app.yml by using hardcoded defaults instead of self-referential variables (app_backend_port, etc.) - Whitelist BlueOak-1.0.0 license (for sax dependency) - All make test checks pass locally --- .gitea/workflows/ci.yml | 2 +- playbooks/app/configure_app.yml | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 3601891..236d984 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -258,7 +258,7 @@ jobs: if [ -f "package.json" ]; then npm ci # Exclude the repo itself (private=true packages are treated as UNLICENSED by license-checker). - license-checker --excludePrivatePackages --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;ISC;BSD-2-Clause;Python-2.0' + license-checker --excludePrivatePackages --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;ISC;BSD-2-Clause;Python-2.0;BlueOak-1.0.0' else echo "No package.json found, skipping license check" fi diff --git a/playbooks/app/configure_app.yml b/playbooks/app/configure_app.yml index bbb2576..974ad51 100644 --- a/playbooks/app/configure_app.yml +++ b/playbooks/app/configure_app.yml @@ -117,18 +117,19 @@ app_owner: "{{ project_def.os_user | default(appuser_name) }}" app_group: "{{ project_def.os_user | default(appuser_name) }}" - app_backend_port: "{{ project_def.backend_port | default(app_backend_port) }}" - app_frontend_port: "{{ project_def.frontend_port | default(app_frontend_port) }}" + # Use different variable names to avoid self-referential recursion + app_backend_port: "{{ project_def.backend_port | default(3001) }}" + app_frontend_port: "{{ project_def.frontend_port | default(3000) }}" app_enable_backend: "{{ project_def.components.backend | default(true) }}" app_enable_frontend: "{{ project_def.components.frontend | default(true) }}" - app_backend_install_cmd: "{{ project_def.deploy.backend_install_cmd | default(app_backend_install_cmd) }}" - app_backend_migrate_cmd: "{{ project_def.deploy.backend_migrate_cmd | default(app_backend_migrate_cmd) }}" - app_backend_start_cmd: "{{ project_def.deploy.backend_start_cmd | default(app_backend_start_cmd) }}" + app_backend_install_cmd: "{{ project_def.deploy.backend_install_cmd | default('npm ci') }}" + app_backend_migrate_cmd: "{{ project_def.deploy.backend_migrate_cmd | default('npm run migrate') }}" + app_backend_start_cmd: "{{ project_def.deploy.backend_start_cmd | default('npm start') }}" - app_frontend_install_cmd: "{{ project_def.deploy.frontend_install_cmd | default(app_frontend_install_cmd) }}" - app_frontend_build_cmd: "{{ project_def.deploy.frontend_build_cmd | default(app_frontend_build_cmd) }}" - app_frontend_start_cmd: "{{ project_def.deploy.frontend_start_cmd | default(app_frontend_start_cmd) }}" + app_frontend_install_cmd: "{{ project_def.deploy.frontend_install_cmd | default('npm ci') }}" + app_frontend_build_cmd: "{{ project_def.deploy.frontend_build_cmd | default('npm run build') }}" + app_frontend_start_cmd: "{{ project_def.deploy.frontend_start_cmd | default('npm start') }}" app_env_vars: "{{ env_def.env_vars | default({}) }}" when: app_project != 'pote' -- 2.49.1 From 85e475b36d415f421d8c54f8192a9153cf3ed43e Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 22:01:40 -0500 Subject: [PATCH 14/14] CI: fix remaining errors - Whitelist 0BSD license (for tslib dependency) - Fix roles_path to use absolute path: /workspace/ilia/ansible/roles (relative 'roles' was searching in wrong directories) - Add Node.js install step before checkout in sonar-analysis job (actions/checkout@v4 requires node runtime) All make test and npm test checks pass locally --- .gitea/workflows/ci.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 236d984..71e8932 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: cat > /tmp/ci-ansible.cfg <<'EOF' [defaults] inventory = /tmp/ci-inventory.ini - roles_path = roles + roles_path = /workspace/ilia/ansible/roles host_key_checking = False stdout_callback = yaml bin_ansible_callbacks = True @@ -258,7 +258,7 @@ jobs: if [ -f "package.json" ]; then npm ci # Exclude the repo itself (private=true packages are treated as UNLICENSED by license-checker). - license-checker --excludePrivatePackages --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;ISC;BSD-2-Clause;Python-2.0;BlueOak-1.0.0' + license-checker --excludePrivatePackages --onlyAllow 'MIT;Apache-2.0;BSD-3-Clause;ISC;BSD-2-Clause;Python-2.0;BlueOak-1.0.0;0BSD' else echo "No package.json found, skipping license check" fi @@ -362,7 +362,7 @@ jobs: cat > /tmp/ci-ansible.cfg <<'EOF' [defaults] inventory = /tmp/ci-inventory.ini - roles_path = roles + roles_path = /workspace/ilia/ansible/roles host_key_checking = False stdout_callback = yaml bin_ansible_callbacks = True @@ -492,6 +492,12 @@ jobs: SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} steps: + - name: Install Node.js for checkout action + run: | + apt-get update && apt-get install -y curl + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs + - name: Check out code uses: actions/checkout@v4 -- 2.49.1