diff --git a/modules/base.py b/modules/base.py index 593880e..bb4a85c 100644 --- a/modules/base.py +++ b/modules/base.py @@ -8,7 +8,7 @@ class BaseModule(Module): super().__init__("base") self.user = user - def files(self): + def files(self) -> dict[str, File]: return { "/etc/pacman.conf": File( source_file="./system/etc/pacman.conf", diff --git a/modules/dev.py b/modules/dev.py index 0897038..dd34ad2 100644 --- a/modules/dev.py +++ b/modules/dev.py @@ -2,13 +2,13 @@ import decman from decman import File, Module from decman.plugins.pacman import packages as pacman_packages -BUN_GLOBAL_PACKAGES = [ +BUN_GLOBAL_PACKAGES: list[str] = [ "@vue/language-server", "dockerfile-language-server-nodejs", "opencode-ai", ] -GO_INSTALL_PACKAGES = [ +GO_INSTALL_PACKAGES: list[str] = [ "github.com/code-yeongyu/go-claude-code-comment-checker/cmd/comment-checker@latest", ] @@ -18,7 +18,7 @@ class DevModule(Module): super().__init__("dev") self.user = user - def files(self): + def files(self) -> dict[str, File]: return { f"/home/{self.user}/.config/mise/config.toml": File( source_file="./home/.config/mise/config.toml", @@ -50,7 +50,7 @@ class DevModule(Module): "zellij", } - def after_update(self, store): + def after_update(self, store: object) -> None: failures: list[str] = [] for pkg in BUN_GLOBAL_PACKAGES: try: diff --git a/modules/docker.py b/modules/docker.py index 2eb18ac..1cbdb66 100644 --- a/modules/docker.py +++ b/modules/docker.py @@ -1,4 +1,5 @@ import subprocess +from pathlib import Path import decman from decman import Module @@ -6,10 +7,19 @@ from decman.plugins.pacman import packages as pacman_packages from decman.plugins.systemd import units +def _is_wsl() -> bool: + """检测是否在 WSL 环境""" + try: + return "microsoft" in Path("/proc/version").read_text().lower() + except OSError: + return False + + class DockerModule(Module): def __init__(self, user: str): super().__init__("docker") self.user = user + self._is_wsl = _is_wsl() @pacman_packages def pacman_packages(self) -> set[str]: @@ -19,26 +29,31 @@ class DockerModule(Module): def units(self) -> set[str]: return {"docker.socket"} - def after_update(self, store): + def after_update(self, store: object) -> None: self._ensure_user_in_docker_group() - self._disable_networkd_wait_online() + if self._is_wsl: + self._disable_networkd_wait_online() - def _ensure_user_in_docker_group(self): + def _ensure_user_in_docker_group(self) -> None: result = subprocess.run( - ["id", "-nG", self.user], capture_output=True, text=True + ["id", "-nG", self.user], + capture_output=True, + text=True, + check=False, ) if result.returncode != 0: + decman.error(f"无法读取用户 {self.user} 的组信息") return if "docker" not in result.stdout.split(): decman.prg(["gpasswd", "-a", self.user, "docker"]) - def _disable_networkd_wait_online(self): - # systemd-networkd-wait-online.service 会阻塞 network-online.target - # 直到所有 link ready 或 120s 超时,导致 docker.service 启动卡约两分钟 + def _disable_networkd_wait_online(self) -> None: + """WSL 环境:禁用 systemd-networkd-wait-online 避免启动阻塞""" result = subprocess.run( ["systemctl", "is-enabled", "systemd-networkd-wait-online.service"], capture_output=True, text=True, + check=False, ) - if result.stdout.strip() == "enabled": + if result.returncode == 0 and result.stdout.strip() == "enabled": decman.prg(["systemctl", "disable", "systemd-networkd-wait-online.service"]) diff --git a/modules/locale.py b/modules/locale.py index 0bc56be..e901ac5 100644 --- a/modules/locale.py +++ b/modules/locale.py @@ -6,11 +6,11 @@ class LocaleModule(Module): def __init__(self): super().__init__("locale") - def files(self): + def files(self) -> dict[str, File]: return { "/etc/locale.conf": File(content="LANG=en_US.UTF-8\n"), "/etc/locale.gen": File(content="en_US.UTF-8 UTF-8\n"), } - def on_change(self, store): + def on_change(self, store: object) -> None: decman.prg(["locale-gen"]) diff --git a/modules/zsh.py b/modules/zsh.py index 36aac66..ba8230e 100644 --- a/modules/zsh.py +++ b/modules/zsh.py @@ -11,7 +11,7 @@ class ZshModule(Module): super().__init__("zsh") self.user = user - def files(self): + def files(self) -> dict[str, File]: return { f"/home/{self.user}/.zshrc": File( source_file="./home/.zshrc", @@ -35,11 +35,15 @@ class ZshModule(Module): "fzf-tab-git", } - def after_update(self, store): + def after_update(self, store: object) -> None: result = subprocess.run( - ["getent", "passwd", self.user], capture_output=True, text=True + ["getent", "passwd", self.user], + capture_output=True, + text=True, + check=False, ) if result.returncode != 0: + decman.error(f"无法读取用户 {self.user} 的 passwd 信息") return # passwd 格式: name:x:uid:gid:gecos:home:shell shell = result.stdout.strip().split(":")[-1] diff --git a/scripts/wsl-init.sh b/scripts/wsl-init.sh index 3b73975..5f63f30 100755 --- a/scripts/wsl-init.sh +++ b/scripts/wsl-init.sh @@ -1,40 +1,40 @@ #!/bin/bash set -euo pipefail -if [ "$(id -u)" -ne 0 ]; then - echo "❌ 请以 root 身份运行此脚本" - exit 1 +if [[ "$(id -u)" -ne 0 ]]; then + echo "❌ 请以 root 身份运行此脚本" + exit 1 fi USERNAME="${1:-}" -if [ -z "$USERNAME" ]; then - echo "用法: wsl-init.sh <用户名>" - echo "示例: wsl-init.sh imbytecat" - exit 1 +if [[ -z "$USERNAME" ]]; then + echo "用法: wsl-init.sh <用户名>" + echo "示例: wsl-init.sh imbytecat" + exit 1 fi echo "🔄 更新系统..." pacman -Syu --noconfirm -if ! command -v sudo &> /dev/null; then - echo "📦 安装 sudo..." - pacman -S --needed --noconfirm sudo +if ! command -v sudo &>/dev/null; then + echo "📦 安装 sudo..." + pacman -S --needed --noconfirm sudo fi echo "🔐 配置 sudo 权限..." -cat > /etc/sudoers.d/10-wheel << 'EOF' +cat >/etc/sudoers.d/10-wheel <<'EOF' %wheel ALL=(ALL) NOPASSWD: ALL EOF chmod 440 /etc/sudoers.d/10-wheel echo "👤 创建用户 $USERNAME..." -if id "$USERNAME" &> /dev/null; then - echo "⏩ 用户 $USERNAME 已存在,确保 wheel 组成员" - usermod -aG wheel "$USERNAME" +if id "$USERNAME" &>/dev/null; then + echo "⏩ 用户 $USERNAME 已存在,确保 wheel 组成员" + usermod -aG wheel "$USERNAME" else - useradd -m -G wheel -s /bin/bash "$USERNAME" - echo "请设置 $USERNAME 的密码:" - passwd "$USERNAME" < /dev/tty + useradd -m -G wheel -s /bin/bash "$USERNAME" + echo "请设置 $USERNAME 的密码:" + passwd "$USERNAME"