refactor-servers-workstations-shell-monitoring (#4)
All checks were successful
CI / skip-ci-check (push) Successful in 1m18s
CI / lint-and-test (push) Successful in 1m23s
CI / ansible-validation (push) Successful in 3m2s
CI / secret-scanning (push) Successful in 1m19s
CI / dependency-scan (push) Successful in 1m24s
CI / sast-scan (push) Successful in 2m32s
CI / license-check (push) Successful in 1m23s
CI / vault-check (push) Successful in 2m22s
CI / playbook-test (push) Successful in 2m25s
CI / container-scan (push) Successful in 1m51s
CI / sonar-analysis (push) Successful in 2m32s
CI / workflow-summary (push) Successful in 1m17s
All checks were successful
CI / skip-ci-check (push) Successful in 1m18s
CI / lint-and-test (push) Successful in 1m23s
CI / ansible-validation (push) Successful in 3m2s
CI / secret-scanning (push) Successful in 1m19s
CI / dependency-scan (push) Successful in 1m24s
CI / sast-scan (push) Successful in 2m32s
CI / license-check (push) Successful in 1m23s
CI / vault-check (push) Successful in 2m22s
CI / playbook-test (push) Successful in 2m25s
CI / container-scan (push) Successful in 1m51s
CI / sonar-analysis (push) Successful in 2m32s
CI / workflow-summary (push) Successful in 1m17s
### 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=<devhost>`
- Servers: `make servers HOST=<serverhost>`
- Then expand to group runs.
Reviewed-on: #4
This commit is contained in:
parent
69a39e5e5b
commit
c3e6caf9e8
@ -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/
|
||||
|
||||
33
.cursor/rules/project-rules.mdc
Normal file
33
.cursor/rules/project-rules.mdc
Normal file
@ -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).
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
31
Makefile
31
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])
|
||||
|
||||
@ -78,3 +78,4 @@ ansible/
|
||||
|
||||
- **Guides**: `docs/guides/`
|
||||
- **Reference**: `docs/reference/`
|
||||
- **Project docs (architecture/standards/workflow)**: `project-docs/index.md`
|
||||
@ -5,5 +5,3 @@
|
||||
|
||||
- name: Configure app project guests
|
||||
import_playbook: playbooks/app/configure_app.yml
|
||||
|
||||
|
||||
|
||||
28
docs/guides/custom-roles.md
Normal file
28
docs/guides/custom-roles.md
Normal file
@ -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/<role_name>/
|
||||
├── 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 <feature>` targets can use `--tags`.
|
||||
|
||||
## Standards (canonical)
|
||||
|
||||
- `project-docs/standards.md`
|
||||
- `project-docs/decisions.md` (add an ADR entry for significant changes)
|
||||
|
||||
22
docs/guides/monitoring.md
Normal file
22
docs/guides/monitoring.md
Normal file
@ -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`).
|
||||
|
||||
31
docs/guides/security.md
Normal file
31
docs/guides/security.md
Normal file
@ -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`
|
||||
|
||||
13
docs/project-docs.md
Normal file
13
docs/project-docs.md
Normal file
@ -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.
|
||||
|
||||
@ -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 |
|
||||
|
||||
@ -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/<hostname>.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
|
||||
|
||||
|
||||
@ -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` |
|
||||
|
||||
21
docs/reference/network.md
Normal file
21
docs/reference/network.md
Normal file
@ -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`
|
||||
|
||||
281
docs/reference/playbooks-and-tags.md
Normal file
281
docs/reference/playbooks-and-tags.md
Normal file
@ -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.
|
||||
|
||||
28
docs/reference/security.md
Normal file
28
docs/reference/security.md
Normal file
@ -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`
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
---
|
||||
vault_devgpu_become_password: root
|
||||
316
package-lock.json
generated
316
package-lock.json
generated
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
|
||||
@ -233,3 +233,5 @@
|
||||
ansible_user: root
|
||||
app_project: "{{ project_key }}"
|
||||
app_env: "{{ env_name }}"
|
||||
|
||||
# EOF
|
||||
|
||||
@ -19,3 +19,5 @@
|
||||
loop: "{{ project_def.envs | dict2items }}"
|
||||
loop_control:
|
||||
loop_var: env_item
|
||||
|
||||
# EOF
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
tags: ['app', 'proxmox', 'info']
|
||||
vars:
|
||||
selected_projects: >-
|
||||
{{
|
||||
@ -95,5 +96,3 @@
|
||||
{% 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 %}
|
||||
|
||||
|
||||
|
||||
@ -11,5 +11,3 @@
|
||||
- name: Configure guests
|
||||
import_playbook: configure_app.yml
|
||||
tags: ['app', 'configure']
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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']}
|
||||
|
||||
|
||||
27
playbooks/servers.yml
Normal file
27
playbooks/servers.yml
Normal file
@ -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!"
|
||||
@ -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:
|
||||
|
||||
@ -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']}
|
||||
|
||||
|
||||
42
playbooks/workstations.yml
Normal file
42
playbooks/workstations.yml
Normal file
@ -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!"
|
||||
63
project-docs/architecture.md
Normal file
63
project-docs/architecture.md
Normal file
@ -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_<project>_all`
|
||||
- `app_<project>_<env>`
|
||||
|
||||
- **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`
|
||||
|
||||
35
project-docs/decisions.md
Normal file
35
project-docs/decisions.md
Normal file
@ -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
|
||||
|
||||
33
project-docs/index.md
Normal file
33
project-docs/index.md
Normal file
@ -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`
|
||||
|
||||
27
project-docs/overview.md
Normal file
27
project-docs/overview.md
Normal file
@ -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
|
||||
|
||||
49
project-docs/standards.md
Normal file
49
project-docs/standards.md
Normal file
@ -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/<role_name>/
|
||||
├── 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.
|
||||
|
||||
86
project-docs/workflow.md
Normal file
86
project-docs/workflow.md
Normal file
@ -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
|
||||
```
|
||||
|
||||
@ -5,5 +5,3 @@
|
||||
|
||||
- name: Provision app project guests
|
||||
import_playbook: playbooks/app/provision_vms.yml
|
||||
|
||||
|
||||
|
||||
@ -37,4 +37,3 @@ app_frontend_start_cmd: "npm start"
|
||||
|
||||
# Arbitrary environment variables for the env file
|
||||
app_env_vars: {}
|
||||
|
||||
|
||||
@ -4,5 +4,3 @@
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
|
||||
|
||||
@ -80,5 +80,3 @@
|
||||
enabled: true
|
||||
state: started
|
||||
when: app_enable_frontend | bool
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
@ -1 +1,7 @@
|
||||
---
|
||||
|
||||
# Desktop GUI applications to install (desktop group only via playbooks/workstations.yml)
|
||||
applications_desktop_packages:
|
||||
- copyq
|
||||
- evince
|
||||
- redshift
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -4,5 +4,3 @@
|
||||
- name: Reload ufw
|
||||
ansible.builtin.command: ufw reload
|
||||
changed_when: false
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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 <package>
|
||||
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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"]
|
||||
|
||||
5
roles/monitoring_desktop/defaults/main.yml
Normal file
5
roles/monitoring_desktop/defaults/main.yml
Normal file
@ -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
|
||||
131
roles/monitoring_desktop/tasks/main.yml
Normal file
131
roles/monitoring_desktop/tasks/main.yml
Normal file
@ -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
|
||||
5
roles/monitoring_server/defaults/main.yml
Normal file
5
roles/monitoring_server/defaults/main.yml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
# Monitoring (server) role defaults
|
||||
monitoring_server_install_btop: true
|
||||
monitoring_server_enable_sysstat: true
|
||||
monitoring_server_create_scripts: true
|
||||
11
roles/monitoring_server/handlers/main.yml
Normal file
11
roles/monitoring_server/handlers/main.yml
Normal file
@ -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
|
||||
148
roles/monitoring_server/tasks/main.yml
Normal file
148
roles/monitoring_server/tasks/main.yml
Normal file
@ -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
|
||||
34
roles/monitoring_server/templates/jail.local.j2
Normal file
34
roles/monitoring_server/templates/jail.local.j2
Normal file
@ -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
|
||||
|
||||
@ -78,5 +78,3 @@
|
||||
Storage: {{ vm_storage }}:{{ vm_disk_size }}
|
||||
Network: {{ vm_network_bridge }}
|
||||
Status: {{ vm_creation_result.msg | default('Created') }}
|
||||
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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 <<<
|
||||
|
||||
38
roles/shell/files/ansible_aliases.zsh
Normal file
38
roles/shell/files/ansible_aliases.zsh
Normal file
@ -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"
|
||||
|
||||
19
roles/shell/files/p10k.zsh
Normal file
19
roles/shell/files/p10k.zsh
Normal file
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
42
roles/shell/files/zshrc.full
Normal file
42
roles/shell/files/zshrc.full
Normal file
@ -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
|
||||
|
||||
@ -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"
|
||||
- "=========================================="
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
---
|
||||
- name: Install shell packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- zsh
|
||||
- tmux
|
||||
- fzf
|
||||
- git
|
||||
name: "{{ shell_packages }}"
|
||||
state: present
|
||||
become: true
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user