Merge pull request #16 from hintjen/remove-macos-support
Remove macos support
This commit is contained in:
commit
badcb65df7
11
.gitignore
vendored
11
.gitignore
vendored
@ -2,3 +2,14 @@
|
||||
*.log
|
||||
.ansible/
|
||||
.vault_pass
|
||||
|
||||
# Secrets and credentials
|
||||
*.env
|
||||
.env*
|
||||
secrets.yml
|
||||
vault.yml
|
||||
*.pem
|
||||
*.key
|
||||
id_rsa*
|
||||
host_vars/
|
||||
group_vars/
|
||||
|
||||
@ -141,7 +141,7 @@ SSH is exposed to the internet. Fail2ban automatically bans IPs after 5 failed a
|
||||
Security patches should be applied promptly. Automatic security-only updates reduce vulnerability windows.
|
||||
|
||||
### Why Scoped Sudo?
|
||||
The clawdbot user only needs to manage its own service and Tailscale. Full root access would be dangerous if the app is compromised.
|
||||
The openclaw user only needs to manage its own service and Tailscale. Full root access would be dangerous if the app is compromised.
|
||||
|
||||
### Why Localhost Binding?
|
||||
Defense in depth. If DOCKER-USER fails, localhost binding prevents external access.
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Clawdbot Contributors
|
||||
Copyright (c) 2025 OpenClaw Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
50
README.md
50
README.md
@ -3,19 +3,29 @@
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/openclaw/openclaw-ansible/actions/workflows/lint.yml)
|
||||
[](https://www.ansible.com/)
|
||||
[](https://www.debian.org/)
|
||||
[](https://www.debian.org/)
|
||||
|
||||
Automated, hardened installation of [OpenClaw](https://github.com/openclaw/openclaw) with Docker, Homebrew, and Tailscale VPN support for Linux and macOS.
|
||||
Automated, hardened installation of [OpenClaw](https://github.com/openclaw/openclaw) with Docker and Tailscale VPN support for Debian/Ubuntu Linux.
|
||||
|
||||
## ⚠️ macOS Support: Deprecated & Disabled
|
||||
|
||||
**Effective 2026-02-06, support for bare-metal macOS installations has been removed from this playbook.**
|
||||
|
||||
### Why?
|
||||
The underlying project currently requires system-level permissions and configurations that introduce significant security risks when executed on a primary host OS. To protect user data and system integrity, we have disabled bare-metal execution.
|
||||
|
||||
### What does this mean?
|
||||
* The playbook will now explicitly fail if run on a `Darwin` (macOS) system.
|
||||
* We strongly discourage manual workarounds to bypass this check.
|
||||
* **Future Support:** We are evaluating a virtualization-first strategy (using Vagrant or Docker) to provide a sandboxed environment for this project in the future.
|
||||
|
||||
## Features
|
||||
|
||||
- 🔒 **Firewall-first**: UFW (Linux) + Application Firewall (macOS) + Docker isolation
|
||||
- 🔒 **Firewall-first**: UFW firewall + Docker isolation
|
||||
- 🛡️ **Fail2ban**: SSH brute-force protection out of the box
|
||||
- 🔄 **Auto-updates**: Automatic security patches via unattended-upgrades
|
||||
- 🔐 **Tailscale VPN**: Secure remote access without exposing services
|
||||
- 🍺 **Homebrew**: Package manager for both Linux and macOS
|
||||
- 🐳 **Docker**: Docker CE (Linux) / Docker Desktop (macOS)
|
||||
- 🌐 **Multi-OS Support**: Debian, Ubuntu, and macOS
|
||||
- 🐳 **Docker**: Docker CE with security hardening
|
||||
- 🚀 **One-command install**: Complete setup in minutes
|
||||
- 🔧 **Auto-configuration**: DBus, systemd, environment setup
|
||||
- 📦 **pnpm installation**: Uses `pnpm install -g openclaw@latest`
|
||||
@ -147,36 +157,18 @@ ansible-playbook playbook.yml --ask-become-pass
|
||||
|
||||
## Requirements
|
||||
|
||||
### Linux (Debian/Ubuntu)
|
||||
- Debian 11+ or Ubuntu 20.04+
|
||||
- Root/sudo access
|
||||
- Internet connection
|
||||
|
||||
### macOS
|
||||
- macOS 11 (Big Sur) or later
|
||||
- Homebrew will be installed automatically
|
||||
- Admin/sudo access
|
||||
- Internet connection
|
||||
|
||||
## What Gets Installed
|
||||
|
||||
### Common (All OS)
|
||||
- Homebrew package manager
|
||||
- Tailscale (mesh VPN)
|
||||
- UFW firewall (SSH + Tailscale ports only)
|
||||
- Docker CE + Compose V2 (for sandboxes)
|
||||
- Node.js 22.x + pnpm
|
||||
- OpenClaw via `pnpm install -g openclaw@latest`
|
||||
- Essential development tools
|
||||
- Git, zsh, oh-my-zsh
|
||||
|
||||
### Linux-Specific
|
||||
- Docker CE + Compose V2
|
||||
- UFW firewall (configured)
|
||||
- Tailscale VPN
|
||||
- systemd service
|
||||
|
||||
### macOS-Specific
|
||||
- Docker Desktop (via Homebrew Cask)
|
||||
- Application Firewall
|
||||
- Tailscale app
|
||||
- OpenClaw on host (not containerized)
|
||||
- Systemd service (auto-start)
|
||||
|
||||
## Manual Installation
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
- Runs `pnpm install` and `pnpm build`
|
||||
- Symlinks `bin/openclaw.js` to `~/.local/bin/openclaw`
|
||||
- Adds aliases: `openclaw-rebuild`, `openclaw-dev`, `openclaw-pull`
|
||||
- Sets `CLAWDBOT_DEV_DIR` environment variable
|
||||
- Sets `OPENCLAW_DEV_DIR` environment variable
|
||||
|
||||
**Usage**:
|
||||
```bash
|
||||
@ -87,7 +87,7 @@ ansible-playbook playbook.yml --ask-become-pass \
|
||||
- Added Homebrew to PATH
|
||||
- Enhanced security with ProtectSystem and ProtectHome
|
||||
|
||||
### 8. Clawdbot Installation via pnpm
|
||||
### 8. OpenClaw Installation via pnpm
|
||||
- **File**: `roles/openclaw/tasks/openclaw.yml`
|
||||
- Changed from `pnpm add -g` to `pnpm install -g openclaw@latest`
|
||||
- Added verification step
|
||||
|
||||
@ -184,7 +184,7 @@ The following aliases are added to `.bashrc`:
|
||||
Plus an environment variable:
|
||||
|
||||
```bash
|
||||
export CLAWDBOT_DEV_DIR="$HOME/code/openclaw"
|
||||
export OPENCLAW_DEV_DIR="$HOME/code/openclaw"
|
||||
```
|
||||
|
||||
## Configuration Variables
|
||||
|
||||
@ -7,7 +7,7 @@ description: Firewall configuration, Docker isolation, and security hardening de
|
||||
|
||||
## Overview
|
||||
|
||||
This playbook implements a multi-layer defense strategy to secure Clawdbot installations.
|
||||
This playbook implements a multi-layer defense strategy to secure OpenClaw installations.
|
||||
|
||||
## Security Layers
|
||||
|
||||
@ -71,24 +71,24 @@ Container processes run as unprivileged `openclaw` user.
|
||||
|
||||
### Layer 6: Systemd Hardening
|
||||
|
||||
The clawdbot service runs with security restrictions:
|
||||
The openclaw service runs with security restrictions:
|
||||
|
||||
- `NoNewPrivileges=true` - Prevents privilege escalation
|
||||
- `PrivateTmp=true` - Isolated /tmp directory
|
||||
- `ProtectSystem=strict` - Read-only system directories
|
||||
- `ProtectHome=read-only` - Limited home directory access
|
||||
- `ReadWritePaths` - Only ~/.clawdbot is writable
|
||||
- `ReadWritePaths` - Only ~/.openclaw is writable
|
||||
|
||||
### Layer 7: Scoped Sudo Access
|
||||
|
||||
The clawdbot user has limited sudo permissions (not full root):
|
||||
The openclaw user has limited sudo permissions (not full root):
|
||||
|
||||
```bash
|
||||
# Allowed commands only:
|
||||
- systemctl start/stop/restart/status clawdbot
|
||||
- systemctl start/stop/restart/status openclaw
|
||||
- systemctl daemon-reload
|
||||
- tailscale commands
|
||||
- journalctl for clawdbot logs
|
||||
- journalctl for openclaw logs
|
||||
```
|
||||
|
||||
### Layer 8: Automatic Security Updates
|
||||
@ -192,5 +192,5 @@ After installation, verify:
|
||||
## Reporting Security Issues
|
||||
|
||||
If you discover a security vulnerability, please report it privately:
|
||||
- Clawdbot: https://github.com/clawdbot/clawdbot/security
|
||||
- This installer: https://github.com/openclaw/clawdbot-ansible/security
|
||||
- OpenClaw: https://github.com/openclaw/openclaw/security
|
||||
- This installer: https://github.com/openclaw/openclaw-ansible/security
|
||||
|
||||
12
install.sh
12
install.sh
@ -31,15 +31,11 @@ echo -e "${GREEN}╚════════════════════
|
||||
echo ""
|
||||
|
||||
# Detect operating system
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
OS_TYPE="macos"
|
||||
echo -e "${GREEN}Detected: macOS${NC}"
|
||||
elif command -v apt-get &> /dev/null; then
|
||||
OS_TYPE="linux"
|
||||
echo -e "${GREEN}Detected: Debian/Ubuntu Linux${NC}"
|
||||
if command -v apt-get &> /dev/null; then
|
||||
echo -e "${GREEN}✓ Detected: Debian/Ubuntu Linux${NC}"
|
||||
else
|
||||
echo -e "${RED}Error: Unsupported operating system.${NC}"
|
||||
echo -e "${RED}This installer supports: Debian/Ubuntu and macOS${NC}"
|
||||
echo -e "${RED}✗ Error: Unsupported operating system${NC}"
|
||||
echo -e "${RED} This installer supports: Debian/Ubuntu Linux only${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
56
playbook.yml
56
playbook.yml
@ -18,28 +18,31 @@
|
||||
|
||||
- name: Detect operating system
|
||||
ansible.builtin.set_fact:
|
||||
is_macos: "{{ ansible_os_family == 'Darwin' }}"
|
||||
is_linux: "{{ ansible_os_family == 'Debian' }}"
|
||||
is_debian: "{{ ansible_distribution in ['Debian', 'Ubuntu'] }}"
|
||||
is_redhat: "{{ ansible_os_family == 'RedHat' }}"
|
||||
|
||||
- name: Fail on unsupported macOS
|
||||
ansible.builtin.fail:
|
||||
msg: >-
|
||||
macOS bare-metal support has been deprecated and disabled.
|
||||
Please use a Linux VM or container instead.
|
||||
See README.md for details.
|
||||
when: ansible_os_family == 'Darwin'
|
||||
|
||||
- name: Display detected OS
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Detected OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
|
||||
OS Family: {{ ansible_os_family }}
|
||||
macOS: {{ is_macos }}
|
||||
Linux (Debian/Ubuntu): {{ is_debian }}
|
||||
Linux (RedHat/CentOS): {{ is_redhat }}
|
||||
|
||||
- name: Update apt cache and upgrade all packages (Debian/Ubuntu)
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
upgrade: dist
|
||||
cache_valid_time: 3600
|
||||
when: is_debian
|
||||
when: is_debian and not ci_test
|
||||
register: apt_upgrade_result
|
||||
ignore_errors: true
|
||||
|
||||
- name: Display apt upgrade results
|
||||
ansible.builtin.debug:
|
||||
@ -71,42 +74,13 @@
|
||||
run_once: true
|
||||
changed_when: false
|
||||
|
||||
- name: Check if Homebrew is installed
|
||||
ansible.builtin.stat:
|
||||
path: "{{ '/opt/homebrew/bin/brew' if is_macos else '/home/linuxbrew/.linuxbrew/bin/brew' }}"
|
||||
register: homebrew_check
|
||||
|
||||
- name: Install Homebrew (macOS and Linux)
|
||||
ansible.builtin.shell: |
|
||||
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
args:
|
||||
creates: "{{ '/opt/homebrew/bin/brew' if is_macos else '/home/linuxbrew/.linuxbrew/bin/brew' }}"
|
||||
when: not homebrew_check.stat.exists
|
||||
register: homebrew_install
|
||||
become: false
|
||||
|
||||
- name: Add Homebrew to PATH for current session (Linux)
|
||||
ansible.builtin.set_fact:
|
||||
ansible_env: "{{ ansible_env | combine({'PATH': '/home/linuxbrew/.linuxbrew/bin:' + (ansible_env.PATH | default(lookup('env', 'PATH')))}) }}"
|
||||
when: is_linux and not is_macos
|
||||
|
||||
- name: Add Homebrew to PATH for current session (macOS)
|
||||
ansible.builtin.set_fact:
|
||||
ansible_env: "{{ ansible_env | combine({'PATH': '/opt/homebrew/bin:' + (ansible_env.PATH | default(lookup('env', 'PATH')))}) }}"
|
||||
when: is_macos
|
||||
|
||||
- name: Display Homebrew installation status
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ Homebrew installed successfully"
|
||||
when: homebrew_install is defined and homebrew_install.changed
|
||||
|
||||
roles:
|
||||
- openclaw
|
||||
|
||||
post_tasks:
|
||||
- name: Copy ASCII art script
|
||||
ansible.builtin.copy:
|
||||
src: roles/openclaw/files/show-lobster.sh
|
||||
ansible.builtin.template:
|
||||
src: roles/openclaw/templates/show-lobster.sh.j2
|
||||
dest: /tmp/show-lobster.sh
|
||||
mode: '0755'
|
||||
|
||||
@ -133,7 +107,6 @@
|
||||
echo "Environment is configured:"
|
||||
echo " ✓ XDG_RUNTIME_DIR: ${XDG_RUNTIME_DIR:-not set}"
|
||||
echo " ✓ DBUS_SESSION_BUS_ADDRESS: ${DBUS_SESSION_BUS_ADDRESS:-not set}"
|
||||
echo " ✓ Homebrew: $(which brew 2>/dev/null || echo 'not found')"
|
||||
echo " ✓ OpenClaw: $(openclaw --version 2>/dev/null || echo 'not found')"
|
||||
echo ""
|
||||
echo "────────────────────────────────────────────────────────"
|
||||
@ -179,18 +152,19 @@
|
||||
echo " • Troubleshoot: openclaw doctor"
|
||||
echo " • List agents: openclaw agents list"
|
||||
echo ""
|
||||
echo "────────────────────────────────────────────────────────"
|
||||
{% if tailscale_enabled | default(false) %}echo "────────────────────────────────────────────────────────"
|
||||
echo "🌐 Connect Tailscale VPN (optional):"
|
||||
echo "────────────────────────────────────────────────────────"
|
||||
echo ""
|
||||
echo " exit"
|
||||
echo " sudo tailscale up"
|
||||
echo ""
|
||||
echo "────────────────────────────────────────────────────────"
|
||||
{% endif %}echo "────────────────────────────────────────────────────────"
|
||||
echo ""
|
||||
echo "Type 'exit' to return to your previous user"
|
||||
echo ""
|
||||
rm -f ~/.openclaw-welcome
|
||||
# Remove welcome message (suppress errors if already deleted)
|
||||
rm -f "$HOME/.openclaw-welcome" 2>/dev/null || true
|
||||
|
||||
- name: Add welcome message to .bashrc
|
||||
ansible.builtin.lineinfile:
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
---
|
||||
# OpenClaw default variables
|
||||
|
||||
# CI testing mode - skips tasks that require systemd, Docker-in-Docker, or kernel access
|
||||
ci_test: false
|
||||
|
||||
# Tailscale settings
|
||||
# WARNING: Tasks using tailscale_authkey MUST set no_log: true to prevent credential exposure
|
||||
tailscale_enabled: false # Set to true to install and configure Tailscale
|
||||
tailscale_authkey: "" # Optional: set to auto-connect during installation
|
||||
|
||||
# Node.js version
|
||||
@ -17,10 +22,6 @@ openclaw_config_dir: "{{ openclaw_home }}/.openclaw"
|
||||
openclaw_user: openclaw
|
||||
openclaw_home: /home/openclaw
|
||||
|
||||
# OS-specific settings (set dynamically in tasks)
|
||||
homebrew_prefix: "{{ '/opt/homebrew' if ansible_os_family == 'Darwin' else '/home/linuxbrew/.linuxbrew' }}"
|
||||
package_manager: "{{ 'brew' if ansible_os_family == 'Darwin' else 'apt' }}"
|
||||
|
||||
# Installation mode: 'release' or 'development'
|
||||
# release: Install via pnpm install -g openclaw@latest
|
||||
# development: Clone repo, build from source, link globally
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
---
|
||||
# macOS-specific Docker installation (Docker Desktop)
|
||||
|
||||
- name: Check if Docker Desktop is installed (macOS)
|
||||
ansible.builtin.stat:
|
||||
path: /Applications/Docker.app
|
||||
register: docker_desktop
|
||||
|
||||
- name: Install Docker Desktop via Homebrew Cask (macOS)
|
||||
community.general.homebrew_cask:
|
||||
name: docker
|
||||
state: present
|
||||
environment:
|
||||
PATH: "/opt/homebrew/bin:{{ ansible_env.PATH }}"
|
||||
when: not docker_desktop.stat.exists
|
||||
|
||||
- name: Wait for Docker Desktop to be available (macOS)
|
||||
ansible.builtin.wait_for:
|
||||
path: /var/run/docker.sock
|
||||
timeout: 120
|
||||
when: not docker_desktop.stat.exists
|
||||
|
||||
- name: Display Docker Desktop installation note (macOS)
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
ℹ️ Docker Desktop installed on macOS.
|
||||
Please ensure Docker Desktop is running and has been configured.
|
||||
You may need to start it manually from Applications.
|
||||
when: not docker_desktop.stat.exists
|
||||
|
||||
- name: Verify Docker is accessible
|
||||
ansible.builtin.command: docker --version
|
||||
register: docker_version
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Display Docker version
|
||||
ansible.builtin.debug:
|
||||
msg: "Docker installed: {{ docker_version.stdout }}"
|
||||
when: docker_version.rc == 0
|
||||
@ -1,10 +0,0 @@
|
||||
---
|
||||
# Main Docker orchestration - delegates to OS-specific tasks
|
||||
|
||||
- name: Include Linux Docker installation
|
||||
ansible.builtin.include_tasks: docker-linux.yml
|
||||
when: ansible_os_family == 'Debian'
|
||||
|
||||
- name: Include macOS Docker installation
|
||||
ansible.builtin.include_tasks: docker-macos.yml
|
||||
when: ansible_os_family == 'Darwin'
|
||||
@ -103,6 +103,7 @@
|
||||
port: '41641'
|
||||
proto: udp
|
||||
comment: 'Tailscale'
|
||||
when: tailscale_enabled | bool
|
||||
|
||||
- name: Get default network interface
|
||||
ansible.builtin.shell:
|
||||
@ -113,6 +114,14 @@
|
||||
register: default_interface
|
||||
changed_when: false
|
||||
|
||||
- name: Validate default network interface was detected
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- default_interface.stdout is defined
|
||||
- default_interface.stdout | length > 0
|
||||
fail_msg: "Failed to detect default network interface. Cannot configure firewall rules safely."
|
||||
success_msg: "Default network interface detected: {{ default_interface.stdout }}"
|
||||
|
||||
- name: Create UFW after.rules for Docker isolation
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/ufw/after.rules
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
---
|
||||
# macOS-specific firewall configuration (pf)
|
||||
|
||||
- name: Display macOS firewall information
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
ℹ️ macOS uses built-in Application Firewall and pf.
|
||||
Docker Desktop on macOS handles its own network isolation.
|
||||
Consider configuring the Application Firewall via System Preferences.
|
||||
|
||||
- name: Check macOS firewall status
|
||||
ansible.builtin.command: /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
|
||||
register: macos_firewall_status
|
||||
changed_when: false
|
||||
become: true
|
||||
|
||||
- name: Display firewall status
|
||||
ansible.builtin.debug:
|
||||
msg: "macOS Firewall Status: {{ macos_firewall_status.stdout }}"
|
||||
|
||||
- name: Enable macOS Application Firewall (if disabled)
|
||||
ansible.builtin.command: /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on
|
||||
when: "'disabled' in macos_firewall_status.stdout.lower()"
|
||||
become: true
|
||||
changed_when: true
|
||||
|
||||
- name: Allow Tailscale through firewall
|
||||
ansible.builtin.command: /usr/libexec/ApplicationFirewall/socketfilterfw --add /Applications/Tailscale.app
|
||||
failed_when: false
|
||||
become: true
|
||||
changed_when: false
|
||||
@ -1,10 +0,0 @@
|
||||
---
|
||||
# Main firewall orchestration - delegates to OS-specific tasks
|
||||
|
||||
- name: Include Linux firewall configuration
|
||||
ansible.builtin.include_tasks: firewall-linux.yml
|
||||
when: ansible_os_family == 'Debian'
|
||||
|
||||
- name: Include macOS firewall configuration
|
||||
ansible.builtin.include_tasks: firewall-macos.yml
|
||||
when: ansible_os_family == 'Darwin'
|
||||
@ -3,16 +3,19 @@
|
||||
ansible.builtin.include_tasks: system-tools.yml
|
||||
|
||||
- name: Include Tailscale installation tasks
|
||||
ansible.builtin.include_tasks: tailscale.yml
|
||||
ansible.builtin.include_tasks: tailscale-linux.yml
|
||||
when: tailscale_enabled | bool
|
||||
|
||||
- name: Include user creation tasks
|
||||
ansible.builtin.include_tasks: user.yml
|
||||
|
||||
- name: Include Docker installation tasks
|
||||
ansible.builtin.include_tasks: docker.yml
|
||||
ansible.builtin.include_tasks: docker-linux.yml
|
||||
when: not ci_test
|
||||
|
||||
- name: Include firewall configuration tasks
|
||||
ansible.builtin.include_tasks: firewall.yml
|
||||
ansible.builtin.include_tasks: firewall-linux.yml
|
||||
when: not ci_test
|
||||
|
||||
- name: Include Node.js installation tasks
|
||||
ansible.builtin.include_tasks: nodejs.yml
|
||||
|
||||
@ -42,10 +42,15 @@
|
||||
name: nodejs
|
||||
state: present
|
||||
|
||||
- name: Check if pnpm is already installed
|
||||
ansible.builtin.command: pnpm --version
|
||||
register: pnpm_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Install pnpm globally
|
||||
ansible.builtin.command: npm install -g pnpm
|
||||
args:
|
||||
creates: /usr/local/bin/pnpm
|
||||
when: pnpm_check.rc != 0
|
||||
|
||||
- name: Verify Node.js installation
|
||||
ansible.builtin.command: node --version
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
become_user: "{{ openclaw_user }}"
|
||||
environment:
|
||||
PNPM_HOME: "{{ openclaw_home }}/.local/share/pnpm"
|
||||
PATH: "{{ openclaw_home }}/.local/bin:/home/linuxbrew/.linuxbrew/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
|
||||
PATH: "{{ openclaw_home }}/.local/bin:{{ openclaw_home }}/.local/share/pnpm:/usr/local/bin:/usr/bin:/bin"
|
||||
HOME: "{{ openclaw_home }}"
|
||||
register: pnpm_install_result
|
||||
changed_when: "'Already up to date' not in pnpm_install_result.stdout"
|
||||
@ -63,7 +63,7 @@
|
||||
become_user: "{{ openclaw_user }}"
|
||||
environment:
|
||||
PNPM_HOME: "{{ openclaw_home }}/.local/share/pnpm"
|
||||
PATH: "{{ openclaw_home }}/.local/bin:/home/linuxbrew/.linuxbrew/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
|
||||
PATH: "{{ openclaw_home }}/.local/bin:{{ openclaw_home }}/.local/share/pnpm:/usr/local/bin:/usr/bin:/bin"
|
||||
HOME: "{{ openclaw_home }}"
|
||||
register: pnpm_build_result
|
||||
changed_when: true # Build always changes dist/ directory
|
||||
@ -106,12 +106,14 @@
|
||||
|
||||
- name: Verify openclaw installation from development build
|
||||
ansible.builtin.shell:
|
||||
cmd: "{{ openclaw_home }}/.local/bin/openclaw --version"
|
||||
cmd: openclaw --version
|
||||
executable: /bin/bash
|
||||
become: true
|
||||
become_user: "{{ openclaw_user }}"
|
||||
environment:
|
||||
PATH: "{{ openclaw_home }}/.local/bin:/usr/local/bin:/usr/bin:/bin"
|
||||
PNPM_HOME: "{{ openclaw_home }}/.local/share/pnpm"
|
||||
PATH: "{{ openclaw_home }}/.local/bin:{{ openclaw_home }}/.local/share/pnpm:/usr/local/bin:/usr/bin:/bin"
|
||||
HOME: "{{ openclaw_home }}"
|
||||
register: openclaw_dev_version
|
||||
changed_when: false
|
||||
|
||||
|
||||
@ -9,17 +9,21 @@
|
||||
become_user: "{{ openclaw_user }}"
|
||||
environment:
|
||||
PNPM_HOME: "{{ openclaw_home }}/.local/share/pnpm"
|
||||
PATH: "{{ openclaw_home }}/.local/bin:/home/linuxbrew/.linuxbrew/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
|
||||
PATH: "{{ openclaw_home }}/.local/bin:{{ openclaw_home }}/.local/share/pnpm:/usr/local/bin:/usr/bin:/bin"
|
||||
HOME: "{{ openclaw_home }}"
|
||||
register: openclaw_install
|
||||
changed_when: "'Already up to date' not in openclaw_install.stdout"
|
||||
|
||||
- name: Verify openclaw installation
|
||||
ansible.builtin.shell:
|
||||
cmd: "{{ openclaw_home }}/.local/bin/openclaw --version"
|
||||
cmd: openclaw --version
|
||||
executable: /bin/bash
|
||||
become: true
|
||||
become_user: "{{ openclaw_user }}"
|
||||
environment:
|
||||
PNPM_HOME: "{{ openclaw_home }}/.local/share/pnpm"
|
||||
PATH: "{{ openclaw_home }}/.local/bin:{{ openclaw_home }}/.local/share/pnpm:/usr/local/bin:/usr/bin:/bin"
|
||||
HOME: "{{ openclaw_home }}"
|
||||
register: openclaw_version
|
||||
changed_when: false
|
||||
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
---
|
||||
- name: Validate openclaw_install_mode
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- openclaw_install_mode in ["release", "development"]
|
||||
fail_msg: "Invalid openclaw_install_mode: '{{ openclaw_install_mode }}'. Must be 'release' or 'development'."
|
||||
success_msg: "Valid install mode: {{ openclaw_install_mode }}"
|
||||
|
||||
- name: Create OpenClaw directories (structure only, no config files)
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.path }}"
|
||||
@ -37,12 +44,24 @@
|
||||
- name: Configure pnpm for openclaw user
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
pnpm config set global-dir {{ openclaw_home }}/.local/share/pnpm
|
||||
pnpm config set global-bin-dir {{ openclaw_home }}/.local/bin
|
||||
CURRENT_GLOBAL_DIR=$(pnpm config get global-dir 2>/dev/null || echo "")
|
||||
CURRENT_BIN_DIR=$(pnpm config get global-bin-dir 2>/dev/null || echo "")
|
||||
CHANGED=0
|
||||
if [ "$CURRENT_GLOBAL_DIR" != "{{ openclaw_home }}/.local/share/pnpm" ]; then
|
||||
pnpm config set global-dir {{ openclaw_home }}/.local/share/pnpm
|
||||
CHANGED=1
|
||||
fi
|
||||
if [ "$CURRENT_BIN_DIR" != "{{ openclaw_home }}/.local/bin" ]; then
|
||||
pnpm config set global-bin-dir {{ openclaw_home }}/.local/bin
|
||||
CHANGED=1
|
||||
fi
|
||||
exit $CHANGED
|
||||
executable: /bin/bash
|
||||
become: true
|
||||
become_user: "{{ openclaw_user }}"
|
||||
changed_when: true # Always consider changed as pnpm config may update
|
||||
register: pnpm_config_result
|
||||
changed_when: pnpm_config_result.rc == 1
|
||||
failed_when: pnpm_config_result.rc > 1
|
||||
|
||||
- name: Display installation mode
|
||||
ansible.builtin.debug:
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
- name: Install essential system tools (Linux - apt)
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
# Shells
|
||||
- zsh
|
||||
# Editors
|
||||
- vim
|
||||
- nano
|
||||
@ -40,17 +38,12 @@
|
||||
- unzip
|
||||
- rsync
|
||||
- less
|
||||
# Build essentials for Homebrew on Linux
|
||||
# Build essentials for development
|
||||
- build-essential
|
||||
- file
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Set zsh as default shell for openclaw user (Linux)
|
||||
ansible.builtin.user:
|
||||
name: "{{ openclaw_user }}"
|
||||
shell: /usr/bin/zsh
|
||||
|
||||
- name: Deploy global vim configuration (Linux)
|
||||
ansible.builtin.template:
|
||||
src: vimrc.j2
|
||||
@ -58,61 +51,3 @@
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Configure .bashrc for openclaw user (Linux)
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ openclaw_home }}/.bashrc"
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - OpenClaw config"
|
||||
block: |
|
||||
# Enable 256 colors
|
||||
export TERM=xterm-256color
|
||||
export COLORTERM=truecolor
|
||||
|
||||
# Add Homebrew to PATH (Linux)
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
|
||||
# Add pnpm to PATH
|
||||
export PNPM_HOME="{{ openclaw_home }}/.local/share/pnpm"
|
||||
export PATH="{{ openclaw_home }}/.local/bin:$PNPM_HOME:$PATH"
|
||||
|
||||
# Color support for common tools
|
||||
export CLICOLOR=1
|
||||
export LS_COLORS='di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43:su=30;41:sg=30;46:tw=30;42:ow=30;43'
|
||||
|
||||
# Aliases
|
||||
alias ls='ls --color=auto'
|
||||
alias grep='grep --color=auto'
|
||||
alias ll='ls -lah'
|
||||
create: true
|
||||
owner: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_user }}"
|
||||
mode: '0644'
|
||||
|
||||
- name: Configure .zshrc for openclaw user (Linux)
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ openclaw_home }}/.zshrc"
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - OpenClaw config"
|
||||
block: |
|
||||
# Enable 256 colors
|
||||
export TERM=xterm-256color
|
||||
export COLORTERM=truecolor
|
||||
|
||||
# Add Homebrew to PATH (Linux)
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
|
||||
# Add pnpm to PATH
|
||||
export PNPM_HOME="{{ openclaw_home }}/.local/share/pnpm"
|
||||
export PATH="{{ openclaw_home }}/.local/bin:$PNPM_HOME:$PATH"
|
||||
|
||||
# Color support for common tools
|
||||
export CLICOLOR=1
|
||||
export LS_COLORS='di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43:su=30;41:sg=30;46:tw=30;42:ow=30;43'
|
||||
|
||||
# Aliases
|
||||
alias ls='ls --color=auto'
|
||||
alias grep='grep --color=auto'
|
||||
alias ll='ls -lah'
|
||||
create: true
|
||||
owner: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_user }}"
|
||||
mode: '0644'
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
---
|
||||
# macOS-specific system tools installation (Homebrew-based)
|
||||
|
||||
- name: Install essential system tools (macOS - Homebrew)
|
||||
community.general.homebrew:
|
||||
name:
|
||||
# Shells
|
||||
- zsh
|
||||
# Editors
|
||||
- vim
|
||||
- nano
|
||||
# Version control
|
||||
- git
|
||||
- git-lfs
|
||||
# Network tools
|
||||
- curl
|
||||
- wget
|
||||
- netcat
|
||||
- nmap
|
||||
- socat
|
||||
- telnet
|
||||
# Debugging tools
|
||||
- htop
|
||||
# System utilities
|
||||
- tmux
|
||||
- tree
|
||||
- jq
|
||||
- unzip
|
||||
- rsync
|
||||
state: present
|
||||
environment:
|
||||
PATH: "/opt/homebrew/bin:{{ ansible_env.PATH }}"
|
||||
|
||||
- name: Get current user shell (macOS)
|
||||
ansible.builtin.command: dscl . -read /Users/{{ openclaw_user }} UserShell
|
||||
register: current_shell
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Set zsh as default shell for openclaw user (macOS)
|
||||
ansible.builtin.command: chsh -s /bin/zsh {{ openclaw_user }}
|
||||
when: "'/bin/zsh' not in current_shell.stdout"
|
||||
changed_when: true
|
||||
|
||||
- name: Configure .zshrc for openclaw user (macOS)
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ openclaw_home }}/.zshrc"
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - OpenClaw config"
|
||||
block: |
|
||||
# Enable 256 colors
|
||||
export TERM=xterm-256color
|
||||
export COLORTERM=truecolor
|
||||
|
||||
# Add Homebrew to PATH (macOS)
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
|
||||
# Add pnpm to PATH
|
||||
export PATH="{{ openclaw_home }}/.local/bin:$PATH"
|
||||
|
||||
# Color support for common tools
|
||||
export CLICOLOR=1
|
||||
export LSCOLORS=ExFxCxDxBxegedabagacad
|
||||
|
||||
# Aliases
|
||||
alias ls='ls -G'
|
||||
alias grep='grep --color=auto'
|
||||
alias ll='ls -lah'
|
||||
create: true
|
||||
owner: "{{ openclaw_user }}"
|
||||
mode: '0644'
|
||||
@ -1,33 +1,11 @@
|
||||
---
|
||||
# Main system tools orchestration - delegates to OS-specific tasks
|
||||
# Main system tools orchestration - Linux only
|
||||
|
||||
- name: Include Linux system tools installation
|
||||
ansible.builtin.include_tasks: system-tools-linux.yml
|
||||
when: ansible_os_family == 'Debian'
|
||||
|
||||
- name: Include macOS system tools installation
|
||||
ansible.builtin.include_tasks: system-tools-macos.yml
|
||||
when: ansible_os_family == 'Darwin'
|
||||
|
||||
- name: Display unsupported OS warning
|
||||
ansible.builtin.fail:
|
||||
msg: "Unsupported OS family: {{ ansible_os_family }}. Only Debian/Ubuntu and macOS are supported."
|
||||
when: ansible_os_family not in ['Debian', 'Darwin']
|
||||
|
||||
# Common tasks for all operating systems
|
||||
|
||||
- name: Install oh-my-zsh for openclaw user
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
|
||||
creates: "{{ openclaw_home }}/.oh-my-zsh"
|
||||
executable: /bin/bash
|
||||
become: true
|
||||
become_user: "{{ openclaw_user }}"
|
||||
environment:
|
||||
HOME: "{{ openclaw_home }}"
|
||||
USER: "{{ openclaw_user }}"
|
||||
|
||||
- name: Configure git globally
|
||||
community.general.git_config:
|
||||
name: "{{ item.name }}"
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
---
|
||||
# macOS-specific Tailscale installation (Homebrew Cask)
|
||||
|
||||
- name: Check if Tailscale is already installed (macOS)
|
||||
ansible.builtin.stat:
|
||||
path: /Applications/Tailscale.app
|
||||
register: tailscale_app_macos
|
||||
|
||||
- name: Install Tailscale via Homebrew Cask (macOS)
|
||||
community.general.homebrew_cask:
|
||||
name: tailscale
|
||||
state: present
|
||||
environment:
|
||||
PATH: "/opt/homebrew/bin:{{ ansible_env.PATH }}"
|
||||
when: not tailscale_app_macos.stat.exists
|
||||
|
||||
- name: Check if Tailscale is running (macOS)
|
||||
ansible.builtin.command: /Applications/Tailscale.app/Contents/MacOS/Tailscale status --json
|
||||
register: tailscale_status_macos
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Display Tailscale setup instructions (macOS)
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "============================================"
|
||||
- "Tailscale installed on macOS"
|
||||
- "============================================"
|
||||
- ""
|
||||
- "To connect this Mac to your Tailnet:"
|
||||
- ""
|
||||
- "Option 1 - GUI:"
|
||||
- " 1. Open Tailscale from Applications"
|
||||
- " 2. Click 'Log in' and follow the web flow"
|
||||
- ""
|
||||
- "Option 2 - CLI:"
|
||||
- " sudo /Applications/Tailscale.app/Contents/MacOS/Tailscale up"
|
||||
- ""
|
||||
- "With auth key:"
|
||||
- " sudo /Applications/Tailscale.app/Contents/MacOS/Tailscale up --authkey tskey-auth-xxxxx"
|
||||
- ""
|
||||
- "Get auth key from: https://login.tailscale.com/admin/settings/keys"
|
||||
when: tailscale_status_macos.rc != 0
|
||||
@ -1,10 +0,0 @@
|
||||
---
|
||||
# Main Tailscale orchestration - delegates to OS-specific tasks
|
||||
|
||||
- name: Include Linux Tailscale installation
|
||||
ansible.builtin.include_tasks: tailscale-linux.yml
|
||||
when: ansible_os_family == 'Debian'
|
||||
|
||||
- name: Include macOS Tailscale installation
|
||||
ansible.builtin.include_tasks: tailscale-macos.yml
|
||||
when: ansible_os_family == 'Darwin'
|
||||
@ -9,6 +9,40 @@
|
||||
home: /home/openclaw
|
||||
state: present
|
||||
|
||||
- name: Ensure openclaw home directory has correct ownership
|
||||
ansible.builtin.file:
|
||||
path: "{{ openclaw_home }}"
|
||||
owner: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_user }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Configure .bashrc for openclaw user
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ openclaw_home }}/.bashrc"
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - OpenClaw config"
|
||||
block: |
|
||||
# Enable 256 colors
|
||||
export TERM=xterm-256color
|
||||
export COLORTERM=truecolor
|
||||
|
||||
# Add pnpm to PATH
|
||||
export PNPM_HOME="{{ openclaw_home }}/.local/share/pnpm"
|
||||
export PATH="{{ openclaw_home }}/.local/bin:$PNPM_HOME:$PATH"
|
||||
|
||||
# Color support for common tools
|
||||
export CLICOLOR=1
|
||||
export LS_COLORS='di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43:su=30;41:sg=30;46:tw=30;42:ow=30;43'
|
||||
|
||||
# Aliases
|
||||
alias ls='ls --color=auto'
|
||||
alias grep='grep --color=auto'
|
||||
alias ll='ls -lah'
|
||||
create: true
|
||||
owner: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_user }}"
|
||||
mode: '0644'
|
||||
|
||||
- name: Add openclaw user to sudoers with scoped NOPASSWD
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/sudoers.d/openclaw
|
||||
@ -60,22 +94,35 @@
|
||||
openclaw_user: openclaw
|
||||
openclaw_home: /home/openclaw
|
||||
|
||||
- name: Create .bash_profile to source .bashrc for login shells
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ openclaw_home }}/.bash_profile"
|
||||
owner: "{{ openclaw_user }}"
|
||||
group: "{{ openclaw_user }}"
|
||||
mode: '0644'
|
||||
content: |
|
||||
# .bash_profile - Executed for login shells
|
||||
# Source .bashrc to ensure environment is loaded for login shells
|
||||
if [ -f ~/.bashrc ]; then
|
||||
. ~/.bashrc
|
||||
fi
|
||||
|
||||
# Fix DBus issues for systemd user services
|
||||
- name: Get openclaw user ID
|
||||
ansible.builtin.command: id -u openclaw
|
||||
register: openclaw_uid
|
||||
changed_when: false
|
||||
when: ansible_os_family == 'Debian'
|
||||
when: ansible_os_family == 'Debian' and not ci_test
|
||||
|
||||
- name: Display openclaw user ID
|
||||
ansible.builtin.debug:
|
||||
msg: "OpenClaw user ID: {{ openclaw_uid.stdout }}"
|
||||
when: ansible_os_family == 'Debian'
|
||||
when: ansible_os_family == 'Debian' and not ci_test
|
||||
|
||||
- name: Enable lingering for openclaw user (allows systemd user services without login)
|
||||
ansible.builtin.command: loginctl enable-linger openclaw
|
||||
changed_when: false
|
||||
when: ansible_os_family == 'Debian'
|
||||
when: ansible_os_family == 'Debian' and not ci_test
|
||||
|
||||
- name: Create runtime directory for openclaw user
|
||||
ansible.builtin.file:
|
||||
@ -84,12 +131,12 @@
|
||||
owner: openclaw
|
||||
group: openclaw
|
||||
mode: '0700'
|
||||
when: ansible_os_family == 'Debian'
|
||||
when: ansible_os_family == 'Debian' and not ci_test
|
||||
|
||||
- name: Store openclaw UID as fact for later use
|
||||
ansible.builtin.set_fact:
|
||||
openclaw_uid_value: "{{ openclaw_uid.stdout }}"
|
||||
when: ansible_os_family == 'Debian'
|
||||
when: ansible_os_family == 'Debian' and not ci_test
|
||||
|
||||
# SSH key configuration
|
||||
- name: Create .ssh directory for openclaw user
|
||||
@ -127,7 +174,7 @@
|
||||
owner: openclaw
|
||||
group: openclaw
|
||||
mode: '0644'
|
||||
when: ansible_os_family == 'Debian'
|
||||
when: ansible_os_family == 'Debian' and not ci_test
|
||||
|
||||
- name: Set DBUS_SESSION_BUS_ADDRESS in .bashrc for openclaw user
|
||||
ansible.builtin.blockinfile:
|
||||
@ -144,4 +191,4 @@
|
||||
owner: openclaw
|
||||
group: openclaw
|
||||
mode: '0644'
|
||||
when: ansible_os_family == 'Debian'
|
||||
when: ansible_os_family == 'Debian' and not ci_test
|
||||
|
||||
@ -10,16 +10,16 @@ Group={{ openclaw_user }}
|
||||
WorkingDirectory={{ openclaw_home }}
|
||||
|
||||
# Environment variables
|
||||
Environment="PATH={{ openclaw_home }}/.local/bin:/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin"
|
||||
Environment="PNPM_HOME={{ openclaw_home }}/.local/share/pnpm"
|
||||
Environment="PATH={{ openclaw_home }}/.local/bin:{{ openclaw_home }}/.local/share/pnpm:/usr/local/bin:/usr/bin:/bin"
|
||||
Environment="HOME={{ openclaw_home }}"
|
||||
Environment="XDG_RUNTIME_DIR=/run/user/{{ openclaw_uid_value | default('1000') }}"
|
||||
Environment="XDG_RUNTIME_DIR=/run/user/{{ openclaw_uid_value }}"
|
||||
|
||||
# DBus session bus
|
||||
Environment="DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{{ openclaw_uid_value | default('1000') }}/bus"
|
||||
Environment="DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{{ openclaw_uid_value }}/bus"
|
||||
|
||||
# Start command
|
||||
ExecStart={{ openclaw_home }}/.local/bin/openclaw gateway
|
||||
ExecStart=openclaw gateway
|
||||
|
||||
# Restart policy
|
||||
Restart=always
|
||||
|
||||
42
roles/openclaw/templates/show-lobster.sh.j2
Normal file
42
roles/openclaw/templates/show-lobster.sh.j2
Normal file
@ -0,0 +1,42 @@
|
||||
#jinja2: lstrip_blocks: True
|
||||
{% raw %}#!/bin/bash
|
||||
cat << 'LOBSTER'
|
||||
[0;36m
|
||||
+====================================================+
|
||||
| |
|
||||
| [0;33mWelcome to OpenClaw! [0;31m🦞[0;36m |
|
||||
| |
|
||||
|[0;31m ,.---._ [0;36m|
|
||||
|[0;31m ,,,, / `, [0;36m|
|
||||
|[0;31m \\\ / '\_ ; [0;36m|
|
||||
|[0;31m |||| /\/``-.__\;' [0;36m|
|
||||
|[0;31m ::::/\/_ [0;36m|
|
||||
|[0;31m {{`-.__.-'(`(^^(^^^(^ 9 `.=========' [0;36m|
|
||||
|[0;31m{{{{{{ { ( ( ( ( (-----:= [0;36m|
|
||||
|[0;31m {{.-'~~'-.(,(,,(,,,(__6_.'=========. [0;36m|
|
||||
|[0;31m ::::\/\ [0;36m|
|
||||
|[0;31m |||| \/\ ,-'/, [0;36m|
|
||||
|[0;31m //// \ `` _/ ; [0;36m|
|
||||
|[0;31m '''' \ ` .' [0;36m|
|
||||
|[0;31m `---' [0;36m|
|
||||
| |
|
||||
| [0;32m✅ Installation Successful![0;36m |
|
||||
| |
|
||||
+====================================================+[0m
|
||||
LOBSTER
|
||||
|
||||
echo ""
|
||||
echo "🔒 Security Status:"
|
||||
echo " - UFW Firewall: ENABLED"
|
||||
{% endraw %}
|
||||
{% if tailscale_enabled | default(false) %}
|
||||
echo " - Open Ports: SSH (22) + Tailscale (41641/udp)"
|
||||
{% else %}
|
||||
echo " - Open Ports: SSH (22)"
|
||||
{% endif %}
|
||||
{% raw %}
|
||||
echo " - Docker isolation: ACTIVE"
|
||||
echo ""
|
||||
echo "📚 Documentation: https://github.com/openclaw/openclaw-ansible"
|
||||
echo ""
|
||||
{% endraw %}
|
||||
@ -32,7 +32,6 @@ if [ $PLAYBOOK_EXIT -eq 0 ]; then
|
||||
echo " • Configure OpenClaw (~/.openclaw/config.yml)"
|
||||
echo " • Login to messaging provider (WhatsApp/Telegram/Signal)"
|
||||
echo " • Test the gateway"
|
||||
echo " • Connect Tailscale VPN"
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
29
tests/Dockerfile.ubuntu2404
Normal file
29
tests/Dockerfile.ubuntu2404
Normal file
@ -0,0 +1,29 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install Ansible and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ansible \
|
||||
python3 \
|
||||
python3-apt \
|
||||
sudo \
|
||||
systemd \
|
||||
git \
|
||||
curl \
|
||||
ca-certificates \
|
||||
acl \
|
||||
gpg \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy project into container
|
||||
COPY . /opt/ansible
|
||||
WORKDIR /opt/ansible
|
||||
|
||||
# Install Ansible Galaxy collections
|
||||
RUN ansible-galaxy collection install -r requirements.yml
|
||||
|
||||
# Default: run the test entrypoint
|
||||
ENTRYPOINT ["bash", "tests/entrypoint.sh"]
|
||||
68
tests/README.md
Normal file
68
tests/README.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Docker CI Test Harness
|
||||
|
||||
This directory contains a Docker-based CI test harness for the Ansible playbook. It validates convergence, correctness, and idempotency by running the playbook inside an Ubuntu 24.04 container.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
bash tests/run-tests.sh
|
||||
|
||||
# Or specify a distro (currently only ubuntu2404 available)
|
||||
bash tests/run-tests.sh ubuntu2404
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
The test harness runs three sequential tests:
|
||||
|
||||
1. **Convergence**: Runs the playbook with `ci_test=true` to verify it completes without errors
|
||||
2. **Verification**: Runs `verify.yml` to assert the system is in the expected state
|
||||
3. **Idempotency**: Runs the playbook a second time and verifies `changed=0`
|
||||
|
||||
## Files
|
||||
|
||||
- `Dockerfile.ubuntu2404` - Ubuntu 24.04 container with Ansible pre-installed
|
||||
- `entrypoint.sh` - Test execution script (convergence → verification → idempotency)
|
||||
- `verify.yml` - Post-convergence assertions (user exists, packages installed, directories created, etc.)
|
||||
- `run-tests.sh` - Local test runner script
|
||||
|
||||
## CI Test Mode
|
||||
|
||||
The `ci_test` variable skips tasks that require:
|
||||
- Docker-in-Docker (Docker CE installation)
|
||||
- Kernel access (UFW/iptables firewall)
|
||||
- systemd services (loginctl, daemon installation)
|
||||
- External package installation (openclaw app install)
|
||||
|
||||
Everything else runs normally: package installation, user creation, Node.js/pnpm setup, directory structure, config file rendering, etc.
|
||||
|
||||
## What Gets Tested
|
||||
|
||||
| Component | Tested? | Notes |
|
||||
|-----------|---------|-------|
|
||||
| System packages (35+) | ✅ Yes | Full apt install |
|
||||
| User creation + config | ✅ Yes | User, .bashrc, sudoers, SSH dir |
|
||||
| Node.js + pnpm | ✅ Yes | Full install + version check |
|
||||
| Directory structure | ✅ Yes | All .openclaw/* dirs with perms |
|
||||
| Git global config | ✅ Yes | Aliases, default branch |
|
||||
| Vim config | ✅ Yes | Template rendering |
|
||||
| Docker CE install | ❌ No | Needs Docker-in-Docker |
|
||||
| UFW / iptables | ❌ No | Needs kernel access |
|
||||
| fail2ban / systemd | ❌ No | Needs running systemd |
|
||||
| Tailscale | ❌ No | Disabled by default already |
|
||||
| OpenClaw app install | ❌ No | External package |
|
||||
| Idempotency | ✅ Yes | Second run must have 0 changes |
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0` - All tests passed
|
||||
- `1` - Test failure (convergence failed, verification failed, or idempotency check failed)
|
||||
|
||||
## Development
|
||||
|
||||
To add tests for additional distributions:
|
||||
1. Create `Dockerfile.<distro>` (e.g., `Dockerfile.debian12`)
|
||||
2. Run: `bash tests/run-tests.sh <distro>`
|
||||
|
||||
The test harness automatically builds the image and runs the test suite.
|
||||
30
tests/entrypoint.sh
Executable file
30
tests/entrypoint.sh
Executable file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PLAYBOOK_ARGS=(-e ci_test=true -e ansible_become=false --connection=local)
|
||||
|
||||
# --- Step 1: Convergence ---
|
||||
echo "===> Step 1: Convergence test"
|
||||
ansible-playbook playbook.yml "${PLAYBOOK_ARGS[@]}"
|
||||
echo "===> Convergence: PASSED"
|
||||
|
||||
# --- Step 2: Verification ---
|
||||
echo "===> Step 2: Verification"
|
||||
ansible-playbook tests/verify.yml "${PLAYBOOK_ARGS[@]}"
|
||||
echo "===> Verification: PASSED"
|
||||
|
||||
# --- Step 3: Idempotency ---
|
||||
echo "===> Step 3: Idempotency test"
|
||||
IDEMPOTENCY_OUT=$(ansible-playbook playbook.yml "${PLAYBOOK_ARGS[@]}" 2>&1)
|
||||
echo "$IDEMPOTENCY_OUT"
|
||||
|
||||
CHANGED=$(echo "$IDEMPOTENCY_OUT" | tail -n 5 | grep -oP 'changed=\K[0-9]+' | head -1)
|
||||
if [ "${CHANGED:-1}" -eq 0 ]; then
|
||||
echo "===> Idempotency: PASSED (0 changed)"
|
||||
else
|
||||
echo "===> Idempotency: FAILED (changed=$CHANGED)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "===> All tests passed"
|
||||
11
tests/run-tests.sh
Executable file
11
tests/run-tests.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
DISTRO="${1:-ubuntu2404}"
|
||||
IMAGE="openclaw-ansible-test:${DISTRO}"
|
||||
|
||||
echo "Building test image (${DISTRO})..."
|
||||
docker build -t "$IMAGE" -f "tests/Dockerfile.${DISTRO}" .
|
||||
|
||||
echo "Running tests..."
|
||||
docker run --rm "$IMAGE"
|
||||
70
tests/verify.yml
Normal file
70
tests/verify.yml
Normal file
@ -0,0 +1,70 @@
|
||||
---
|
||||
- name: Verify playbook results
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
openclaw_user: openclaw
|
||||
openclaw_home: /home/openclaw
|
||||
|
||||
tasks:
|
||||
- name: Verify openclaw user exists
|
||||
ansible.builtin.command: id openclaw
|
||||
changed_when: false
|
||||
|
||||
- name: Verify critical packages installed
|
||||
ansible.builtin.command: "dpkg -s {{ item }}"
|
||||
loop: [git, curl, vim, jq, tmux, tree, htop]
|
||||
changed_when: false
|
||||
|
||||
- name: Verify Node.js installed
|
||||
ansible.builtin.command: node --version
|
||||
changed_when: false
|
||||
|
||||
- name: Verify pnpm installed
|
||||
ansible.builtin.command: pnpm --version
|
||||
changed_when: false
|
||||
|
||||
- name: Verify openclaw directory structure
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item.path }}"
|
||||
loop:
|
||||
- { path: "{{ openclaw_home }}/.openclaw", mode: "0755" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/sessions" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/credentials", mode: "0700" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/data" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/logs" }
|
||||
- { path: "{{ openclaw_home }}/.ssh", mode: "0700" }
|
||||
register: dir_checks
|
||||
|
||||
- name: Assert directories exist
|
||||
ansible.builtin.assert:
|
||||
that: item.stat.exists and item.stat.isdir
|
||||
fail_msg: "Directory missing: {{ item.item.path }}"
|
||||
loop: "{{ dir_checks.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.path }}"
|
||||
|
||||
- name: Assert credentials dir has restricted permissions
|
||||
ansible.builtin.assert:
|
||||
that: dir_checks.results[2].stat.mode == '0700'
|
||||
fail_msg: "credentials dir should be 0700"
|
||||
|
||||
- name: Verify sudoers file exists and is valid
|
||||
ansible.builtin.command: visudo -cf /etc/sudoers.d/openclaw
|
||||
changed_when: false
|
||||
|
||||
- name: Verify global vim config exists
|
||||
ansible.builtin.stat:
|
||||
path: /etc/vim/vimrc.local
|
||||
register: vimrc
|
||||
- ansible.builtin.assert:
|
||||
that: vimrc.stat.exists
|
||||
|
||||
- name: Verify git global config
|
||||
ansible.builtin.command: git config --global init.defaultBranch
|
||||
changed_when: false
|
||||
register: git_branch
|
||||
- ansible.builtin.assert:
|
||||
that: git_branch.stdout == 'main'
|
||||
Loading…
Reference in New Issue
Block a user