From c3e6caf9e8ea9a3362d81f3fd6b71854150752bd Mon Sep 17 00:00:00 2001 From: ilia Date: Thu, 1 Jan 2026 22:11:24 -0500 Subject: [PATCH] refactor-servers-workstations-shell-monitoring (#4) ### Summary This PR refactors the playbook layout to reduce duplication and make host intent clearer (servers vs workstations), splits monitoring by host type, and restores full Zsh setup for developers while keeping servers aliases-only. ### Key changes - **New playbooks** - `playbooks/servers.yml`: baseline for server-class hosts (no desktop apps) - `playbooks/workstations.yml`: baseline for dev/desktop/local + **desktop apps only on `desktop` group** - **Monitoring split** - `roles/monitoring_server`: server monitoring + intrusion prevention (includes `fail2ban`, sysstat) - `roles/monitoring_desktop`: desktop-oriented monitoring tooling - Updated playbooks to use the correct monitoring role per host type - **Shell role: server-safe + developer-friendly** - `roles/shell` now supports two modes: - `shell_mode: minimal` (default): aliases-only, does not overwrite `.zshrc` - `shell_mode: full`: installs Oh My Zsh + Powerlevel10k + plugins and deploys a managed `.zshrc` - `playbooks/development.yml` and `playbooks/workstations.yml` use `shell_mode: full` - `playbooks/servers.yml` remains **aliases-only** - **Applications** - Applications role runs only on `desktop` group (via `workstations.yml`) - Removed Brave installs/repo management - Added **CopyQ** to desktop apps (`applications_desktop_packages`) - **Docs + architecture** - Added canonical doc tree under `project-docs/` (overview/architecture/standards/workflow/decisions) - Consolidated architecture docs: `docs/reference/architecture.md` is now a pointer to `project-docs/architecture.md` - Fixed broken doc links by adding the missing referenced pages under `docs/` ### Behavior changes (important) - Desktop GUI apps install **only** on the `desktop` inventory group (not on servers, not on dev VMs unless they are in `desktop`). - Dev/workstation Zsh is now provisioned in **full mode** (managed `.zshrc` + p10k). ### How to test (local CI parity) ```bash make test npm test ``` Optional dry runs (interactive sudo may be required): ```bash make check make check-local ``` ### Rollout guidance - Apply to a single host first: - Workstations: `make workstations HOST=` - Servers: `make servers HOST=` - Then expand to group runs. Reviewed-on: https://git.levkin.ca/ilia/ansible/pulls/4 --- .ansible-lint | 11 +- .cursor/rules/project-rules.mdc | 33 + .gitea/workflows/ci.yml | 200 +- Makefile | 31 +- 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 | 65 +- docs/reference/architecture.md | 261 +-- docs/reference/makefile.md | 22 +- docs/reference/network.md | 21 + docs/reference/playbooks-and-tags.md | 281 +++ docs/reference/security.md | 28 + inventories/production/host_vars/devGPU.yml | 10 +- .../production/host_vars/devGPU/vault.yml | 2 - package-lock.json | 316 +-- package.json | 4 +- playbooks/app/configure_app.yml | 21 +- 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 | 224 +-- roles/docker/tasks/main.yml | 11 - roles/docker/tasks/setup_repo_linux_mint.yml | 29 +- 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 - 75 files changed, 1933 insertions(+), 3179 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 delete mode 100644 inventories/production/host_vars/devGPU/vault.yml 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..71e8932 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -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}..." @@ -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: @@ -100,6 +96,30 @@ 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 = /workspace/ilia/ansible/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" + - name: Install Python and dependencies run: | apt-get update && apt-get install -y python3 python3-pip @@ -107,6 +127,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 +139,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 @@ -154,8 +175,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 @@ -198,11 +256,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;Python-2.0;BlueOak-1.0.0;0BSD' else echo "No package.json found, skipping license check" fi - continue-on-error: true vault-check: needs: skip-ci-check @@ -230,7 +289,12 @@ jobs: - name: Validate vault files are encrypted run: | echo "Checking for Ansible Vault files..." - vault_files=$(find . -name "*vault*.yml" -o -name "*vault*.yaml" | grep -v ".git" | grep -v ".example" || true) + # Intentionally skip *.example files: they are plaintext templates. + # 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 @@ -239,7 +303,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!" @@ -268,6 +333,48 @@ 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' + [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 + + cat > /tmp/ci-ansible.cfg <<'EOF' + [defaults] + inventory = /tmp/ci-inventory.ini + roles_path = /workspace/ilia/ansible/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" + - name: Install Python and dependencies run: | apt-get update && apt-get install -y python3 python3-pip @@ -275,29 +382,33 @@ 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)..." 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 @@ -373,7 +484,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 @@ -381,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 @@ -467,9 +584,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}" @@ -477,12 +593,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=. \ @@ -503,7 +624,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 diff --git a/Makefile b/Makefile index b427cfb..e578222 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 @@ -33,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 @@ -251,6 +254,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 +423,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..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,18 +50,15 @@ 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 | -| 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 | -|---------|-------------|--------|------| +| --------- | ------------- | -------- | ------ | | htop | Process viewer | apt | monitoring | | btop | Modern system monitor | snap | monitoring | | iotop | I/O monitor | apt | monitoring | @@ -77,23 +74,41 @@ 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 | -|---------|-------------|--------|------| -| 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: @@ -130,18 +148,17 @@ 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 | | tailscale | Tailscale repository | Tailscale GPG key + repo | -| brave | Brave browser repository | Brave GPG key + repo | | git | Git repositories | Direct clone | ## Services Enabled | Service | Description | Management | -|---------|-------------|------------| +| --------- | ------------- | ------------ | | docker | Container runtime | systemctl | | tailscaled | VPN daemon | systemctl | | ufw | Firewall | systemctl | 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/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` | 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..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 @@ -22,10 +20,4 @@ jupyter_bind_all_interfaces: true # R configuration install_r: true -# Cursor IDE configuration -install_cursor_extensions: true - -# Cursor extension groups to enable -install_python: true # Python development - -install_docs: true # Markdown/documentation +# IDE/editor tooling is intentionally not managed by Ansible in this repo. diff --git a/inventories/production/host_vars/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 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 +} diff --git a/playbooks/app/configure_app.yml b/playbooks/app/configure_app.yml index 8891b58..974ad51 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 @@ -115,20 +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' - - 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..28ee38f 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,18 +64,13 @@ 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: | # 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 @@ -97,13 +79,12 @@ echo "ERROR: Key file is not valid PGP format" exit 1 fi + args: + creates: /etc/apt/keyrings/nodesource.gpg 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/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"] 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 - -