Update Makefile and inventory configurations for improved task execution and organization
- Refactor Makefile to enhance command structure, including clearer descriptions and usage examples for targets related to development, inventory, and monitoring tasks. - Update inventory files to ensure correct host configurations and user settings, including adjustments to ansible_user for specific hosts. - Modify group_vars to streamline Tailscale configuration and ensure proper handling of authentication keys. These changes improve the clarity and usability of the Makefile and inventory setup, facilitating smoother operations across the infrastructure.
This commit is contained in:
parent
e05b3aa0d5
commit
579f0709ce
42
Makefile
42
Makefile
@ -1,4 +1,4 @@
|
||||
.PHONY: help bootstrap lint test check apply dev local clean status tailscale tailscale-check tailscale-dev tailscale-status create-vault create-vm monitoring
|
||||
.PHONY: help bootstrap lint test check dev datascience inventory inventory-all local clean status tailscale tailscale-check tailscale-dev tailscale-status create-vault create-vm monitoring
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
## Colors for output
|
||||
@ -226,10 +226,6 @@ check-local: ## Dry-run the local playbook
|
||||
@echo "$(YELLOW)Running dry-run on localhost...$(RESET)"
|
||||
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_LOCAL) --check --diff -K
|
||||
|
||||
apply: auto-fallback ## Run the development playbook on all dev hosts
|
||||
@echo "$(YELLOW)Applying development playbook...$(RESET)"
|
||||
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV)
|
||||
|
||||
site: ## Run the complete site playbook
|
||||
@echo "$(YELLOW)Running complete site deployment...$(RESET)"
|
||||
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_SITE)
|
||||
@ -248,6 +244,42 @@ endif
|
||||
@echo "$(YELLOW)Running on host: $(HOST)$(RESET)"
|
||||
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --limit $(HOST)
|
||||
|
||||
# Data science role
|
||||
datascience: ## Install data science stack (usage: make datascience HOST=server01)
|
||||
ifndef HOST
|
||||
@echo "$(RED)Error: HOST parameter required$(RESET)"
|
||||
@echo "Usage: make datascience HOST=server01"
|
||||
@exit 1
|
||||
endif
|
||||
@echo "$(YELLOW)Installing data science stack on: $(HOST)$(RESET)"
|
||||
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --limit $(HOST) --tags datascience
|
||||
|
||||
# Inventory system
|
||||
inventory: ## Show installed software on host (usage: make inventory HOST=dev01)
|
||||
ifndef HOST
|
||||
@echo "$(RED)Error: HOST parameter required$(RESET)"
|
||||
@echo "Usage: make inventory HOST=dev01"
|
||||
@exit 1
|
||||
endif
|
||||
@echo "$(BOLD)Software Inventory for: $(HOST)$(RESET)"
|
||||
@echo ""
|
||||
@ip=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_host // empty" 2>/dev/null); \
|
||||
user=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_user // empty" 2>/dev/null); \
|
||||
if [ -n "$$ip" ] && [ "$$ip" != "null" ] && [ -n "$$user" ] && [ "$$user" != "null" ]; then \
|
||||
scp -q scripts/inventory.sh $$user@$$ip:/tmp/inventory.sh 2>/dev/null && \
|
||||
ssh $$user@$$ip 'bash /tmp/inventory.sh; rm /tmp/inventory.sh' 2>/dev/null; \
|
||||
else \
|
||||
echo "$(RED)Could not determine IP or user for $(HOST)$(RESET)"; \
|
||||
fi
|
||||
|
||||
inventory-all: ## Show installed software on all hosts
|
||||
@echo "$(BOLD)Software Inventory - All Hosts$(RESET)\n"
|
||||
@for host in $$(ansible all --list-hosts 2>/dev/null | grep -v "hosts" | tr -d ' '); do \
|
||||
echo "$(YELLOW)=== $$host ===$(RESET)"; \
|
||||
ansible $$host -m script -a "scripts/inventory.sh" 2>/dev/null | sed -n '/CHANGED/,$$p' | tail -n +3 || echo "$(RED)Failed to connect$(RESET)"; \
|
||||
echo ""; \
|
||||
done
|
||||
|
||||
# Tag-based execution
|
||||
security: ## Run only security-related roles
|
||||
@echo "$(YELLOW)Running security roles...$(RESET)"
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
---
|
||||
# Common variables for all hosts
|
||||
timezone: America/Toronto
|
||||
locale: en_US.UTF-8
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
# Debug settings
|
||||
ansible_debug_output: false
|
||||
|
||||
# Security settings
|
||||
fail2ban_bantime: 3600
|
||||
fail2ban_findtime: 600
|
||||
fail2ban_maxretry: 3
|
||||
|
||||
# Maintenance settings
|
||||
maintenance_default_serial: "100%" # Default serial execution for maintenance
|
||||
maintenance_reboot_timeout: 300 # Reboot timeout in seconds
|
||||
maintenance_pre_reboot_delay: 5 # Delay before reboot in seconds
|
||||
|
||||
# Global variables for all hosts
|
||||
|
||||
# Tailscale configuration
|
||||
# Store your actual auth key in vault_tailscale_auth_key using ansible-vault
|
||||
# Example: ansible-vault create group_vars/all/vault.yml
|
||||
# vault_tailscale_auth_key: "tskey-auth-your-actual-key-here"
|
||||
|
||||
# Default Tailscale settings - these tell the playbook to use your vault key
|
||||
tailscale_auth_key: "{{ vault_tailscale_auth_key | default('') }}"
|
||||
tailscale_accept_routes: true
|
||||
tailscale_accept_dns: true
|
||||
tailscale_ssh: true
|
||||
tailscale_hostname: "{{ inventory_hostname }}"
|
||||
@ -28,5 +28,5 @@ maintenance_pre_reboot_delay: 5 # Delay before reboot in seconds
|
||||
tailscale_auth_key: "{{ vault_tailscale_auth_key | default('') }}"
|
||||
tailscale_accept_routes: true
|
||||
tailscale_accept_dns: true
|
||||
tailscale_ssh: true
|
||||
tailscale_ssh: false
|
||||
tailscale_hostname: "{{ inventory_hostname }}"
|
||||
|
||||
31
inventories/production/host_vars/devGPU.yml
Normal file
31
inventories/production/host_vars/devGPU.yml
Normal file
@ -0,0 +1,31 @@
|
||||
ansible_become_password: root
|
||||
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
# Shell configuration
|
||||
# ansible_user (root) is configured by default
|
||||
# Add additional users here if needed:
|
||||
shell_additional_users:
|
||||
- devuser01
|
||||
- devuser02
|
||||
- dev
|
||||
|
||||
# Data Science configuration (datascience role)
|
||||
install_conda: true
|
||||
conda_install_path: "/root/anaconda3"
|
||||
install_jupyter: true
|
||||
jupyter_port: 8888
|
||||
jupyter_allow_remote: true
|
||||
jupyter_bind_all_interfaces: true
|
||||
|
||||
# R configuration
|
||||
install_r: true
|
||||
|
||||
# Cursor IDE configuration
|
||||
install_cursor_extensions: true
|
||||
|
||||
# Cursor extension groups to enable
|
||||
install_python: true # Python development
|
||||
install_jupyter: true # Jupyter notebooks
|
||||
install_r: true # R language support
|
||||
install_docs: true # Markdown/documentation
|
||||
@ -13,13 +13,13 @@ portainerVM ansible_host=10.0.30.69 ansible_user=ladmin
|
||||
homepageVM ansible_host=10.0.30.12 ansible_user=homepage
|
||||
|
||||
[vaultwarden]
|
||||
vaultwardenVM ansible_host=100.100.19.11 ansible_host_fallback=10.0.10.142 ansible_user=ladmin
|
||||
vaultwardenVM ansible_host=100.100.19.11 ansible_host_fallback=10.0.10.142 ansible_user=root
|
||||
|
||||
[dev]
|
||||
dev01 ansible_host=10.0.30.105 ansible_user=ladmin
|
||||
bottom ansible_host=10.0.10.156 ansible_user=beast
|
||||
debianDesktopVM ansible_host=10.0.10.206 ansible_user=user skip_reboot=true
|
||||
|
||||
devGPU ansible_host=10.0.30.63 ansible_user=root
|
||||
|
||||
[ansible]
|
||||
ansibleVM ansible_host=10.0.10.157 ansible_user=master
|
||||
@ -28,8 +28,8 @@ ansibleVM ansible_host=10.0.10.157 ansible_user=master
|
||||
tailscaleVM ansible_host=100.66.218.53 ansible_user=ladmin
|
||||
|
||||
[services]
|
||||
caddy ansible_host=100.117.106.18 ansible_host_fallback=10.0.10.50 ansible_user=ladmin
|
||||
jellyfin ansible_host=100.104.109.45 ansible_host_fallback=10.0.10.232 ansible_user=user
|
||||
caddy ansible_host=100.117.106.18 ansible_host_fallback=10.0.10.50 ansible_user=root
|
||||
jellyfin ansible_host=100.104.109.45 ansible_host_fallback=10.0.10.232 ansible_user=root
|
||||
listmonk ansible_host=100.73.190.115 ansible_host_fallback=10.0.10.149 ansible_user=ladmin
|
||||
slack ansible_host=100.110.190.69 ansible_host_fallback=10.0.10.154 ansible_user=ladmin
|
||||
|
||||
|
||||
@ -11,10 +11,10 @@
|
||||
- {role: ssh, tags: ['ssh', 'security']}
|
||||
- {role: shell, tags: ['shell']}
|
||||
- {role: development, tags: ['development', 'dev']}
|
||||
- {role: datascience, tags: ['datascience', 'conda', 'jupyter', 'r']}
|
||||
- {role: docker, tags: ['docker']}
|
||||
- {role: applications, tags: ['applications', 'apps']}
|
||||
- {role: snap, tags: ['snap', 'apps']}
|
||||
- {role: tailscale, tags: ['tailscale', 'vpn']}
|
||||
# - {role: tailscale, tags: ['tailscale', 'vpn']}
|
||||
- {role: monitoring, tags: ['monitoring']}
|
||||
|
||||
pre_tasks:
|
||||
|
||||
@ -13,8 +13,7 @@
|
||||
- {role: development, tags: ['development', 'dev']}
|
||||
- {role: docker, tags: ['docker']}
|
||||
- {role: applications, tags: ['applications', 'apps']}
|
||||
- {role: snap, tags: ['snap', 'apps']}
|
||||
- {role: tailscale, tags: ['tailscale', 'vpn']}
|
||||
# - {role: tailscale, tags: ['tailscale', 'vpn']}
|
||||
- {role: monitoring, tags: ['monitoring']}
|
||||
|
||||
pre_tasks:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
# Playbook: shell.yml
|
||||
# Purpose: Configure shell environment (zsh, oh-my-zsh, plugins) on all hosts
|
||||
# Purpose: Configure shell environment (zsh, oh-my-zsh, plugins)
|
||||
# Targets: all hosts
|
||||
# Tags: shell
|
||||
# Usage: make shell-all
|
||||
@ -9,6 +9,8 @@
|
||||
hosts: all
|
||||
become: true
|
||||
strategy: free
|
||||
ignore_errors: true
|
||||
ignore_unreachable: true
|
||||
|
||||
roles:
|
||||
- {role: shell, tags: ['shell']}
|
||||
|
||||
@ -1,38 +1,64 @@
|
||||
Role Name
|
||||
=========
|
||||
# Role: base
|
||||
|
||||
A brief description of the role goes here.
|
||||
## Description
|
||||
Installs core system packages and utilities required by all other roles. This is the foundation role that should be applied to all managed hosts.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
## Requirements
|
||||
- Ansible 2.9+
|
||||
- Debian/Ubuntu systems
|
||||
- Root or sudo access
|
||||
|
||||
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||
## Installed Packages
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
### Base Utilities
|
||||
- curl, wget - Download tools
|
||||
- unzip - Archive extraction
|
||||
- xclip - Clipboard utility
|
||||
- tree - Directory visualization
|
||||
|
||||
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||
### Network & Admin Tools
|
||||
- net-tools - Network configuration
|
||||
- ufw - Uncomplicated Firewall
|
||||
- mailutils - Email utilities
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
### Modern CLI Tools
|
||||
- jq - JSON processor
|
||||
- yq - YAML processor (from apt or GitHub binary)
|
||||
- ripgrep - Fast text search
|
||||
- fd-find - Fast file finder
|
||||
|
||||
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||
## Configuration Tasks
|
||||
- Sets system timezone (default: UTC)
|
||||
- Configures system locale (default: en_US.UTF-8)
|
||||
- Creates Ansible temporary directory with proper permissions
|
||||
- Creates fd symlink for Ubuntu compatibility
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
## Variables
|
||||
|
||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `timezone` | `UTC` | System timezone |
|
||||
| `locale` | `en_US.UTF-8` | System locale |
|
||||
|
||||
- hosts: servers
|
||||
roles:
|
||||
- { role: username.rolename, x: 42 }
|
||||
## Dependencies
|
||||
None - this is the foundation role.
|
||||
|
||||
License
|
||||
-------
|
||||
## Example Playbook
|
||||
|
||||
BSD
|
||||
```yaml
|
||||
- hosts: all
|
||||
roles:
|
||||
- role: base
|
||||
timezone: America/Toronto
|
||||
locale: en_US.UTF-8
|
||||
```
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
## Tags
|
||||
- `base`: All base tasks
|
||||
- `security`: Security-related tasks
|
||||
|
||||
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
||||
## Notes
|
||||
- This role should be applied before all other roles
|
||||
- yq is installed from apt if available, otherwise from GitHub binary
|
||||
- fd-find is symlinked to fd for compatibility across distributions
|
||||
- UFW firewall is installed but not enabled (handled by SSH role)
|
||||
|
||||
@ -27,11 +27,27 @@
|
||||
- fd-find
|
||||
state: present
|
||||
|
||||
- name: Install modern tools via snap
|
||||
community.general.snap:
|
||||
name:
|
||||
- yq
|
||||
- name: Install yq YAML processor
|
||||
ansible.builtin.apt:
|
||||
name: yq
|
||||
state: present
|
||||
update_cache: false
|
||||
failed_when: false
|
||||
register: yq_apt_install
|
||||
|
||||
- name: Install yq from binary if apt fails
|
||||
when: yq_apt_install.failed or yq_apt_install is not succeeded
|
||||
block:
|
||||
- name: Download yq binary
|
||||
ansible.builtin.get_url:
|
||||
url: https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
|
||||
dest: /usr/local/bin/yq
|
||||
mode: '0755'
|
||||
register: yq_download
|
||||
|
||||
- name: Verify yq installation
|
||||
ansible.builtin.command: yq --version
|
||||
changed_when: false
|
||||
|
||||
- name: Create fd symlink (Ubuntu uses fd-find)
|
||||
ansible.builtin.file:
|
||||
|
||||
185
roles/datascience/README.md
Normal file
185
roles/datascience/README.md
Normal file
@ -0,0 +1,185 @@
|
||||
# Role: datascience
|
||||
|
||||
## Description
|
||||
Installs and configures a complete data science environment including Anaconda/Conda, Jupyter Notebook, and R statistical computing language. This role is optional and separate from general development tools for faster deployments when data science capabilities are not needed.
|
||||
|
||||
## Requirements
|
||||
- Ansible 2.9+
|
||||
- Debian/Ubuntu systems
|
||||
- At least 5GB free disk space (Anaconda is ~700MB)
|
||||
- Root or sudo access
|
||||
|
||||
## Installed Components
|
||||
|
||||
### Anaconda/Conda
|
||||
- Full Anaconda3 distribution (latest version)
|
||||
- Python data science packages (pandas, numpy, matplotlib, scikit-learn)
|
||||
- Conda package manager
|
||||
- Initialized for bash (zsh initialization via shell role)
|
||||
|
||||
### Jupyter Notebook
|
||||
- Jupyter Notebook server
|
||||
- IPython kernel
|
||||
- JupyterLab interface
|
||||
- Systemd service for automatic startup
|
||||
- Web-based access on configurable port
|
||||
|
||||
### R Language
|
||||
- R base and development packages
|
||||
- R recommended packages
|
||||
- CRAN repository configuration
|
||||
- IRkernel for Jupyter integration
|
||||
- Common R packages
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `install_conda` | `false` | Install Anaconda/Conda |
|
||||
| `conda_install_path` | `{{ ansible_env.HOME }}/anaconda3` | Conda installation directory |
|
||||
| `install_jupyter` | `false` | Install Jupyter Notebook (requires conda) |
|
||||
| `jupyter_port` | `8888` | Jupyter web server port |
|
||||
| `jupyter_bind_all_interfaces` | `true` | Listen on all network interfaces |
|
||||
| `jupyter_allow_remote` | `true` | Allow remote connections |
|
||||
| `install_r` | `false` | Install R language |
|
||||
| `r_packages` | `[r-base, r-base-dev, r-recommended]` | R packages to install |
|
||||
|
||||
## Dependencies
|
||||
- `base` role (for core utilities)
|
||||
|
||||
## Example Playbook
|
||||
|
||||
### Full Data Science Stack
|
||||
```yaml
|
||||
- hosts: datascience_servers
|
||||
roles:
|
||||
- role: datascience
|
||||
install_conda: true
|
||||
install_jupyter: true
|
||||
install_r: true
|
||||
```
|
||||
|
||||
### Conda + Jupyter Only
|
||||
```yaml
|
||||
- hosts: jupyter_servers
|
||||
roles:
|
||||
- role: datascience
|
||||
install_conda: true
|
||||
install_jupyter: true
|
||||
install_r: false
|
||||
```
|
||||
|
||||
### R Only (no Python)
|
||||
```yaml
|
||||
- hosts: r_servers
|
||||
roles:
|
||||
- role: datascience
|
||||
install_conda: false
|
||||
install_r: true
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
```bash
|
||||
# Install full data science stack
|
||||
make datascience HOST=server01
|
||||
|
||||
# Install on specific host with custom vars
|
||||
ansible-playbook playbooks/development.yml --limit server01 --tags datascience \
|
||||
-e "install_conda=true install_jupyter=true install_r=true"
|
||||
```
|
||||
|
||||
### Post-Installation
|
||||
|
||||
#### Set Jupyter Password
|
||||
```bash
|
||||
jupyter notebook password
|
||||
```
|
||||
|
||||
#### Access Jupyter
|
||||
```bash
|
||||
# Local
|
||||
http://localhost:8888
|
||||
|
||||
# Remote
|
||||
http://your-server-ip:8888
|
||||
```
|
||||
|
||||
#### Verify Installations
|
||||
```bash
|
||||
conda --version
|
||||
jupyter --version
|
||||
R --version
|
||||
```
|
||||
|
||||
## Tags
|
||||
- `datascience`: All data science tasks
|
||||
- `conda`: Conda/Anaconda installation only
|
||||
- `jupyter`: Jupyter Notebook installation only
|
||||
- `r`, `rstats`: R language installation only
|
||||
|
||||
## Services
|
||||
|
||||
### Jupyter Notebook Service
|
||||
- **Service name**: `jupyter-notebook.service`
|
||||
- **Start**: `systemctl start jupyter-notebook`
|
||||
- **Status**: `systemctl status jupyter-notebook`
|
||||
- **Logs**: `journalctl -u jupyter-notebook`
|
||||
|
||||
## Performance Notes
|
||||
|
||||
### Installation Times
|
||||
- **Anaconda**: 5-10 minutes (700MB download)
|
||||
- **Jupyter**: 2-5 minutes (if Anaconda already installed)
|
||||
- **R**: 10-30 minutes (compilation of packages)
|
||||
|
||||
### Disk Space
|
||||
- **Anaconda**: ~2.5GB after installation
|
||||
- **R + packages**: ~500MB
|
||||
- **Total**: ~3GB for full stack
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Jupyter Password**: Always set a password after installation
|
||||
2. **Firewall**: Ensure port 8888 (or custom port) is properly firewalled
|
||||
3. **HTTPS**: Consider using HTTPS for remote access
|
||||
4. **User Isolation**: Jupyter runs as the ansible user by default
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Conda Not in PATH
|
||||
```bash
|
||||
# Add to .bashrc or .zshrc
|
||||
export PATH="$HOME/anaconda3/bin:$PATH"
|
||||
```
|
||||
|
||||
### Jupyter Service Won't Start
|
||||
```bash
|
||||
# Check logs
|
||||
journalctl -u jupyter-notebook -n 50
|
||||
|
||||
# Verify conda is accessible
|
||||
/root/anaconda3/bin/jupyter --version
|
||||
```
|
||||
|
||||
### R Package Installation Fails
|
||||
```bash
|
||||
# Install manually in R
|
||||
R
|
||||
> install.packages("IRkernel")
|
||||
```
|
||||
|
||||
## Integration with Other Roles
|
||||
|
||||
- **Shell Role**: Provides zsh with conda integration
|
||||
- **Monitoring**: btop/htop for resource monitoring
|
||||
- **Docker**: Can run Jupyter in containers alternatively
|
||||
|
||||
## Notes
|
||||
- Anaconda installer is cleaned up after installation
|
||||
- Conda init for zsh is handled by the shell role
|
||||
- IRkernel is automatically installed if both Jupyter and R are enabled
|
||||
- R packages are compiled during installation (can be slow)
|
||||
- Jupyter service starts on boot automatically
|
||||
|
||||
20
roles/datascience/defaults/main.yml
Normal file
20
roles/datascience/defaults/main.yml
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
# Data Science role defaults
|
||||
|
||||
# Conda/Anaconda configuration
|
||||
install_conda: false
|
||||
conda_install_path: "{{ ansible_env.HOME }}/anaconda3"
|
||||
|
||||
# Jupyter Notebook configuration
|
||||
install_jupyter: false
|
||||
jupyter_port: 8888
|
||||
jupyter_allow_remote: true
|
||||
jupyter_bind_all_interfaces: true
|
||||
|
||||
# R language configuration
|
||||
install_r: false
|
||||
r_packages:
|
||||
- r-base
|
||||
- r-base-dev
|
||||
- r-recommended
|
||||
|
||||
8
roles/datascience/handlers/main.yml
Normal file
8
roles/datascience/handlers/main.yml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
- name: Restart jupyter-notebook
|
||||
ansible.builtin.systemd:
|
||||
name: jupyter-notebook
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
become: true
|
||||
|
||||
4
roles/datascience/meta/main.yml
Normal file
4
roles/datascience/meta/main.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
dependencies:
|
||||
- role: base
|
||||
|
||||
203
roles/datascience/tasks/main.yml
Normal file
203
roles/datascience/tasks/main.yml
Normal file
@ -0,0 +1,203 @@
|
||||
---
|
||||
# Conda/Anaconda installation
|
||||
- name: Conda installation block
|
||||
when: install_conda | default(false) | bool
|
||||
tags: ['conda']
|
||||
block:
|
||||
- name: Check if conda is installed
|
||||
ansible.builtin.stat:
|
||||
path: "{{ conda_install_path }}/bin/conda"
|
||||
register: conda_installed
|
||||
|
||||
- name: Download Anaconda installer
|
||||
ansible.builtin.get_url:
|
||||
url: https://repo.anaconda.com/archive/Anaconda3-2024.10-1-Linux-x86_64.sh
|
||||
dest: /tmp/anaconda_installer.sh
|
||||
mode: '0755'
|
||||
when: not conda_installed.stat.exists
|
||||
|
||||
- name: Install Anaconda
|
||||
ansible.builtin.shell: |
|
||||
bash /tmp/anaconda_installer.sh -b -p {{ conda_install_path }}
|
||||
args:
|
||||
creates: "{{ conda_install_path }}/bin/conda"
|
||||
when: not conda_installed.stat.exists
|
||||
|
||||
- name: Initialize conda for bash
|
||||
ansible.builtin.shell: |
|
||||
{{ conda_install_path }}/bin/conda init bash
|
||||
args:
|
||||
creates: "{{ ansible_env.HOME }}/.bashrc"
|
||||
when: not conda_installed.stat.exists
|
||||
failed_when: false
|
||||
|
||||
# Note: conda init zsh is skipped because conda initialization
|
||||
# is already included in the custom .zshrc deployed by the shell role
|
||||
# This prevents conda from overwriting our custom .zshrc configuration
|
||||
|
||||
- name: Clean up Anaconda installer
|
||||
ansible.builtin.file:
|
||||
path: /tmp/anaconda_installer.sh
|
||||
state: absent
|
||||
|
||||
- name: Verify conda installation
|
||||
ansible.builtin.command: "{{ conda_install_path }}/bin/conda --version"
|
||||
register: conda_version
|
||||
changed_when: false
|
||||
|
||||
- name: Display conda version
|
||||
ansible.builtin.debug:
|
||||
msg: "Conda version installed: {{ conda_version.stdout if conda_version.stdout is defined else 'Not checked in dry-run mode' }}"
|
||||
|
||||
# Jupyter Notebook installation
|
||||
- name: Jupyter Notebook installation block
|
||||
tags: ['jupyter']
|
||||
when:
|
||||
- install_conda | default(false) | bool
|
||||
- install_jupyter | default(false) | bool
|
||||
block:
|
||||
- name: Check if Jupyter is installed
|
||||
ansible.builtin.command: "{{ conda_install_path }}/bin/conda list jupyter"
|
||||
register: jupyter_installed
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Install Jupyter Notebook and common packages via conda
|
||||
ansible.builtin.shell: |
|
||||
{{ conda_install_path }}/bin/conda install -y jupyter notebook ipython pandas numpy matplotlib scikit-learn
|
||||
when: jupyter_installed.rc != 0 or 'jupyter' not in jupyter_installed.stdout
|
||||
changed_when: true
|
||||
|
||||
- name: Create Jupyter config directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ ansible_env.HOME }}/.jupyter"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Configure Jupyter Notebook
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
# Jupyter Notebook Configuration
|
||||
c.NotebookApp.ip = '{{ "0.0.0.0" if jupyter_bind_all_interfaces | default(true) | bool else "localhost" }}'
|
||||
c.NotebookApp.port = {{ jupyter_port | default(8888) }}
|
||||
c.NotebookApp.open_browser = False
|
||||
c.NotebookApp.allow_root = True
|
||||
# Note: For security, set a password with: jupyter notebook password
|
||||
dest: "{{ ansible_env.HOME }}/.jupyter/jupyter_notebook_config.py"
|
||||
mode: '0644'
|
||||
|
||||
- name: Create systemd service for Jupyter Notebook
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Jupyter Notebook Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ ansible_user_id }}
|
||||
WorkingDirectory={{ ansible_env.HOME }}
|
||||
ExecStart={{ conda_install_path }}/bin/jupyter notebook
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
dest: /etc/systemd/system/jupyter-notebook.service
|
||||
mode: '0644'
|
||||
become: true
|
||||
|
||||
- name: Enable and start Jupyter Notebook service
|
||||
ansible.builtin.systemd:
|
||||
name: jupyter-notebook
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
become: true
|
||||
|
||||
- name: Verify Jupyter installation
|
||||
ansible.builtin.command: "{{ conda_install_path }}/bin/jupyter --version"
|
||||
register: jupyter_version
|
||||
changed_when: false
|
||||
|
||||
- name: Display Jupyter installation info
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Jupyter version: {{ jupyter_version.stdout if jupyter_version.stdout is defined else 'Not checked in dry-run mode' }}"
|
||||
- "Access Jupyter at: http://{{ ansible_host }}:{{ jupyter_port | default(8888) }}"
|
||||
- "Set a password with: jupyter notebook password"
|
||||
|
||||
# R language installation
|
||||
- name: R language installation block
|
||||
when: install_r | default(false) | bool
|
||||
tags: ['r', 'rstats']
|
||||
block:
|
||||
- name: Install R dependencies
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- dirmngr
|
||||
- gnupg
|
||||
- apt-transport-https
|
||||
- ca-certificates
|
||||
- software-properties-common
|
||||
state: present
|
||||
update_cache: false
|
||||
become: true
|
||||
|
||||
- name: Fetch CRAN GPG key from keyserver
|
||||
ansible.builtin.shell: |
|
||||
gpg --keyserver keyserver.ubuntu.com --recv-key '95C0FAF38DB3CCAD0C080A7BDC78B2DDEABC47B7'
|
||||
gpg --armor --export '95C0FAF38DB3CCAD0C080A7BDC78B2DDEABC47B7' | tee /etc/apt/trusted.gpg.d/cran_debian_key.asc
|
||||
args:
|
||||
creates: /etc/apt/trusted.gpg.d/cran_debian_key.asc
|
||||
become: true
|
||||
|
||||
- name: Add CRAN repository
|
||||
ansible.builtin.apt_repository:
|
||||
repo: "deb https://cloud.r-project.org/bin/linux/debian {{ ansible_distribution_release }}-cran40/"
|
||||
state: present
|
||||
filename: cran
|
||||
update_cache: false
|
||||
become: true
|
||||
|
||||
- name: Update apt cache after adding CRAN
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
become: true
|
||||
retries: 2
|
||||
delay: 2
|
||||
|
||||
- name: Install R packages
|
||||
ansible.builtin.apt:
|
||||
name: "{{ r_packages }}"
|
||||
state: present
|
||||
become: true
|
||||
|
||||
- name: Install common R packages via R (non-interactive)
|
||||
ansible.builtin.shell: >
|
||||
R --quiet --no-save -e "install.packages(c('IRkernel'), repos='https://cloud.r-project.org', Ncpus=4)"
|
||||
environment:
|
||||
R_LIBS_USER: "/usr/local/lib/R/site-library"
|
||||
register: r_packages_install
|
||||
changed_when: "'DONE' in r_packages_install.stdout or r_packages_install.rc == 0"
|
||||
failed_when: false
|
||||
async: 3600
|
||||
poll: 30
|
||||
|
||||
- name: Install IRkernel for Jupyter
|
||||
ansible.builtin.command: >
|
||||
R -e "IRkernel::installspec(user = TRUE)"
|
||||
when: install_jupyter | default(false) | bool
|
||||
register: irkernel_install
|
||||
changed_when: "'✔' in irkernel_install.stdout or 'successfully' in irkernel_install.stdout.lower()"
|
||||
failed_when: false
|
||||
|
||||
- name: Verify R installation
|
||||
ansible.builtin.command: R --version
|
||||
register: r_version
|
||||
changed_when: false
|
||||
|
||||
- name: Display R version
|
||||
ansible.builtin.debug:
|
||||
msg: "R version installed: {{ r_version.stdout_lines[0] if r_version.stdout_lines | length > 0 else 'Not checked in dry-run mode' }}"
|
||||
|
||||
@ -1,38 +1,249 @@
|
||||
Role Name
|
||||
=========
|
||||
# Role: development
|
||||
|
||||
A brief description of the role goes here.
|
||||
## Description
|
||||
Installs core development tools and utilities for software development. This role provides a lightweight foundation for coding without heavy data science dependencies.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
**For data science tools (Anaconda, Jupyter, R), see the `datascience` role.**
|
||||
|
||||
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||
## Requirements
|
||||
- Ansible 2.9+
|
||||
- Debian/Ubuntu systems
|
||||
- Root or sudo access
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
## Installed Components
|
||||
|
||||
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||
### Development Tools
|
||||
- **git**: Version control system
|
||||
- **build-essential**: C/C++ compilation tools (gcc, g++, make)
|
||||
- **python3**: Python 3 interpreter
|
||||
- **python3-pip**: Python package manager
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
### Node.js
|
||||
- **Node.js 22.x**: Latest LTS from NodeSource
|
||||
- **npm**: Node package manager (included with Node.js)
|
||||
- Configured from official NodeSource repository
|
||||
|
||||
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||
### Code Editors
|
||||
- **Cursor IDE**: AI-powered code editor (AppImage)
|
||||
- Installed to `/usr/local/bin/cursor`
|
||||
- Latest stable version from cursor.com
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
## Variables
|
||||
|
||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||
### Core Settings
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `install_cursor` | `true` | Install Cursor IDE |
|
||||
| `install_cursor_extensions` | `false` | Install Cursor extensions |
|
||||
|
||||
- hosts: servers
|
||||
roles:
|
||||
- { role: username.rolename, x: 42 }
|
||||
### Extension Groups
|
||||
Enable specific extension groups based on your development needs:
|
||||
|
||||
License
|
||||
-------
|
||||
| Variable | Default | Extensions Included |
|
||||
|----------|---------|-------------------|
|
||||
| `install_python` | `false` | Python, Pylance, Black, isort, Flake8, Ruff |
|
||||
| `install_jupyter` | `false` | Jupyter notebooks, keybindings, renderers |
|
||||
| `install_web` | `false` | Prettier, ESLint, Tailwind, Vue, Svelte |
|
||||
| `install_playwright` | `false` | Playwright testing framework |
|
||||
| `install_devops` | `false` | Go, Rust, YAML, Docker, Ansible |
|
||||
| `install_r` | `false` | R language support and pack development |
|
||||
| `install_docs` | `false` | Markdown tools and linter |
|
||||
|
||||
BSD
|
||||
### Base Extensions (Always Installed)
|
||||
When `install_cursor_extensions: true`, these are always installed:
|
||||
- ErrorLens (better error highlighting)
|
||||
- GitLens (Git supercharged)
|
||||
- Git Graph (visualization)
|
||||
- Code Spell Checker
|
||||
- EditorConfig support
|
||||
- Material Icon Theme
|
||||
- GitHub Copilot (if licensed)
|
||||
- Copilot Chat
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
## Dependencies
|
||||
- `base` role (for core utilities)
|
||||
|
||||
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
||||
## Example Playbook
|
||||
|
||||
### Basic Installation
|
||||
```yaml
|
||||
- hosts: developers
|
||||
roles:
|
||||
- role: development
|
||||
```
|
||||
|
||||
### Python Data Science Machine
|
||||
```yaml
|
||||
- hosts: datascience
|
||||
roles:
|
||||
- role: development
|
||||
vars:
|
||||
install_cursor_extensions: true
|
||||
install_python: true
|
||||
install_jupyter: true
|
||||
install_docs: true
|
||||
```
|
||||
|
||||
### Web Development Machine
|
||||
```yaml
|
||||
- hosts: webdevs
|
||||
roles:
|
||||
- role: development
|
||||
vars:
|
||||
install_cursor_extensions: true
|
||||
install_web: true
|
||||
install_playwright: true
|
||||
install_docs: true
|
||||
```
|
||||
|
||||
### Full Stack with DevOps
|
||||
```yaml
|
||||
- hosts: fullstack
|
||||
roles:
|
||||
- role: development
|
||||
vars:
|
||||
install_cursor_extensions: true
|
||||
install_python: true
|
||||
install_web: true
|
||||
install_devops: true
|
||||
install_docs: true
|
||||
```
|
||||
|
||||
### Custom Extension List
|
||||
You can also override the extension list completely in `host_vars`:
|
||||
```yaml
|
||||
# host_vars/myhost.yml
|
||||
install_cursor_extensions: true
|
||||
cursor_extensions:
|
||||
- ms-python.python
|
||||
- golang.go
|
||||
- hashicorp.terraform
|
||||
# ... your custom list
|
||||
```
|
||||
|
||||
### With Cursor disabled
|
||||
```yaml
|
||||
- hosts: servers
|
||||
roles:
|
||||
- role: development
|
||||
install_cursor: false
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Install on specific host
|
||||
make dev HOST=dev01
|
||||
|
||||
# Install only development tools (skip other roles)
|
||||
ansible-playbook playbooks/development.yml --limit dev01 --tags development
|
||||
```
|
||||
|
||||
## Tags
|
||||
- `development`, `dev`: All development tasks
|
||||
- `cursor`, `ide`: Cursor IDE installation only
|
||||
|
||||
## Post-Installation
|
||||
|
||||
### Verify Installations
|
||||
```bash
|
||||
git --version
|
||||
node --version
|
||||
npm --version
|
||||
python3 --version
|
||||
cursor --version
|
||||
```
|
||||
|
||||
### Node.js Usage
|
||||
```bash
|
||||
# Install packages globally
|
||||
npm install -g <package>
|
||||
|
||||
# Check Node.js version
|
||||
node --version # Should show v22.x
|
||||
```
|
||||
|
||||
### Cursor IDE Usage
|
||||
```bash
|
||||
# Launch Cursor (if using X11/Wayland)
|
||||
cursor
|
||||
|
||||
# For root users, use the aliased version from .zshrc:
|
||||
cursor # Automatically adds --no-sandbox flags
|
||||
```
|
||||
|
||||
## Performance Notes
|
||||
|
||||
### Installation Time
|
||||
- **Base packages**: 1-2 minutes
|
||||
- **Node.js**: 1-2 minutes
|
||||
- **Cursor IDE**: 2-5 minutes (~200MB download)
|
||||
- **Total**: ~5-10 minutes
|
||||
|
||||
### Disk Space
|
||||
- **Node.js + npm**: ~100MB
|
||||
- **Cursor IDE**: ~200MB
|
||||
- **Build tools**: ~50MB
|
||||
- **Total**: ~350MB
|
||||
|
||||
## Integration
|
||||
|
||||
### With Data Science Role
|
||||
```yaml
|
||||
- hosts: datascience_workstation
|
||||
roles:
|
||||
- role: development # Core dev tools
|
||||
- role: datascience # Anaconda, Jupyter, R
|
||||
```
|
||||
|
||||
### With Docker
|
||||
```yaml
|
||||
- hosts: fullstack_dev
|
||||
roles:
|
||||
- role: development
|
||||
- role: docker
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Node.js Version Issues
|
||||
If Node.js doesn't upgrade to v22:
|
||||
```bash
|
||||
# Check current version
|
||||
node --version
|
||||
|
||||
# Force reinstall
|
||||
apt-get remove nodejs
|
||||
# Re-run playbook
|
||||
```
|
||||
|
||||
### Cursor Won't Launch
|
||||
For root users, use the alias that adds required flags:
|
||||
```bash
|
||||
# Check alias in .zshrc
|
||||
alias cursor="cursor --no-sandbox --disable-gpu-sandbox..."
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Node.js 22 is the current LTS version
|
||||
- NodeSource repository is configured for automatic updates
|
||||
- Cursor IDE is installed as AppImage for easy updates
|
||||
- Build tools (gcc, make) are essential for npm native modules
|
||||
- Python 3 is included for development scripts
|
||||
- All installations are idempotent (safe to re-run)
|
||||
|
||||
## Comparison with Data Science Role
|
||||
|
||||
| Component | Development Role | Data Science Role |
|
||||
|-----------|------------------|-------------------|
|
||||
| Git | ✅ | - |
|
||||
| Node.js | ✅ | - |
|
||||
| Build Tools | ✅ | - |
|
||||
| Cursor IDE | ✅ | - |
|
||||
| Anaconda | ❌ | ✅ |
|
||||
| Jupyter | ❌ | ✅ |
|
||||
| R Language | ❌ | ✅ |
|
||||
| Install Time | ~10 min | ~30-60 min |
|
||||
| Disk Space | ~350MB | ~3GB |
|
||||
|
||||
**Recommendation**: Use `development` role for general coding. Add `datascience` role only when needed for data analysis/ML work.
|
||||
|
||||
@ -1,2 +1,87 @@
|
||||
---
|
||||
# defaults file for development
|
||||
# Development role defaults
|
||||
|
||||
# Node.js is installed by default from NodeSource
|
||||
# No additional configuration needed
|
||||
|
||||
# Cursor IDE - lightweight IDE installation
|
||||
install_cursor: true
|
||||
install_cursor_extensions: false
|
||||
|
||||
# Base Cursor extensions (always good to have)
|
||||
cursor_extensions_base:
|
||||
- usernamehw.errorlens # Better error highlighting
|
||||
- eamodio.gitlens # Git supercharged
|
||||
- mhutchie.git-graph # Git graph visualization
|
||||
- streetsidesoftware.code-spell-checker # Spell checker
|
||||
- EditorConfig.EditorConfig # EditorConfig support
|
||||
- PKief.material-icon-theme # Better file icons
|
||||
|
||||
# Python/Data Science extensions
|
||||
cursor_extensions_python:
|
||||
- ms-python.python # Python language support
|
||||
- ms-python.vscode-pylance # Python IntelliSense
|
||||
- ms-python.black-formatter # Black formatter
|
||||
- ms-python.isort # Import sorter
|
||||
- ms-python.flake8 # Linter
|
||||
- charliermarsh.ruff # Fast Python linter
|
||||
|
||||
# Jupyter/Data Science extensions
|
||||
cursor_extensions_jupyter:
|
||||
- ms-toolsai.jupyter # Jupyter notebooks
|
||||
- ms-toolsai.jupyter-keymap # Jupyter keybindings
|
||||
- ms-toolsai.jupyter-renderers # Jupyter renderers
|
||||
|
||||
# Web Development extensions
|
||||
cursor_extensions_web:
|
||||
- esbenp.prettier-vscode # Code formatter
|
||||
- dbaeumer.vscode-eslint # ESLint
|
||||
- bradlc.vscode-tailwindcss # Tailwind CSS
|
||||
- vue.volar # Vue 3
|
||||
- svelte.svelte-vscode # Svelte
|
||||
|
||||
# Testing extensions
|
||||
cursor_extensions_testing:
|
||||
- ms-playwright.playwright # Playwright testing
|
||||
|
||||
# Systems/DevOps extensions
|
||||
cursor_extensions_devops:
|
||||
- golang.go # Go language
|
||||
- rust-lang.rust-analyzer # Rust language
|
||||
- redhat.vscode-yaml # YAML support
|
||||
- ms-azuretools.vscode-docker # Docker support
|
||||
- redhat.ansible # Ansible support
|
||||
|
||||
# R language extensions
|
||||
cursor_extensions_r:
|
||||
- REditorSupport.r # R language support
|
||||
- Ikuyadeu.r-pack # R package development
|
||||
|
||||
# Markdown/Documentation extensions
|
||||
cursor_extensions_docs:
|
||||
- yzhang.markdown-all-in-one # Markdown tools
|
||||
- DavidAnson.vscode-markdownlint # Markdown linter
|
||||
|
||||
# Default combined list (customize per host in host_vars)
|
||||
cursor_extensions: >-
|
||||
{{
|
||||
[
|
||||
cursor_extensions_base,
|
||||
(cursor_extensions_python if install_python | default(false) else []),
|
||||
(cursor_extensions_jupyter if install_jupyter | default(false) else []),
|
||||
(cursor_extensions_web if install_web | default(false) else []),
|
||||
(cursor_extensions_testing if install_playwright | default(false) else []),
|
||||
(cursor_extensions_devops if install_devops | default(false) else []),
|
||||
(cursor_extensions_r if install_r | default(false) else []),
|
||||
(cursor_extensions_docs if install_docs | default(false) else [])
|
||||
] | flatten
|
||||
}}
|
||||
|
||||
# Feature flags to enable extension groups
|
||||
install_python: false
|
||||
install_jupyter: false
|
||||
install_web: false
|
||||
install_playwright: false
|
||||
install_devops: false
|
||||
install_r: false
|
||||
install_docs: false
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
fi
|
||||
register: nodesource_repo_check
|
||||
failed_when: false
|
||||
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2')
|
||||
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||
|
||||
- name: Check if NodeSource GPG key exists and is correct
|
||||
ansible.builtin.shell: |
|
||||
@ -45,7 +45,7 @@
|
||||
fi
|
||||
register: nodesource_key_check
|
||||
failed_when: false
|
||||
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2')
|
||||
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||
|
||||
- name: Remove incorrect NodeSource repository
|
||||
ansible.builtin.file:
|
||||
@ -53,7 +53,7 @@
|
||||
state: absent
|
||||
become: true
|
||||
when:
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2')
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||
- nodesource_repo_check.stdout == "wrong_config"
|
||||
|
||||
- name: Remove incorrect NodeSource key
|
||||
@ -62,7 +62,7 @@
|
||||
state: absent
|
||||
become: true
|
||||
when:
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2')
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||
- nodesource_key_check.stdout == "wrong_key"
|
||||
|
||||
- name: Create keyrings directory
|
||||
@ -72,7 +72,7 @@
|
||||
mode: '0755'
|
||||
become: true
|
||||
when:
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2')
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||
- nodesource_key_check.stdout in ["not_exists", "wrong_key"]
|
||||
|
||||
- name: Add NodeSource GPG key only if needed
|
||||
@ -83,7 +83,7 @@
|
||||
force: true
|
||||
become: true
|
||||
when:
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2')
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||
- nodesource_key_check.stdout in ["not_exists", "wrong_key"]
|
||||
|
||||
- name: Add NodeSource repository only if needed
|
||||
@ -93,7 +93,7 @@
|
||||
update_cache: false
|
||||
become: true
|
||||
when:
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2')
|
||||
- node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||
- nodesource_repo_check.stdout in ["not_exists", "wrong_config"]
|
||||
|
||||
- name: Install Node.js 22 from NodeSource
|
||||
@ -101,7 +101,7 @@
|
||||
name: nodejs
|
||||
state: present
|
||||
become: true
|
||||
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v2')
|
||||
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||
|
||||
- name: Verify Node.js installation
|
||||
ansible.builtin.command: node --version
|
||||
@ -110,4 +110,93 @@
|
||||
|
||||
- name: Display Node.js version
|
||||
ansible.builtin.debug:
|
||||
msg: "Node.js version installed: {{ final_node_version.stdout }}"
|
||||
msg: "Node.js version installed: {{ final_node_version.stdout if final_node_version.stdout is defined else 'Not checked in dry-run mode' }}"
|
||||
|
||||
# Cursor IDE installation (using AppImage)
|
||||
# Downloads the latest version from cursor.com API
|
||||
- name: Install Cursor IDE block
|
||||
tags: ['cursor', 'ide']
|
||||
block:
|
||||
- name: Install libfuse2 dependency for AppImage
|
||||
ansible.builtin.apt:
|
||||
name: libfuse2
|
||||
state: present
|
||||
update_cache: false
|
||||
become: true
|
||||
when: ansible_os_family == "Debian"
|
||||
|
||||
- name: Check if Cursor is already installed at /usr/local/bin
|
||||
ansible.builtin.stat:
|
||||
path: /usr/local/bin/cursor
|
||||
register: cursor_bin_check
|
||||
|
||||
- name: Get Cursor download URL from API and download AppImage
|
||||
ansible.builtin.shell: |
|
||||
DOWNLOAD_URL=$(curl -sL "https://www.cursor.com/api/download?platform=linux-x64&releaseTrack=stable" | grep -o '"downloadUrl":"[^"]*' | cut -d'"' -f4)
|
||||
wget --timeout=60 --tries=3 -O /tmp/cursor.AppImage "$DOWNLOAD_URL"
|
||||
args:
|
||||
creates: /tmp/cursor.AppImage
|
||||
when: not cursor_bin_check.stat.exists
|
||||
register: cursor_download
|
||||
retries: 2
|
||||
delay: 5
|
||||
until: cursor_download.rc == 0
|
||||
|
||||
- name: Make Cursor AppImage executable
|
||||
ansible.builtin.file:
|
||||
path: /tmp/cursor.AppImage
|
||||
mode: '0755'
|
||||
when:
|
||||
- not cursor_bin_check.stat.exists
|
||||
- cursor_download is defined
|
||||
- cursor_download.rc is defined
|
||||
- cursor_download.rc == 0
|
||||
|
||||
- name: Install Cursor to /usr/local/bin
|
||||
ansible.builtin.copy:
|
||||
src: /tmp/cursor.AppImage
|
||||
dest: /usr/local/bin/cursor
|
||||
mode: '0755'
|
||||
remote_src: true
|
||||
when:
|
||||
- not cursor_bin_check.stat.exists
|
||||
- cursor_download is defined
|
||||
- cursor_download.rc is defined
|
||||
- cursor_download.rc == 0
|
||||
become: true
|
||||
|
||||
- name: Clean up Cursor download
|
||||
ansible.builtin.file:
|
||||
path: /tmp/cursor.AppImage
|
||||
state: absent
|
||||
when:
|
||||
- cursor_download is defined
|
||||
- cursor_download.rc is defined
|
||||
- cursor_download.rc == 0
|
||||
|
||||
- name: Display Cursor installation status
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ 'Cursor already installed' if cursor_bin_check.stat.exists else ('Cursor installed successfully' if (cursor_download is defined and cursor_download.rc is defined and cursor_download.rc == 0) else 'Cursor installation failed - download manually from cursor.com') }}"
|
||||
|
||||
# Cursor extensions installation
|
||||
- name: Install Cursor extensions block
|
||||
when:
|
||||
- install_cursor | default(true) | bool
|
||||
- install_cursor_extensions | default(false) | bool
|
||||
- cursor_extensions is defined
|
||||
- cursor_extensions | length > 0
|
||||
tags: ['cursor', 'extensions']
|
||||
block:
|
||||
- name: Install Cursor extensions
|
||||
ansible.builtin.shell: |
|
||||
cursor --install-extension {{ item }} --force --user-data-dir={{ ansible_env.HOME }}/.cursor-root 2>/dev/null || true
|
||||
loop: "{{ cursor_extensions }}"
|
||||
register: cursor_ext_install
|
||||
changed_when: "'successfully installed' in cursor_ext_install.stdout.lower()"
|
||||
failed_when: false
|
||||
become: true
|
||||
become_user: "{{ ansible_user }}"
|
||||
|
||||
- name: Display Cursor extensions status
|
||||
ansible.builtin.debug:
|
||||
msg: "Installed {{ cursor_extensions | length }} Cursor extensions"
|
||||
|
||||
@ -9,7 +9,8 @@ Installs comprehensive system monitoring tools and custom monitoring scripts for
|
||||
- Sufficient disk space for logs
|
||||
|
||||
## Installed Tools
|
||||
- **htop/btop**: Interactive process viewers
|
||||
- **htop**: Interactive process viewer
|
||||
- **btop**: Modern system monitor (from apt or binary)
|
||||
- **iotop**: I/O monitoring
|
||||
- **nethogs**: Network usage per process
|
||||
- **iftop**: Network bandwidth monitoring
|
||||
@ -57,5 +58,5 @@ Installs comprehensive system monitoring tools and custom monitoring scripts for
|
||||
## Notes
|
||||
- Creates monitoring user directories
|
||||
- Configures fail2ban with custom rules
|
||||
- Installs both CLI and snap-based tools
|
||||
- btop installed from apt on Debian 12+, or from GitHub binary on older versions
|
||||
- Custom scripts require manual execution
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
# Monitoring role defaults
|
||||
monitoring_install_snap_tools: true
|
||||
monitoring_install_btop: true
|
||||
monitoring_enable_sysstat: true
|
||||
monitoring_create_scripts: true
|
||||
|
||||
@ -21,12 +21,56 @@
|
||||
- atop
|
||||
state: present
|
||||
|
||||
- name: Install modern monitoring tools via snap
|
||||
community.general.snap:
|
||||
name:
|
||||
- btop
|
||||
- bandwhich
|
||||
- name: Check if btop is available in apt
|
||||
ansible.builtin.command: apt-cache policy btop
|
||||
register: btop_apt_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Install btop from apt if available (Debian 12+)
|
||||
ansible.builtin.apt:
|
||||
name: btop
|
||||
state: present
|
||||
update_cache: false
|
||||
when:
|
||||
- btop_apt_check.rc == 0
|
||||
- "'Candidate:' in btop_apt_check.stdout"
|
||||
- "'(none)' not in btop_apt_check.stdout"
|
||||
failed_when: false
|
||||
|
||||
- name: Install btop from binary if apt not available
|
||||
when: btop_apt_check.rc != 0 or "(none)" in btop_apt_check.stdout
|
||||
block:
|
||||
- name: Download btop binary
|
||||
ansible.builtin.get_url:
|
||||
url: https://github.com/aristocratos/btop/releases/latest/download/btop-x86_64-linux-musl.tbz
|
||||
dest: /tmp/btop.tbz
|
||||
mode: '0644'
|
||||
failed_when: false
|
||||
|
||||
- name: Extract btop
|
||||
ansible.builtin.unarchive:
|
||||
src: /tmp/btop.tbz
|
||||
dest: /tmp/
|
||||
remote_src: true
|
||||
failed_when: false
|
||||
|
||||
- name: Install btop binary
|
||||
ansible.builtin.copy:
|
||||
src: /tmp/btop/bin/btop
|
||||
dest: /usr/local/bin/btop
|
||||
mode: '0755'
|
||||
remote_src: true
|
||||
failed_when: false
|
||||
|
||||
- name: Clean up btop download
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /tmp/btop.tbz
|
||||
- /tmp/btop
|
||||
failed_when: false
|
||||
|
||||
- name: Configure fail2ban
|
||||
ansible.builtin.template:
|
||||
|
||||
@ -1,46 +1,202 @@
|
||||
# Role: shell
|
||||
### Role: shell
|
||||
|
||||
## Description
|
||||
Configures modern shell environment with Zsh, Oh My Zsh, and Powerlevel10k theme for enhanced terminal experience.
|
||||
Configures modern shell environment with zsh, Oh My Zsh, Powerlevel10k theme, and useful plugins. Can be configured for multiple users on the same host.
|
||||
|
||||
## Requirements
|
||||
- Ansible 2.9+
|
||||
- Debian/Ubuntu target systems
|
||||
- Internet access for downloading themes and plugins
|
||||
- Debian/Ubuntu systems
|
||||
- Users must exist before running this role
|
||||
|
||||
## Features
|
||||
- Installs and configures Zsh as default shell
|
||||
- Sets up Oh My Zsh framework
|
||||
- Installs Powerlevel10k theme
|
||||
- Adds useful plugins: syntax highlighting, autosuggestions
|
||||
- Configures tmux and fzf for enhanced productivity
|
||||
## Installed Components
|
||||
|
||||
### Shell Environment
|
||||
- **zsh**: Z shell
|
||||
- **Oh My Zsh**: Zsh configuration framework
|
||||
- **Powerlevel10k**: Modern, feature-rich theme
|
||||
- **tmux**: Terminal multiplexer
|
||||
- **fzf**: Fuzzy finder
|
||||
|
||||
### Zsh Plugins
|
||||
- **zsh-syntax-highlighting**: Syntax highlighting for commands
|
||||
- **zsh-autosuggestions**: Fish-like autosuggestions
|
||||
|
||||
### Configuration Files
|
||||
- `.zshrc`: Custom zsh configuration with conda support
|
||||
- `.p10k.zsh`: Powerlevel10k theme configuration
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `shell_user` | `{{ ansible_user }}` | User to configure shell for |
|
||||
| `shell_install_ohmyzsh` | `true` | Whether to install Oh My Zsh |
|
||||
| `shell_install_p10k` | `true` | Whether to install Powerlevel10k theme |
|
||||
| `shell_users` | `[ansible_user]` | List of users to configure zsh for |
|
||||
| `zsh_plugins` | See defaults/main.yml | List of zsh plugins to install |
|
||||
|
||||
## Dependencies
|
||||
- `base` role (for basic packages)
|
||||
None
|
||||
|
||||
## Example Playbook
|
||||
|
||||
### Configure Only Ansible User (Default)
|
||||
```yaml
|
||||
- hosts: dev
|
||||
- hosts: servers
|
||||
roles:
|
||||
- { role: shell, shell_user: "myuser" }
|
||||
- role: shell
|
||||
```
|
||||
|
||||
## Tags
|
||||
- `shell`: All shell configuration tasks
|
||||
- `zsh`: Zsh installation and configuration
|
||||
- `ohmyzsh`: Oh My Zsh framework setup
|
||||
- `theme`: Theme and plugin configuration
|
||||
### Configure Multiple Users
|
||||
```yaml
|
||||
- hosts: servers
|
||||
roles:
|
||||
- role: shell
|
||||
shell_users:
|
||||
- root
|
||||
- ladmin
|
||||
- beast
|
||||
- user
|
||||
```
|
||||
|
||||
### Host-Specific Configuration
|
||||
In `host_vars/server01.yml`:
|
||||
```yaml
|
||||
shell_users:
|
||||
- root
|
||||
- myuser
|
||||
- developer
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Default (Ansible User Only)
|
||||
```bash
|
||||
make shell HOST=dev01
|
||||
```
|
||||
|
||||
### Configure Additional Users
|
||||
Add to host_vars file:
|
||||
```yaml
|
||||
# host_vars/devGPU.yml
|
||||
shell_users:
|
||||
- root
|
||||
- beast
|
||||
- ladmin
|
||||
```
|
||||
|
||||
Then run:
|
||||
```bash
|
||||
make dev HOST=devGPU --tags shell
|
||||
```
|
||||
|
||||
## Post-Installation
|
||||
|
||||
### For Each Configured User
|
||||
The shell configuration is immediately active. Users can:
|
||||
|
||||
1. **Customize Powerlevel10k**:
|
||||
```bash
|
||||
p10k configure
|
||||
```
|
||||
|
||||
2. **Reload Configuration**:
|
||||
```bash
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
3. **View Available Aliases**:
|
||||
```bash
|
||||
alias # List all aliases
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Custom Aliases
|
||||
The `.zshrc` includes aliases for:
|
||||
- Git operations (`gs`, `ga`, `gc`, etc.)
|
||||
- Docker (`dps`, `dex`, `dlogs`)
|
||||
- System (`ll`, `la`, `update`, `sysinfo`)
|
||||
- Networking (`ports`, `myip`)
|
||||
- Development (`serve`, `mkcd`)
|
||||
- Data Science (`jup`, `conda-list`, `r-studio`)
|
||||
|
||||
### Conda Integration
|
||||
If Anaconda is installed (via datascience role), conda is automatically initialized in zsh.
|
||||
|
||||
### Root User Support
|
||||
Includes special aliases for root users (IDEs with `--no-sandbox` flags).
|
||||
|
||||
## Notes
|
||||
- Changes default shell to zsh for the target user
|
||||
- Downloads themes and plugins from GitHub
|
||||
- Requires logout/login to see changes
|
||||
|
||||
- Zsh is set as the default shell for all configured users
|
||||
- Oh My Zsh is installed in each user's home directory
|
||||
- The role is idempotent - safe to run multiple times
|
||||
- Existing `.zshrc` files are overwritten
|
||||
- Users must log out and back in for shell changes to take effect
|
||||
- The role skips users that don't exist on the system
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### User Not Found
|
||||
If a user in `shell_users` doesn't exist:
|
||||
```
|
||||
User username not found, skipping shell configuration
|
||||
```
|
||||
Solution: Ensure the user exists or remove from `shell_users` list.
|
||||
|
||||
### Oh My Zsh Installation Fails
|
||||
Check user has a valid home directory and write permissions.
|
||||
|
||||
### Powerlevel10k Not Loading
|
||||
Verify the theme is cloned:
|
||||
```bash
|
||||
ls ~/.oh-my-zsh/custom/themes/powerlevel10k
|
||||
```
|
||||
|
||||
### Conda Not Initialized
|
||||
The datascience role must be run to install Anaconda. The shell role only adds the initialization code to `.zshrc`.
|
||||
|
||||
## Integration
|
||||
|
||||
### With User Role
|
||||
The user role should run before the shell role to ensure users exist:
|
||||
```yaml
|
||||
roles:
|
||||
- user
|
||||
- shell
|
||||
```
|
||||
|
||||
### With Data Science Role
|
||||
Conda initialization is included in `.zshrc`. Run datascience role after shell:
|
||||
```yaml
|
||||
roles:
|
||||
- shell
|
||||
- datascience
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- The `.zshrc` is deployed from a template - review before deploying to production
|
||||
- Root aliases include `--no-sandbox` flags for IDEs (required for root but less secure)
|
||||
- Consider limiting which users get shell configuration on production servers
|
||||
|
||||
## Performance
|
||||
|
||||
- Installation time: ~2-3 minutes per user
|
||||
- Disk space: ~10MB per user (Oh My Zsh + plugins + theme)
|
||||
- First shell launch: ~1-2 seconds (Powerlevel10k initialization)
|
||||
- Subsequent launches: <0.5 seconds
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding Custom Aliases
|
||||
Edit `roles/shell/files/.zshrc` and add your aliases.
|
||||
|
||||
### Adding More Plugins
|
||||
Update `roles/shell/defaults/main.yml`:
|
||||
```yaml
|
||||
zsh_plugins:
|
||||
- name: "my-plugin"
|
||||
repo: "https://github.com/user/my-plugin.git"
|
||||
```
|
||||
|
||||
### Custom Theme
|
||||
Replace Powerlevel10k in tasks if desired, or users can run `p10k configure` to customize.
|
||||
|
||||
@ -1,2 +1,23 @@
|
||||
---
|
||||
# defaults file for shell
|
||||
# Shell role defaults
|
||||
|
||||
# List of users to configure zsh for
|
||||
# By default, only configure the ansible user
|
||||
# Override in host_vars to add more users
|
||||
shell_users:
|
||||
- "{{ ansible_user | default(ansible_user_id) }}"
|
||||
|
||||
# Additional users to configure (appended to shell_users)
|
||||
# Use this to add users WITHOUT having to repeat ansible_user
|
||||
# Example in host_vars:
|
||||
# shell_additional_users:
|
||||
# - beast
|
||||
# - ladmin
|
||||
shell_additional_users: []
|
||||
|
||||
# Zsh plugins to install
|
||||
zsh_plugins:
|
||||
- name: "zsh-syntax-highlighting"
|
||||
repo: "https://github.com/zsh-users/zsh-syntax-highlighting.git"
|
||||
- name: "zsh-autosuggestions"
|
||||
repo: "https://github.com/zsh-users/zsh-autosuggestions.git"
|
||||
|
||||
1719
roles/shell/files/.p10k.zsh
Normal file
1719
roles/shell/files/.p10k.zsh
Normal file
File diff suppressed because it is too large
Load Diff
@ -132,6 +132,7 @@ alias dc="cd ~/Documents/code"
|
||||
alias df="df -h" # disk usage human readable
|
||||
alias du="du -h" # directory usage human readable
|
||||
alias free="free -h" # memory usage human readable
|
||||
alias sysinfo="/usr/local/bin/monitoring/sysinfo 2>/dev/null || echo 'sysinfo script not found'"
|
||||
|
||||
# Process management
|
||||
alias ps="ps aux"
|
||||
@ -143,6 +144,11 @@ alias ports="ss -tulpn" # open ports
|
||||
# Network information
|
||||
alias myip="curl -s http://ipecho.net/plain; echo"
|
||||
alias localip="ip route get 1.2.3.4 | awk '{print $7}'"
|
||||
alias netinfo="/usr/local/bin/monitoring/netinfo 2>/dev/null || echo 'netinfo script not found'"
|
||||
|
||||
# Software inventory - show what's installed on this system
|
||||
alias showapps="$HOME/.local/bin/showapps"
|
||||
alias apps="showapps"
|
||||
|
||||
# Python
|
||||
alias py="python3"
|
||||
@ -159,6 +165,10 @@ alias dcb="docker-compose build"
|
||||
alias dps="docker ps"
|
||||
alias di="docker images"
|
||||
|
||||
# IDE - suppress root warnings
|
||||
alias code="code --no-sandbox --user-data-dir=/root/.vscode-root"
|
||||
alias cursor="cursor --no-sandbox --disable-gpu-sandbox --appimage-extract-and-run --user-data-dir=/root/.cursor-root"
|
||||
|
||||
# Date and time
|
||||
alias now="date +'%Y-%m-%d %H:%M:%S'"
|
||||
alias today="date +'%Y-%m-%d'"
|
||||
@ -175,3 +185,20 @@ alias own="sudo chown -R $USER:$USER"
|
||||
|
||||
alias nfresh="rm -rf node_modules/ package-lock.json && npm install"
|
||||
|
||||
# >>> conda initialize >>>
|
||||
# !! Contents within this block are managed by 'conda init' !!
|
||||
if [ -f "$HOME/anaconda3/bin/conda" ]; then
|
||||
__conda_setup="$('$HOME/anaconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
|
||||
if [ $? -eq 0 ]; then
|
||||
eval "$__conda_setup"
|
||||
else
|
||||
if [ -f "$HOME/anaconda3/etc/profile.d/conda.sh" ]; then
|
||||
. "$HOME/anaconda3/etc/profile.d/conda.sh"
|
||||
else
|
||||
export PATH="$HOME/anaconda3/bin:$PATH"
|
||||
fi
|
||||
fi
|
||||
unset __conda_setup
|
||||
fi
|
||||
# <<< conda initialize <<<
|
||||
|
||||
|
||||
54
roles/shell/files/showapps.sh
Normal file
54
roles/shell/files/showapps.sh
Normal file
@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# Show installed software on this system
|
||||
# Part of the Ansible shell role
|
||||
|
||||
BOLD='\033[1m'
|
||||
GREEN='\033[32m'
|
||||
YELLOW='\033[33m'
|
||||
RED='\033[31m'
|
||||
RESET='\033[0m'
|
||||
|
||||
echo -e "${BOLD}=== Installed Software ===${RESET}\n"
|
||||
|
||||
echo -e "${YELLOW}Development:${RESET}"
|
||||
for tool in git node python3 docker; do
|
||||
if command -v $tool >/dev/null 2>&1; then
|
||||
version=$(case $tool in
|
||||
node) node --version 2>/dev/null ;;
|
||||
python3) python3 --version 2>/dev/null | awk '{print $2}' ;;
|
||||
git) git --version 2>/dev/null | awk '{print $3}' ;;
|
||||
docker) docker --version 2>/dev/null | awk '{print $3}' | tr -d ',' ;;
|
||||
esac)
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version"
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\n${YELLOW}Data Science:${RESET}"
|
||||
for tool in conda jupyter R; do
|
||||
if command -v $tool >/dev/null 2>&1; then
|
||||
version=$(case $tool in
|
||||
conda) conda --version 2>/dev/null | awk '{print $2}' ;;
|
||||
jupyter) echo "installed" ;;
|
||||
R) R --version 2>/dev/null | head -1 | awk '{print $3}' ;;
|
||||
esac)
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version"
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\n${YELLOW}Editors:${RESET}"
|
||||
for tool in cursor code vim nvim nano; do
|
||||
command -v $tool >/dev/null 2>&1 && printf " ${GREEN}✓${RESET} %s\n" "$tool"
|
||||
done
|
||||
|
||||
echo -e "\n${YELLOW}CLI Tools:${RESET}"
|
||||
for tool in tmux fzf htop btop jq yq rg fd; do
|
||||
command -v $tool >/dev/null 2>&1 && printf " ${GREEN}✓${RESET} %s\n" "$tool"
|
||||
done
|
||||
|
||||
echo -e "\n${YELLOW}Shell:${RESET}"
|
||||
echo -e " ${GREEN}✓${RESET} current: $SHELL"
|
||||
[ -d "$HOME/.oh-my-zsh" ] && echo -e " ${GREEN}✓${RESET} oh-my-zsh"
|
||||
[ -d "$HOME/.oh-my-zsh/custom/themes/powerlevel10k" ] && echo -e " ${GREEN}✓${RESET} powerlevel10k"
|
||||
|
||||
echo ""
|
||||
|
||||
104
roles/shell/tasks/configure_user_shell.yml
Normal file
104
roles/shell/tasks/configure_user_shell.yml
Normal file
@ -0,0 +1,104 @@
|
||||
---
|
||||
# Configure shell for a single user
|
||||
# Variable: current_user - the username to configure
|
||||
|
||||
- name: "Get user information: {{ current_user }}"
|
||||
ansible.builtin.getent:
|
||||
database: passwd
|
||||
key: "{{ current_user }}"
|
||||
register: user_info
|
||||
failed_when: false
|
||||
|
||||
- name: "Set user home directory: {{ current_user }}"
|
||||
ansible.builtin.set_fact:
|
||||
user_home: "{{ user_info.ansible_facts.getent_passwd[current_user][4] }}"
|
||||
when: user_info.ansible_facts.getent_passwd[current_user] is defined
|
||||
|
||||
- name: Skip if user not found
|
||||
ansible.builtin.debug:
|
||||
msg: "User {{ current_user }} not found, skipping shell configuration"
|
||||
when: user_info.ansible_facts.getent_passwd[current_user] is not defined
|
||||
|
||||
- name: Configure shell environment
|
||||
when: user_info.ansible_facts.getent_passwd[current_user] is defined
|
||||
block:
|
||||
- name: "Set zsh as default shell: {{ current_user }}"
|
||||
ansible.builtin.user:
|
||||
name: "{{ current_user }}"
|
||||
shell: /usr/bin/zsh
|
||||
become: true
|
||||
|
||||
- name: "Install Oh My Zsh: {{ current_user }}"
|
||||
become: true
|
||||
become_user: "{{ current_user }}"
|
||||
ansible.builtin.shell: sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)" "" --unattended
|
||||
args:
|
||||
creates: "{{ user_home }}/.oh-my-zsh"
|
||||
|
||||
- name: "Clone Powerlevel10k theme: {{ current_user }}"
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/romkatv/powerlevel10k.git
|
||||
dest: "{{ user_home }}/.oh-my-zsh/custom/themes/powerlevel10k"
|
||||
version: master
|
||||
depth: 1
|
||||
update: false
|
||||
become: true
|
||||
become_user: "{{ current_user }}"
|
||||
|
||||
- name: "Install zsh plugins: {{ current_user }}"
|
||||
ansible.builtin.git:
|
||||
repo: "{{ item.repo }}"
|
||||
dest: "{{ user_home }}/.oh-my-zsh/custom/plugins/{{ item.name }}"
|
||||
version: master
|
||||
depth: 1
|
||||
update: false
|
||||
become: true
|
||||
become_user: "{{ current_user }}"
|
||||
loop: "{{ zsh_plugins }}"
|
||||
|
||||
- name: "Deploy .zshrc: {{ current_user }}"
|
||||
ansible.builtin.copy:
|
||||
src: files/.zshrc
|
||||
dest: "{{ user_home }}/.zshrc"
|
||||
owner: "{{ current_user }}"
|
||||
group: "{{ current_user }}"
|
||||
mode: '0644'
|
||||
become: true
|
||||
|
||||
- name: "Deploy Powerlevel10k configuration: {{ current_user }}"
|
||||
ansible.builtin.copy:
|
||||
src: files/.p10k.zsh
|
||||
dest: "{{ user_home }}/.p10k.zsh"
|
||||
owner: "{{ current_user }}"
|
||||
group: "{{ current_user }}"
|
||||
mode: '0644'
|
||||
become: true
|
||||
|
||||
- name: "Ensure .local/bin directory exists: {{ current_user }}"
|
||||
ansible.builtin.file:
|
||||
path: "{{ user_home }}/.local/bin"
|
||||
state: directory
|
||||
owner: "{{ current_user }}"
|
||||
group: "{{ current_user }}"
|
||||
mode: '0755'
|
||||
become: true
|
||||
|
||||
- name: "Deploy showapps script: {{ current_user }}"
|
||||
ansible.builtin.copy:
|
||||
src: files/showapps.sh
|
||||
dest: "{{ user_home }}/.local/bin/showapps"
|
||||
owner: "{{ current_user }}"
|
||||
group: "{{ current_user }}"
|
||||
mode: '0755'
|
||||
become: true
|
||||
|
||||
- name: "Display post-installation instructions: {{ current_user }}"
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "=== Shell Configuration Complete for {{ current_user }} ==="
|
||||
- "NOTE: Zsh has been set as the default shell."
|
||||
- "To activate immediately, choose one of:"
|
||||
- " 1. Log out and back in (recommended)"
|
||||
- " 2. Run: exec zsh"
|
||||
- " 3. Or simply run: zsh"
|
||||
- "=========================================="
|
||||
@ -1,8 +1,4 @@
|
||||
---
|
||||
- name: Set target user variable
|
||||
ansible.builtin.set_fact:
|
||||
shell_target_user: "{{ ansible_user | default(ansible_user_id) }}"
|
||||
|
||||
- name: Install shell packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
@ -10,46 +6,17 @@
|
||||
- tmux
|
||||
- fzf
|
||||
state: present
|
||||
|
||||
- name: Install zsh plugins
|
||||
ansible.builtin.git:
|
||||
repo: "{{ item.repo }}"
|
||||
dest: "/home/{{ shell_target_user }}/.oh-my-zsh/custom/plugins/{{ item.name }}"
|
||||
version: master
|
||||
depth: 1
|
||||
update: false
|
||||
become: true
|
||||
become_user: "{{ shell_target_user }}"
|
||||
loop:
|
||||
- {name: "zsh-syntax-highlighting", repo: "https://github.com/zsh-users/zsh-syntax-highlighting.git"}
|
||||
- {name: "zsh-autosuggestions", repo: "https://github.com/zsh-users/zsh-autosuggestions.git"}
|
||||
|
||||
- name: Set zsh as default shell for user
|
||||
ansible.builtin.user:
|
||||
name: "{{ shell_target_user }}"
|
||||
shell: /usr/bin/zsh
|
||||
# Merge shell_users and shell_additional_users
|
||||
- name: Build complete user list
|
||||
ansible.builtin.set_fact:
|
||||
shell_all_users: "{{ (shell_users | default([ansible_user])) + (shell_additional_users | default([])) | unique }}"
|
||||
|
||||
- name: Install Oh My Zsh for user
|
||||
become: true
|
||||
become_user: "{{ shell_target_user }}"
|
||||
ansible.builtin.shell: sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)" "" --unattended
|
||||
args:
|
||||
creates: "/home/{{ shell_target_user }}/.oh-my-zsh"
|
||||
|
||||
- name: Clone Powerlevel10k theme
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/romkatv/powerlevel10k.git
|
||||
dest: "/home/{{ shell_target_user }}/.oh-my-zsh/custom/themes/powerlevel10k"
|
||||
version: master
|
||||
depth: 1
|
||||
update: false
|
||||
become: true
|
||||
become_user: "{{ shell_target_user }}"
|
||||
|
||||
- name: Deploy .zshrc for user
|
||||
ansible.builtin.copy:
|
||||
src: files/.zshrc
|
||||
dest: "/home/{{ shell_target_user }}/.zshrc"
|
||||
owner: "{{ shell_target_user }}"
|
||||
group: "{{ shell_target_user }}"
|
||||
mode: '0644'
|
||||
# Configure shell for each user
|
||||
- name: Configure shell for each user
|
||||
ansible.builtin.include_tasks: configure_user_shell.yml
|
||||
loop: "{{ shell_all_users }}"
|
||||
loop_control:
|
||||
loop_var: current_user
|
||||
when: shell_all_users is defined and shell_all_users | length > 0
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
# Role: snap
|
||||
|
||||
## Description
|
||||
Installs and configures Snap package manager and essential snap applications for development.
|
||||
|
||||
## Requirements
|
||||
- Ansible 2.9+
|
||||
- Ubuntu/Debian systems
|
||||
- Internet access for snap store
|
||||
|
||||
## Installed Snap Applications
|
||||
- **Visual Studio Code**: Popular code editor
|
||||
- **Cursor**: AI-powered code editor
|
||||
- **yq**: YAML processor
|
||||
- **btop**: Modern system monitor
|
||||
- **bandwhich**: Network utilization monitor
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `snap_install_vscode` | `true` | Install VS Code via snap |
|
||||
| `snap_install_cursor` | `true` | Install Cursor editor |
|
||||
| `snap_install_yq` | `true` | Install yq YAML processor |
|
||||
| `snap_install_btop` | `true` | Install btop system monitor |
|
||||
|
||||
## Dependencies
|
||||
- `base` role (for system packages)
|
||||
|
||||
## Example Playbook
|
||||
|
||||
```yaml
|
||||
- hosts: dev
|
||||
roles:
|
||||
- { role: snap, snap_install_cursor: false }
|
||||
```
|
||||
|
||||
## Tags
|
||||
- `snap`: All snap-related tasks
|
||||
- `apps`: Application installations
|
||||
- `editors`: Code editor installations
|
||||
|
||||
## Notes
|
||||
- Enables universe repository for snap support
|
||||
- Waits for snap system to be ready before installing
|
||||
- Some snaps may require additional permissions
|
||||
- Classic confinement used for development tools
|
||||
@ -1 +0,0 @@
|
||||
---
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
- name: Restart snapd
|
||||
ansible.builtin.systemd:
|
||||
name: snapd
|
||||
state: restarted
|
||||
@ -1,78 +0,0 @@
|
||||
---
|
||||
- name: Enable 'universe' repo (Ubuntu and Mint only)
|
||||
ansible.builtin.command: add-apt-repository universe
|
||||
when: ansible_facts['distribution'] in ["Ubuntu", "Linux Mint"]
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Remove Mint's nosnap.pref block (Mint only)
|
||||
ansible.builtin.file:
|
||||
path: /etc/apt/preferences.d/nosnap.pref
|
||||
state: absent
|
||||
when: ansible_facts['distribution'] == "Linux Mint"
|
||||
|
||||
- name: Install Snap daemon
|
||||
ansible.builtin.apt:
|
||||
name: snapd
|
||||
state: present
|
||||
when: ansible_facts['os_family'] == "Debian"
|
||||
notify: restart snapd
|
||||
|
||||
- name: Enable snapd socket on Debian
|
||||
ansible.builtin.systemd:
|
||||
name: snapd.socket
|
||||
enabled: true
|
||||
state: started
|
||||
when: ansible_facts['distribution'] == "Debian"
|
||||
|
||||
- name: Force Ansible to reload facts
|
||||
ansible.builtin.setup:
|
||||
|
||||
- name: Wait for snapd to be ready
|
||||
ansible.builtin.command: snap wait system seed.loaded
|
||||
register: snap_wait_result
|
||||
until: snap_wait_result.rc == 0
|
||||
retries: 10
|
||||
delay: 5
|
||||
when: ansible_facts['os_family'] == "Debian"
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Check if snap is working
|
||||
ansible.builtin.command: snap list
|
||||
register: snap_check
|
||||
when: ansible_facts['os_family'] == "Debian"
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Install VSCode IDE (Snap, all distros)
|
||||
community.general.snap:
|
||||
name: code
|
||||
classic: true
|
||||
state: present
|
||||
when:
|
||||
- ansible_facts['os_family'] == "Debian"
|
||||
- snap_check is defined
|
||||
- snap_check.rc == 0
|
||||
failed_when: false
|
||||
|
||||
- name: Install Cursor (Snap, all distros)
|
||||
community.general.snap:
|
||||
name: cursor
|
||||
state: present
|
||||
when:
|
||||
- ansible_facts['os_family'] == "Debian"
|
||||
- snap_check is defined
|
||||
- snap_check.rc == 0
|
||||
failed_when: false
|
||||
|
||||
- name: Display snap installation status
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Snap check result: {{ snap_check.rc if snap_check is defined else 'not defined' }}
|
||||
VSCode and Cursor installation may have failed if snap is not properly configured.
|
||||
You may need to manually install these applications or troubleshoot snap on this host.
|
||||
when:
|
||||
- ansible_facts['os_family'] == "Debian"
|
||||
- snap_check is defined
|
||||
- snap_check.rc != 0
|
||||
@ -2,14 +2,14 @@
|
||||
# SSH server configuration
|
||||
ssh_port: 22
|
||||
ssh_listen_addresses: ['0.0.0.0']
|
||||
ssh_permit_root_login: 'no'
|
||||
ssh_password_authentication: 'no'
|
||||
ssh_permit_root_login: 'yes'
|
||||
ssh_password_authentication: 'yes'
|
||||
ssh_pubkey_authentication: 'yes'
|
||||
ssh_max_auth_tries: 3
|
||||
ssh_client_alive_interval: 300
|
||||
ssh_max_sessions: 10
|
||||
ssh_allowed_users: [] # Restrict to specific users
|
||||
ssh_allowed_groups: ['sudo', 'ssh']
|
||||
ssh_allowed_groups: ['sudo']
|
||||
|
||||
# Security hardening
|
||||
ssh_kex_algorithms:
|
||||
|
||||
147
scripts/inventory.sh
Executable file
147
scripts/inventory.sh
Executable file
@ -0,0 +1,147 @@
|
||||
#!/bin/bash
|
||||
# Dynamic inventory script - lists all installed tools and services
|
||||
# Can be run on any managed host
|
||||
|
||||
# Colors
|
||||
BOLD='\033[1m'
|
||||
GREEN='\033[32m'
|
||||
YELLOW='\033[33m'
|
||||
BLUE='\033[34m'
|
||||
RED='\033[31m'
|
||||
RESET='\033[0m'
|
||||
|
||||
echo -e "${BOLD}=== Installed Software Inventory ===${RESET}\n"
|
||||
|
||||
# Function to check if command exists
|
||||
cmd_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Function to get version
|
||||
get_version() {
|
||||
if cmd_exists "$1"; then
|
||||
case "$1" in
|
||||
node) node --version 2>/dev/null ;;
|
||||
python3) python3 --version 2>/dev/null | awk '{print $2}' ;;
|
||||
git) git --version 2>/dev/null | awk '{print $3}' ;;
|
||||
docker) docker --version 2>/dev/null | awk '{print $3}' | tr -d ',' ;;
|
||||
conda) conda --version 2>/dev/null | awk '{print $2}' ;;
|
||||
jupyter) jupyter --version 2>/dev/null | head -1 | awk '{print $3}' ;;
|
||||
R) R --version 2>/dev/null | head -1 | awk '{print $3}' ;;
|
||||
yq) yq --version 2>/dev/null | awk '{print $NF}' ;;
|
||||
btop) btop --version 2>/dev/null | head -1 | awk '{print $3}' ;;
|
||||
cursor) echo "installed" ;;
|
||||
*) echo "unknown" ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
# Development Tools
|
||||
echo -e "${YELLOW}Development Tools:${RESET}"
|
||||
for tool in git node python3; do
|
||||
version=$(get_version $tool)
|
||||
if [ -n "$version" ]; then
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version"
|
||||
else
|
||||
printf " ${RED}✗${RESET} %-15s %s\n" "$tool" "not installed"
|
||||
fi
|
||||
done
|
||||
|
||||
# Data Science
|
||||
echo -e "\n${YELLOW}Data Science Stack:${RESET}"
|
||||
for tool in conda jupyter R; do
|
||||
version=$(get_version $tool)
|
||||
if [ -n "$version" ]; then
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version"
|
||||
else
|
||||
printf " ${RED}✗${RESET} %-15s %s\n" "$tool" "not installed"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check Jupyter service
|
||||
if systemctl is-active --quiet jupyter-notebook 2>/dev/null; then
|
||||
echo -e " ${GREEN}✓${RESET} Jupyter service running"
|
||||
else
|
||||
echo -e " ${RED}✗${RESET} Jupyter service not running"
|
||||
fi
|
||||
|
||||
# IDEs & Editors
|
||||
echo -e "\n${YELLOW}IDEs & Editors:${RESET}"
|
||||
for tool in cursor code; do
|
||||
if cmd_exists $tool; then
|
||||
printf " ${GREEN}✓${RESET} %-15s installed\n" "$tool"
|
||||
else
|
||||
printf " ${RED}✗${RESET} %-15s not installed\n" "$tool"
|
||||
fi
|
||||
done
|
||||
|
||||
# Container Platform
|
||||
echo -e "\n${YELLOW}Container Platform:${RESET}"
|
||||
version=$(get_version docker)
|
||||
if [ -n "$version" ]; then
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "docker" "$version"
|
||||
if cmd_exists docker-compose; then
|
||||
compose_version=$(docker-compose --version 2>/dev/null | awk '{print $NF}')
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "docker-compose" "$compose_version"
|
||||
fi
|
||||
else
|
||||
printf " ${RED}✗${RESET} %-15s not installed\n" "docker"
|
||||
fi
|
||||
|
||||
# Monitoring Tools
|
||||
echo -e "\n${YELLOW}Monitoring Tools:${RESET}"
|
||||
for tool in htop btop iotop nethogs; do
|
||||
if cmd_exists $tool; then
|
||||
version=$(get_version $tool)
|
||||
if [ "$version" = "unknown" ] || [ -z "$version" ]; then
|
||||
printf " ${GREEN}✓${RESET} %-15s installed\n" "$tool"
|
||||
else
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version"
|
||||
fi
|
||||
else
|
||||
printf " ${RED}✗${RESET} %-15s not installed\n" "$tool"
|
||||
fi
|
||||
done
|
||||
|
||||
# CLI Utilities
|
||||
echo -e "\n${YELLOW}CLI Utilities:${RESET}"
|
||||
for tool in jq yq ripgrep fd; do
|
||||
if cmd_exists $tool; then
|
||||
version=$(get_version $tool)
|
||||
if [ "$version" = "unknown" ] || [ -z "$version" ]; then
|
||||
printf " ${GREEN}✓${RESET} %-15s installed\n" "$tool"
|
||||
else
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "$tool" "$version"
|
||||
fi
|
||||
else
|
||||
printf " ${RED}✗${RESET} %-15s not installed\n" "$tool"
|
||||
fi
|
||||
done
|
||||
|
||||
# Shell
|
||||
echo -e "\n${YELLOW}Shell Environment:${RESET}"
|
||||
current_shell=$(basename "$SHELL")
|
||||
printf " ${GREEN}✓${RESET} %-15s %s\n" "current shell" "$current_shell"
|
||||
|
||||
if [ -d "$HOME/.oh-my-zsh" ]; then
|
||||
printf " ${GREEN}✓${RESET} %-15s installed\n" "oh-my-zsh"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.oh-my-zsh/custom/themes/powerlevel10k" ]; then
|
||||
printf " ${GREEN}✓${RESET} %-15s installed\n" "powerlevel10k"
|
||||
fi
|
||||
|
||||
# Services
|
||||
echo -e "\n${YELLOW}System Services:${RESET}"
|
||||
for service in docker tailscaled fail2ban sshd; do
|
||||
if systemctl is-active --quiet $service 2>/dev/null; then
|
||||
printf " ${GREEN}✓${RESET} %-15s running\n" "$service"
|
||||
elif systemctl list-unit-files | grep -q "^$service"; then
|
||||
printf " ${BLUE}○${RESET} %-15s installed (not running)\n" "$service"
|
||||
else
|
||||
printf " ${RED}✗${RESET} %-15s not installed\n" "$service"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user