Add WSL support and fix config formatting
All checks were successful
Ansible Lint Check / check-ansible (push) Successful in 1m17s
Nix Format Check / check-format (push) Successful in 44s
Python Lint Check / check-python (push) Successful in 9s

This commit is contained in:
2025-10-22 16:18:08 +02:00
parent 77424506d6
commit e1b07a6edf
7 changed files with 253 additions and 168 deletions

View File

@@ -5,4 +5,7 @@ mennos-desktop ansible_connection=local
[servers] [servers]
mennos-vps ansible_connection=local mennos-vps ansible_connection=local
mennos-server ansible_connection=local mennos-server ansible_connection=local
mennos-rtlsdr-pc ansible_connection=local mennos-rtlsdr-pc ansible_connection=local
[wsl]
mennos-desktopw ansible_connection=local

View File

@@ -2,18 +2,18 @@
- name: Configure all hosts - name: Configure all hosts
hosts: all hosts: all
handlers: handlers:
- name: Import handler tasks - name: Import handler tasks
ansible.builtin.import_tasks: handlers/main.yml ansible.builtin.import_tasks: handlers/main.yml
gather_facts: true gather_facts: true
tasks: tasks:
- name: Include global tasks - name: Include global tasks
ansible.builtin.import_tasks: tasks/global/global.yml ansible.builtin.import_tasks: tasks/global/global.yml
- name: Include workstation tasks - name: Include workstation tasks
ansible.builtin.import_tasks: tasks/workstations/workstation.yml ansible.builtin.import_tasks: tasks/workstations/workstation.yml
when: inventory_hostname in ['mennos-laptop', 'mennos-desktop'] when: inventory_hostname in ['mennos-laptop', 'mennos-desktop']
- name: Include server tasks - name: Include server tasks
ansible.builtin.import_tasks: tasks/servers/server.yml ansible.builtin.import_tasks: tasks/servers/server.yml
when: inventory_hostname in ['mennos-vps', 'mennos-server', 'mennos-rtlsdr-pc'] when: inventory_hostname in ['mennos-vps', 'mennos-server', 'mennos-rtlsdr-pc', 'mennos-desktopw']

View File

@@ -1,161 +1,163 @@
--- ---
- name: Server setup - name: Server setup
block: block:
- name: Ensure openssh-server is installed on Arch-based systems - name: Ensure openssh-server is installed on Arch-based systems
ansible.builtin.package: ansible.builtin.package:
name: openssh name: openssh
state: present state: present
when: ansible_pkg_mgr == 'pacman' when: ansible_pkg_mgr == 'pacman' and 'microsoft-standard-WSL2' not in ansible_kernel
become: true
- name: Ensure openssh-server is installed on non-Arch systems - name: Ensure openssh-server is installed on non-Arch systems
ansible.builtin.package: ansible.builtin.package:
name: openssh-server name: openssh-server
state: present state: present
when: ansible_pkg_mgr != 'pacman' when: ansible_pkg_mgr != 'pacman' and 'microsoft-standard-WSL2' not in ansible_kernel
become: true
- name: Ensure Borg is installed on Arch-based systems - name: Ensure Borg is installed on Arch-based systems
ansible.builtin.package: ansible.builtin.package:
name: borg name: borg
state: present state: present
become: true become: true
when: ansible_pkg_mgr == 'pacman' when: ansible_pkg_mgr == 'pacman'
- name: Ensure Borg is installed on Debian/Ubuntu systems - name: Ensure Borg is installed on Debian/Ubuntu systems
ansible.builtin.package: ansible.builtin.package:
name: borgbackup name: borgbackup
state: present state: present
become: true become: true
when: ansible_pkg_mgr != 'pacman' when: ansible_pkg_mgr != 'pacman'
- name: Include JuiceFS tasks - name: Include JuiceFS tasks
ansible.builtin.include_tasks: juicefs.yml ansible.builtin.include_tasks: juicefs.yml
tags: tags:
- juicefs - juicefs
- name: Include Dynamic DNS tasks - name: Include Dynamic DNS tasks
ansible.builtin.include_tasks: dynamic-dns.yml ansible.builtin.include_tasks: dynamic-dns.yml
tags: tags:
- dynamic-dns - dynamic-dns
- name: Include Borg Backup tasks - name: Include Borg Backup tasks
ansible.builtin.include_tasks: borg-backup.yml ansible.builtin.include_tasks: borg-backup.yml
tags: tags:
- borg-backup - borg-backup
- name: Include Borg Local Sync tasks - name: Include Borg Local Sync tasks
ansible.builtin.include_tasks: borg-local-sync.yml ansible.builtin.include_tasks: borg-local-sync.yml
tags: tags:
- borg-local-sync - borg-local-sync
- name: System performance optimizations - name: System performance optimizations
ansible.posix.sysctl: ansible.posix.sysctl:
name: "{{ item.name }}" name: "{{ item.name }}"
value: "{{ item.value }}" value: "{{ item.value }}"
state: present state: present
reload: true reload: true
become: true become: true
loop: loop:
- { name: "fs.file-max", value: "2097152" } # Max open files for the entire system - { name: "fs.file-max", value: "2097152" } # Max open files for the entire system
- { name: "vm.max_map_count", value: "16777216" } # Max memory map areas a process can have - { name: "vm.max_map_count", value: "16777216" } # Max memory map areas a process can have
- { name: "vm.swappiness", value: "10" } # Controls how aggressively the kernel swaps out memory - { name: "vm.swappiness", value: "10" } # Controls how aggressively the kernel swaps out memory
- { name: "vm.vfs_cache_pressure", value: "50" } # Controls kernel's tendency to reclaim memory for directory/inode caches - { name: "vm.vfs_cache_pressure", value: "50" } # Controls kernel's tendency to reclaim memory for directory/inode caches
- { name: "net.core.somaxconn", value: "65535" } # Max pending connections for a listening socket - { name: "net.core.somaxconn", value: "65535" } # Max pending connections for a listening socket
- { name: "net.core.netdev_max_backlog", value: "65535" } # Max packets queued on network interface input - { name: "net.core.netdev_max_backlog", value: "65535" } # Max packets queued on network interface input
- { name: "net.ipv4.tcp_fin_timeout", value: "30" } # How long sockets stay in FIN-WAIT-2 state - { name: "net.ipv4.tcp_fin_timeout", value: "30" } # How long sockets stay in FIN-WAIT-2 state
- { name: "net.ipv4.tcp_tw_reuse", value: "1" } # Allows reusing TIME_WAIT sockets for new outgoing connections - { name: "net.ipv4.tcp_tw_reuse", value: "1" } # Allows reusing TIME_WAIT sockets for new outgoing connections
- name: Include service tasks - name: Include service tasks
ansible.builtin.include_tasks: "services/{{ item.name }}/{{ item.name }}.yml" ansible.builtin.include_tasks: "services/{{ item.name }}/{{ item.name }}.yml"
loop: "{{ services | selectattr('enabled', 'equalto', true) | selectattr('hosts', 'contains', inventory_hostname) | list if specific_service is not defined else services | selectattr('name', 'equalto', specific_service) | selectattr('enabled', 'equalto', true) | selectattr('hosts', 'contains', inventory_hostname) | list }}" loop: "{{ services | selectattr('enabled', 'equalto', true) | selectattr('hosts', 'contains', inventory_hostname) | list if specific_service is not defined else services | selectattr('name', 'equalto', specific_service) | selectattr('enabled', 'equalto', true) | selectattr('hosts', 'contains', inventory_hostname) | list }}"
loop_control: loop_control:
label: "{{ item.name }}" label: "{{ item.name }}"
tags: tags:
- services - services
- always - always
vars: vars:
services: services:
- name: dashy - name: dashy
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: gitea - name: gitea
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: factorio - name: factorio
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: dozzle - name: dozzle
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: beszel - name: beszel
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: caddy - name: caddy
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: golink - name: golink
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: immich - name: immich
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: plex - name: plex
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: tautulli - name: tautulli
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: downloaders - name: downloaders
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: wireguard - name: wireguard
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: nextcloud - name: nextcloud
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: cloudreve - name: cloudreve
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: echoip - name: echoip
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: arr-stack - name: arr-stack
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: home-assistant - name: home-assistant
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: privatebin - name: privatebin
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: unifi-network-application - name: unifi-network-application
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server
- name: avorion - name: avorion
enabled: false enabled: false
hosts: hosts:
- mennos-server - mennos-server
- name: sathub - name: sathub
enabled: true enabled: true
hosts: hosts:
- mennos-server - mennos-server

View File

@@ -26,6 +26,7 @@ def main():
printfe("red", f"Error reading help file: {e}") printfe("red", f"Error reading help file: {e}")
return 1 return 1
print(help_text)
println(" ", "cyan") println(" ", "cyan")
return 0 return 0

View File

@@ -5,10 +5,12 @@ import signal
import subprocess import subprocess
import sys import sys
def signal_handler(sig, frame): def signal_handler(sig, frame):
print('Exiting.') print("Exiting.")
sys.exit(0) sys.exit(0)
signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGINT, signal_handler)
# Script constants # Script constants
@@ -22,43 +24,54 @@ from helpers.functions import printfe, ensure_dependencies
ensure_dependencies() ensure_dependencies()
def run_script(script_path, args): def run_script(script_path, args):
"""Run an action script with the given arguments""" """Run an action script with the given arguments"""
if not os.path.isfile(script_path) or not os.access(script_path, os.X_OK): if not os.path.isfile(script_path) or not os.access(script_path, os.X_OK):
printfe("red", f"Error: Script not found or not executable: {script_path}") printfe("red", f"Error: Script not found or not executable: {script_path}")
return 1 return 1
result = subprocess.run([script_path] + args, env={**os.environ, "DOTFILES_PATH": DOTFILES_PATH}) result = subprocess.run(
[script_path] + args, env={**os.environ, "DOTFILES_PATH": DOTFILES_PATH}
)
return result.returncode return result.returncode
def update(args): def update(args):
"""Run the update action""" """Run the update action"""
return run_script(f"{DOTFILES_BIN}/actions/update.py", args) return run_script(f"{DOTFILES_BIN}/actions/update.py", args)
def hello(args): def hello(args):
"""Run the hello action""" """Run the hello action"""
return run_script(f"{DOTFILES_BIN}/actions/hello.py", args) return run_script(f"{DOTFILES_BIN}/actions/hello.py", args)
def help(args): def help(args):
"""Run the help action""" """Run the help action"""
return run_script(f"{DOTFILES_BIN}/actions/help.py", args) return run_script(f"{DOTFILES_BIN}/actions/help.py", args)
def service(args): def service(args):
"""Run the service/docker action""" """Run the service/docker action"""
return run_script(f"{DOTFILES_BIN}/actions/service.py", args) return run_script(f"{DOTFILES_BIN}/actions/service.py", args)
def lint(args): def lint(args):
"""Run the lint action""" """Run the lint action"""
return run_script(f"{DOTFILES_BIN}/actions/lint.py", args) return run_script(f"{DOTFILES_BIN}/actions/lint.py", args)
def timers(args): def timers(args):
"""Run the timers action""" """Run the timers action"""
return run_script(f"{DOTFILES_BIN}/actions/timers.py", args) return run_script(f"{DOTFILES_BIN}/actions/timers.py", args)
def source(args): def source(args):
"""Run the source action""" """Run the source action"""
return run_script(f"{DOTFILES_BIN}/actions/source.py", args) return run_script(f"{DOTFILES_BIN}/actions/source.py", args)
def ensure_git_hooks(): def ensure_git_hooks():
"""Ensure git hooks are correctly set up""" """Ensure git hooks are correctly set up"""
hooks_dir = os.path.join(DOTFILES_ROOT, ".git/hooks") hooks_dir = os.path.join(DOTFILES_ROOT, ".git/hooks")
@@ -66,14 +79,19 @@ def ensure_git_hooks():
# Validate target directory exists # Validate target directory exists
if not os.path.isdir(target_link): if not os.path.isdir(target_link):
printfe("red", f"Error: Git hooks source directory does not exist: {target_link}") printfe(
"red", f"Error: Git hooks source directory does not exist: {target_link}"
)
return 1 return 1
# Handle existing symlink # Handle existing symlink
if os.path.islink(hooks_dir): if os.path.islink(hooks_dir):
current_link = os.readlink(hooks_dir) current_link = os.readlink(hooks_dir)
if current_link != target_link: if current_link != target_link:
printfe("yellow", "Incorrect git hooks symlink found. Removing and recreating...") printfe(
"yellow",
"Incorrect git hooks symlink found. Removing and recreating...",
)
os.remove(hooks_dir) os.remove(hooks_dir)
else: else:
return 0 return 0
@@ -82,6 +100,7 @@ def ensure_git_hooks():
if os.path.isdir(hooks_dir) and not os.path.islink(hooks_dir): if os.path.isdir(hooks_dir) and not os.path.islink(hooks_dir):
printfe("yellow", "Removing existing hooks directory...") printfe("yellow", "Removing existing hooks directory...")
import shutil import shutil
shutil.rmtree(hooks_dir) shutil.rmtree(hooks_dir)
# Create new symlink # Create new symlink
@@ -93,6 +112,7 @@ def ensure_git_hooks():
printfe("red", f"Failed to create git hooks symlink: {e}") printfe("red", f"Failed to create git hooks symlink: {e}")
return 1 return 1
def main(): def main():
# Ensure we're in the correct directory # Ensure we're in the correct directory
if not os.path.isdir(DOTFILES_ROOT): if not os.path.isdir(DOTFILES_ROOT):
@@ -114,13 +134,42 @@ def main():
"service": service, "service": service,
"lint": lint, "lint": lint,
"timers": timers, "timers": timers,
"source": source "source": source,
} }
if command in commands: if command in commands:
return commands[command](args) return commands[command](args)
else: else:
# For invalid commands, show error after logo
if command != "help":
from helpers.functions import logo
logo(continue_after=True)
print()
printfe("red", f"✗ Error: Unknown command '{command}'")
# Provide helpful hints for common mistakes
if command == "ls":
printfe("yellow", " Hint: Did you mean 'dotf service ls'?")
elif command == "list":
printfe("yellow", " Hint: Did you mean 'dotf service list'?")
print()
# Now print help text without logo
dotfiles_path = os.environ.get(
"DOTFILES_PATH", os.path.expanduser("~/.dotfiles")
)
try:
with open(
f"{dotfiles_path}/bin/resources/help.txt", "r", encoding="utf-8"
) as f:
print(f.read())
except OSError as e:
printfe("red", f"Error reading help file: {e}")
return 1
return 1
return help([]) return help([])
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(main()) sys.exit(main())

View File

@@ -45,6 +45,7 @@
"mennos-server" = mkHomeConfig "x86_64-linux" "mennos-server" true; "mennos-server" = mkHomeConfig "x86_64-linux" "mennos-server" true;
"mennos-rtlsdr-pc" = mkHomeConfig "x86_64-linux" "mennos-rtlsdr-pc" true; "mennos-rtlsdr-pc" = mkHomeConfig "x86_64-linux" "mennos-rtlsdr-pc" true;
"mennos-laptop" = mkHomeConfig "x86_64-linux" "mennos-laptop" false; "mennos-laptop" = mkHomeConfig "x86_64-linux" "mennos-laptop" false;
"mennos-desktopw" = mkHomeConfig "x86_64-linux" "mennos-desktopw" true;
}; };
}; };
} }

View File

@@ -166,6 +166,13 @@ validate_hostname() {
return 0 return 0
} }
is_wsl() {
if grep -qEi "(Microsoft|WSL)" /proc/version &> /dev/null; then
return 0
fi
return 1
}
update_home_manager_flake() { update_home_manager_flake() {
local hostname="$1" local hostname="$1"
local isServer="$2" local isServer="$2"
@@ -290,7 +297,15 @@ prepare_hostname() {
fi fi
log_info "Setting hostname to $hostname..." log_info "Setting hostname to $hostname..."
sudo hostnamectl set-hostname "$hostname" || die "Failed to set hostname"
# WSL doesn't support hostnamectl reliably, use /etc/hostname instead
if is_wsl; then
log_info "Detected WSL environment, using alternative hostname method..."
echo "$hostname" | sudo tee /etc/hostname > /dev/null || die "Failed to set hostname"
sudo hostname "$hostname" || log_warning "Failed to set hostname for current session (will take effect on restart)"
else
sudo hostnamectl set-hostname "$hostname" || die "Failed to set hostname"
fi
echo "$hostname" > "$hostname_file" || die "Failed to save hostname" echo "$hostname" > "$hostname_file" || die "Failed to save hostname"
log_success "Hostname set successfully." log_success "Hostname set successfully."
@@ -301,7 +316,14 @@ warning_prompt() {
log_error "Please ensure you have a backup of your data before proceeding." log_error "Please ensure you have a backup of your data before proceeding."
log_error "This script will modify system files and may require sudo permissions." log_error "This script will modify system files and may require sudo permissions."
echo "" echo ""
log_info "This script has been tested on Ubuntu 22.04, 24.04, 24.10, Pop!_OS 24.04 Alpha 7, Debian 12, Fedora 41 and CachyOS."
if is_wsl; then
log_info "WSL environment detected."
log_info "This script has been tested on Ubuntu under WSL2."
else
log_info "This script has been tested on Ubuntu 22.04, 24.04, 24.10, Pop!_OS 24.04 Alpha 7, Debian 12, Fedora 41 and CachyOS."
fi
log_info "Setup starts in 10 seconds, to abort use Ctrl+C to exit NOW." log_info "Setup starts in 10 seconds, to abort use Ctrl+C to exit NOW."
echo "" echo ""
sleep 10 sleep 10
@@ -397,6 +419,11 @@ check_compatibility() {
local distro local distro
distro=$(awk -F= '/^NAME/{print $2}' /etc/os-release | tr -d '"') distro=$(awk -F= '/^NAME/{print $2}' /etc/os-release | tr -d '"')
# Special handling for WSL
if is_wsl; then
log_info "Running in WSL environment."
fi
case "$distro" in case "$distro" in
Fedora*) Fedora*)
log_success "Detected Fedora. Proceeding with setup..." log_success "Detected Fedora. Proceeding with setup..."
@@ -413,9 +440,11 @@ check_compatibility() {
;; ;;
Debian*) Debian*)
log_success "Detected Debian. Proceeding with setup..." log_success "Detected Debian. Proceeding with setup..."
log_warning "Debian has known issues with ZFS kernel modules, you might need to manually install it to make ZFS work." if ! is_wsl; then
log_warning "Continueing in 5 seconds..." log_warning "Debian has known issues with ZFS kernel modules, you might need to manually install it to make ZFS work."
sleep 5 log_warning "Continueing in 5 seconds..."
sleep 5
fi
check_command_availibility "apt" check_command_availibility "apt"
;; ;;
Pop!_OS*) Pop!_OS*)