gentleman-system

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

When to Use

适用场景

Use this skill when:
  • Adding support for new operating systems
  • Modifying OS detection logic
  • Working with command execution (sudo, brew, pkg)
  • Adding new system checks
  • Implementing backup/restore functionality

在以下场景中使用本技能:
  • 添加新操作系统支持
  • 修改操作系统检测逻辑
  • 处理命令执行(sudo、brew、pkg)
  • 添加新的系统检查项
  • 实现备份/恢复功能

Critical Patterns

核心模式

Pattern 1: OSType Enum

模式1:OSType枚举类型

All OS types are defined in
detect.go
:
go
type OSType int

const (
    OSMac OSType = iota
    OSLinux
    OSArch
    OSDebian    // Debian-based (Debian, Ubuntu)
    OSTermux    // Termux on Android
    OSUnknown
)
所有操作系统类型均在
detect.go
中定义:
go
type OSType int

const (
    OSMac OSType = iota
    OSLinux
    OSArch
    OSDebian    // Debian-based (Debian, Ubuntu)
    OSTermux    // Termux on Android
    OSUnknown
)

Pattern 2: SystemInfo Structure

模式2:SystemInfo结构体

Detection results in
SystemInfo
struct:
go
type SystemInfo struct {
    OS        OSType
    OSName    string
    IsWSL     bool
    IsARM     bool
    IsTermux  bool
    HomeDir   string
    HasBrew   bool
    HasPkg    bool    // Termux package manager
    HasXcode  bool
    UserShell string
    Prefix    string  // Termux $PREFIX or empty
}
检测结果存储在
SystemInfo
结构体中:
go
type SystemInfo struct {
    OS        OSType
    OSName    string
    IsWSL     bool
    IsARM     bool
    IsTermux  bool
    HomeDir   string
    HasBrew   bool
    HasPkg    bool    // Termux package manager
    HasXcode  bool
    UserShell string
    Prefix    string  // Termux $PREFIX or empty
}

Pattern 3: OS Detection Priority

模式3:操作系统检测优先级

Termux is checked FIRST (runs on Linux but is special):
go
func Detect() *SystemInfo {
    info := &SystemInfo{...}

    // Check Termux FIRST
    if isTermux() {
        info.OS = OSTermux
        info.IsTermux = true
        info.HasPkg = checkPkg()
        return info
    }

    // Then check standard OS
    switch runtime.GOOS {
    case "darwin":
        info.OS = OSMac
    case "linux":
        if isArchLinux() {
            info.OS = OSArch
        } else if isDebian() {
            info.OS = OSDebian
        }
    }
    return info
}
会优先检测Termux(它基于Linux但属于特殊场景):
go
func Detect() *SystemInfo {
    info := &SystemInfo{...}

    // Check Termux FIRST
    if isTermux() {
        info.OS = OSTermux
        info.IsTermux = true
        info.HasPkg = checkPkg()
        return info
    }

    // Then check standard OS
    switch runtime.GOOS {
    case "darwin":
        info.OS = OSMac
    case "linux":
        if isArchLinux() {
            info.OS = OSArch
        } else if isDebian() {
            info.OS = OSDebian
        }
    }
    return info
}

Pattern 4: Command Execution Functions

模式4:命令执行函数

Use the right function for each context:
go
// Basic command (no sudo, no logs)
system.Run("git clone ...", nil)

// With real-time logs
system.RunWithLogs("git clone ...", nil, func(line string) {
    SendLog(stepID, line)
})

// Homebrew commands
system.RunBrewWithLogs("install fish", nil, logFunc)

// Sudo commands (password prompt)
system.RunSudo("apt-get install -y git", nil)
system.RunSudoWithLogs("pacman -S git", nil, logFunc)

// Termux pkg commands (no sudo needed)
system.RunPkgInstall("fish git", nil, logFunc)
system.RunPkgWithLogs("update", nil, logFunc)

根据上下文选择合适的函数:
go
// Basic command (no sudo, no logs)
system.Run("git clone ...", nil)

// With real-time logs
system.RunWithLogs("git clone ...", nil, func(line string) {
    SendLog(stepID, line)
})

// Homebrew commands
system.RunBrewWithLogs("install fish", nil, logFunc)

// Sudo commands (password prompt)
system.RunSudo("apt-get install -y git", nil)
system.RunSudoWithLogs("pacman -S git", nil, logFunc)

// Termux pkg commands (no sudo needed)
system.RunPkgInstall("fish git", nil, logFunc)
system.RunPkgWithLogs("update", nil, logFunc)

Decision Tree

决策树

Adding new OS support?
├── Add OSType constant in detect.go
├── Add detection function (isNewOS())
├── Update Detect() with priority order
├── Update SystemInfo if new fields needed
└── Add OS case in installer.go steps

Running a command?
├── Needs sudo? → RunSudo() or RunSudoWithLogs()
├── Needs brew? → RunBrewWithLogs()
├── On Termux? → RunPkgInstall() or RunPkgWithLogs()
├── Needs logs? → RunWithLogs()
└── Simple exec? → Run()

Checking if tool exists?
├── Use CommandExists("toolname")
└── Returns bool

添加新操作系统支持?
├── 在detect.go中添加OSType常量
├── 添加检测函数(isNewOS())
├── 按优先级顺序更新Detect()函数
├── 若需新增字段则更新SystemInfo结构体
└── 在installer.go步骤中添加对应操作系统分支

执行命令?
├── 需要sudo?→ 使用RunSudo()或RunSudoWithLogs()
├── 需要brew?→ 使用RunBrewWithLogs()
├── 运行在Termux?→ 使用RunPkgInstall()或RunPkgWithLogs()
├── 需要日志?→ 使用RunWithLogs()
└── 简单执行?→ 使用Run()

检查工具是否存在?
├── 调用CommandExists("toolname")
└── 返回布尔值

Code Examples

代码示例

Example 1: Termux Detection

示例1:Termux检测

go
func isTermux() bool {
    // Check TERMUX_VERSION environment variable
    if os.Getenv("TERMUX_VERSION") != "" {
        return true
    }
    // Check PREFIX contains termux path
    prefix := os.Getenv("PREFIX")
    if strings.Contains(prefix, "com.termux") {
        return true
    }
    // Check for Termux-specific paths
    if _, err := os.Stat("/data/data/com.termux"); err == nil {
        return true
    }
    return false
}
go
func isTermux() bool {
    // Check TERMUX_VERSION environment variable
    if os.Getenv("TERMUX_VERSION") != "" {
        return true
    }
    // Check PREFIX contains termux path
    prefix := os.Getenv("PREFIX")
    if strings.Contains(prefix, "com.termux") {
        return true
    }
    // Check for Termux-specific paths
    if _, err := os.Stat("/data/data/com.termux"); err == nil {
        return true
    }
    return false
}

Example 2: Platform-Specific Execution

示例2:平台特定执行逻辑

go
func installTool(m *Model) error {
    var result *system.ExecResult

    switch {
    case m.SystemInfo.IsTermux:
        // Termux: use pkg (no sudo)
        result = system.RunPkgInstall("tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSArch:
        // Arch: use pacman with sudo
        result = system.RunSudoWithLogs("pacman -S --noconfirm tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSMac:
        // macOS: use Homebrew
        result = system.RunBrewWithLogs("install tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSDebian:
        // Debian/Ubuntu: use Homebrew (installed by us)
        result = system.RunBrewWithLogs("install tool", nil, logFunc)

    default:
        return fmt.Errorf("unsupported OS: %v", m.SystemInfo.OS)
    }

    return result.Error
}
go
func installTool(m *Model) error {
    var result *system.ExecResult

    switch {
    case m.SystemInfo.IsTermux:
        // Termux: use pkg (no sudo)
        result = system.RunPkgInstall("tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSArch:
        // Arch: use pacman with sudo
        result = system.RunSudoWithLogs("pacman -S --noconfirm tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSMac:
        // macOS: use Homebrew
        result = system.RunBrewWithLogs("install tool", nil, logFunc)

    case m.SystemInfo.OS == system.OSDebian:
        // Debian/Ubuntu: use Homebrew (installed by us)
        result = system.RunBrewWithLogs("install tool", nil, logFunc)

    default:
        return fmt.Errorf("unsupported OS: %v", m.SystemInfo.OS)
    }

    return result.Error
}

Example 3: Homebrew Prefix Detection

示例3:Homebrew路径检测

go
func GetBrewPrefix() string {
    if runtime.GOOS == "darwin" {
        // Apple Silicon uses /opt/homebrew
        // Intel uses /usr/local
        if runtime.GOARCH == "arm64" {
            return "/opt/homebrew"
        }
        return "/usr/local"
    }
    return "/home/linuxbrew/.linuxbrew"
}
go
func GetBrewPrefix() string {
    if runtime.GOOS == "darwin" {
        // Apple Silicon uses /opt/homebrew
        // Intel uses /usr/local
        if runtime.GOARCH == "arm64" {
            return "/opt/homebrew"
        }
        return "/usr/local"
    }
    return "/home/linuxbrew/.linuxbrew"
}

Example 4: File Operations

示例4:文件操作

go
// Ensure directory exists
if err := system.EnsureDir(filepath.Join(homeDir, ".config/tool")); err != nil {
    return err
}

// Copy single file
if err := system.CopyFile(src, dst); err != nil {
    return err
}

// Copy directory contents
if err := system.CopyDir("Gentleman.Dots/Config/*", destDir+"/"); err != nil {
    return err
}

go
// Ensure directory exists
if err := system.EnsureDir(filepath.Join(homeDir, ".config/tool")); err != nil {
    return err
}

// Copy single file
if err := system.CopyFile(src, dst); err != nil {
    return err
}

// Copy directory contents
if err := system.CopyDir("Gentleman.Dots/Config/*", destDir+"/"); err != nil {
    return err
}

ExecResult Structure

ExecResult结构体

go
type ExecResult struct {
    Output   string  // stdout
    Stderr   string  // stderr
    ExitCode int     // exit code
    Error    error   // error if any
}

// Usage
result := system.Run("command", nil)
if result.Error != nil {
    // Handle error
}
if result.ExitCode != 0 {
    // Non-zero exit
}

go
type ExecResult struct {
    Output   string  // stdout
    Stderr   string  // stderr
    ExitCode int     // exit code
    Error    error   // error if any
}

// Usage
result := system.Run("command", nil)
if result.Error != nil {
    // Handle error
}
if result.ExitCode != 0 {
    // Non-zero exit
}

Commands

命令

bash
cd installer && go test ./internal/system/...   # Run system tests
cd installer && go test -run TestDetect         # Test OS detection
cd installer && go test -run TestExec           # Test command execution

bash
cd installer && go test ./internal/system/...   # 运行系统测试
cd installer && go test -run TestDetect         # 测试操作系统检测
cd installer && go test -run TestExec           # 测试命令执行

Resources

资源参考

  • Detection: See
    installer/internal/system/detect.go
    for OS detection
  • Execution: See
    installer/internal/system/exec.go
    for command running
  • Backup: See
    installer/internal/system/backup.go
    for backup/restore
  • Tests: See
    installer/internal/system/*_test.go
    for patterns
  • 检测模块: 查看
    installer/internal/system/detect.go
    了解操作系统检测逻辑
  • 执行模块: 查看
    installer/internal/system/exec.go
    了解命令运行逻辑
  • 备份模块: 查看
    installer/internal/system/backup.go
    了解备份/恢复逻辑
  • 测试用例: 查看
    installer/internal/system/*_test.go
    了解实现模式