working on tests
This commit is contained in:
Executable
+68
@@ -0,0 +1,68 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "=== Installing LXD on Debian Bookworm ==="
|
||||||
|
|
||||||
|
# Check if snapd is installed
|
||||||
|
if ! command -v snap &> /dev/null; then
|
||||||
|
echo "Installing snapd..."
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y snapd
|
||||||
|
echo "Enabling snapd service..."
|
||||||
|
sudo systemctl enable --now snapd.socket
|
||||||
|
# Wait for snapd to be ready
|
||||||
|
sleep 5
|
||||||
|
else
|
||||||
|
echo "snapd already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if LXD is already installed
|
||||||
|
if snap list lxd &> /dev/null; then
|
||||||
|
echo "LXD already installed via snap"
|
||||||
|
else
|
||||||
|
echo "Installing LXD via snap..."
|
||||||
|
sudo snap install lxd
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if LXD is initialized
|
||||||
|
if sudo lxd init --dump &> /dev/null; then
|
||||||
|
echo "LXD already initialized"
|
||||||
|
else
|
||||||
|
echo "Initializing LXD with default settings..."
|
||||||
|
sudo lxd init --auto
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Configuring LXD network for Docker compatibility..."
|
||||||
|
lxc network set lxdbr0 ipv4.firewall false 2>/dev/null || true
|
||||||
|
lxc network set lxdbr0 ipv6.firewall false 2>/dev/null || true
|
||||||
|
lxc network set lxdbr0 ipv4.nat true 2>/dev/null || true
|
||||||
|
|
||||||
|
LXD_SUBNET=$(lxc network get lxdbr0 ipv4.address)
|
||||||
|
if ! sudo iptables -t nat -C POSTROUTING -s "$LXD_SUBNET" ! -d "$LXD_SUBNET" -j MASQUERADE 2>/dev/null; then
|
||||||
|
sudo iptables -t nat -I POSTROUTING -s "$LXD_SUBNET" ! -d "$LXD_SUBNET" -j MASQUERADE
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v docker &>/dev/null; then
|
||||||
|
if ! sudo iptables -C DOCKER-USER -i lxdbr0 -j ACCEPT 2>/dev/null; then
|
||||||
|
sudo iptables -I DOCKER-USER -i lxdbr0 -j ACCEPT
|
||||||
|
fi
|
||||||
|
if ! sudo iptables -C DOCKER-USER -o lxdbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
|
||||||
|
sudo iptables -I DOCKER-USER -o lxdbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ LXD installation complete!"
|
||||||
|
echo " Default network bridge (lxdbr0) configured for internet access"
|
||||||
|
|
||||||
|
# Add user to lxd group for passwordless access
|
||||||
|
if groups "$USER" | grep -q "\blxd\b"; then
|
||||||
|
echo " User '$USER' already in lxd group"
|
||||||
|
else
|
||||||
|
echo "Adding user '$USER' to lxd group..."
|
||||||
|
sudo usermod -a -G lxd "$USER"
|
||||||
|
echo "✓ User added to lxd group"
|
||||||
|
echo ""
|
||||||
|
echo "IMPORTANT: You need to log out and back in for group changes to take effect"
|
||||||
|
echo "Or run: newgrp lxd"
|
||||||
|
fi
|
||||||
@@ -71,3 +71,9 @@ def install_zoxide(c):
|
|||||||
@task(install_apt_packages, install_docker, install_uv, install_claude, install_fzf, install_zoxide)
|
@task(install_apt_packages, install_docker, install_uv, install_claude, install_fzf, install_zoxide)
|
||||||
def bootstrap(c):
|
def bootstrap(c):
|
||||||
"""Install all base tools."""
|
"""Install all base tools."""
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def test(c):
|
||||||
|
"""Run tasks in an ephemeral LXD container."""
|
||||||
|
run(c, "pytest tests/ -v -s")
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
from collections.abc import Generator
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
CONTAINER = "cli-tools-test"
|
||||||
|
IMAGE = "images:debian/bookworm"
|
||||||
|
REPO_ROOT = "/home/jev/projects/cli-tools"
|
||||||
|
|
||||||
|
|
||||||
|
def _lxc(*args: str, capture: bool = False) -> subprocess.CompletedProcess:
|
||||||
|
return subprocess.run(
|
||||||
|
["lxc", *args],
|
||||||
|
check=True,
|
||||||
|
capture_output=capture,
|
||||||
|
text=capture,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_network(container: str, retries: int = 20, delay: float = 2.0) -> None:
|
||||||
|
print("[container] Waiting for network...", flush=True)
|
||||||
|
for _ in range(retries):
|
||||||
|
result = subprocess.run(
|
||||||
|
["lxc", "exec", container, "--", "bash", "-c", "ping -c1 8.8.8.8 &>/dev/null"],
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
print("[container] Network ready", flush=True)
|
||||||
|
return
|
||||||
|
time.sleep(delay)
|
||||||
|
raise RuntimeError(f"Container {container!r} did not get network access")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def container() -> Generator[str, None, None]:
|
||||||
|
# Clean up any stale container from a previous run
|
||||||
|
subprocess.run(["lxc", "delete", "--force", CONTAINER], capture_output=True)
|
||||||
|
|
||||||
|
print(f"\n[container] Launching {CONTAINER}...", flush=True)
|
||||||
|
_lxc("launch", IMAGE, CONTAINER)
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
_wait_for_network(CONTAINER)
|
||||||
|
|
||||||
|
print("[container] Installing sudo and python3-pip...", flush=True)
|
||||||
|
_lxc(
|
||||||
|
"exec", CONTAINER, "--",
|
||||||
|
"bash", "-c",
|
||||||
|
"""
|
||||||
|
apt-get update -qq
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq sudo python3-pip
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("[container] Creating jev user...", flush=True)
|
||||||
|
_lxc(
|
||||||
|
"exec", CONTAINER, "--",
|
||||||
|
"bash", "-c",
|
||||||
|
"""
|
||||||
|
useradd -m -s /bin/bash -u 1000 jev
|
||||||
|
usermod -aG sudo jev
|
||||||
|
echo 'jev ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/jev
|
||||||
|
chmod 440 /etc/sudoers.d/jev
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("[container] Installing invoke...", flush=True)
|
||||||
|
_lxc(
|
||||||
|
"exec", CONTAINER, "--",
|
||||||
|
"bash", "-c",
|
||||||
|
"pip3 install invoke --quiet --break-system-packages",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("[container] Mounting repo...", flush=True)
|
||||||
|
_lxc("config", "device", "add", CONTAINER, "repo", "disk",
|
||||||
|
f"source={REPO_ROOT}", "path=/home/jev/cli-tools")
|
||||||
|
print("[container] Ready\n", flush=True)
|
||||||
|
|
||||||
|
yield CONTAINER
|
||||||
|
|
||||||
|
print(f"\n[container] Destroying {CONTAINER}...", flush=True)
|
||||||
|
_lxc("delete", "--force", CONTAINER)
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def lxc_exec(container: str, cmd: str, check: bool = True) -> subprocess.CompletedProcess:
|
||||||
|
"""Run a command in the container as the jev user."""
|
||||||
|
return subprocess.run(
|
||||||
|
["lxc", "exec", container, "--user", "1000", "--", "bash", "-lc", cmd],
|
||||||
|
check=check,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_task(container: str, task: str) -> None:
|
||||||
|
lxc_exec(container, f"cd /home/jev/cli-tools && invoke {task.replace('_', '-')}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_apt_packages(container: str) -> None:
|
||||||
|
run_task(container, "install_apt_packages")
|
||||||
|
for binary in ["git", "micro", "tree", "detox"]:
|
||||||
|
result = lxc_exec(container, f"which {binary}", check=False)
|
||||||
|
assert result.returncode == 0, f"{binary!r} not found after install_apt_packages"
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_docker(container: str) -> None:
|
||||||
|
run_task(container, "install_docker")
|
||||||
|
result = lxc_exec(container, "docker --version", check=False)
|
||||||
|
assert result.returncode == 0, "docker not found after install_docker"
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_uv(container: str) -> None:
|
||||||
|
run_task(container, "install_uv")
|
||||||
|
result = lxc_exec(container, "~/.local/bin/uv --version", check=False)
|
||||||
|
assert result.returncode == 0, "uv not found after install_uv"
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_fzf(container: str) -> None:
|
||||||
|
run_task(container, "install_fzf")
|
||||||
|
for binary in ["fzf", "batcat"]:
|
||||||
|
result = lxc_exec(container, f"which {binary}", check=False)
|
||||||
|
assert result.returncode == 0, f"{binary!r} not found after install_fzf"
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_zoxide(container: str) -> None:
|
||||||
|
run_task(container, "install_zoxide")
|
||||||
|
result = lxc_exec(container, "which zoxide", check=False)
|
||||||
|
assert result.returncode == 0, "zoxide not found after install_zoxide"
|
||||||
Reference in New Issue
Block a user