with one click
gentleman-system
// System detection and command execution patterns for Gentleman.Dots. Trigger: When editing files in installer/internal/system/, adding OS support, or modifying command execution.
// System detection and command execution patterns for Gentleman.Dots. Trigger: When editing files in installer/internal/system/, adding OS support, or modifying command execution.
| name | gentleman-system |
| description | System detection and command execution patterns for Gentleman.Dots. Trigger: When editing files in installer/internal/system/, adding OS support, or modifying command execution. |
| license | Apache-2.0 |
| metadata | {"author":"gentleman-programming","version":"1.0"} |
Use this skill when:
All OS types are defined in detect.go:
type OSType int
const (
OSMac OSType = iota
OSLinux
OSArch
OSDebian // Debian-based (Debian, Ubuntu)
OSTermux // Termux on Android
OSUnknown
)
Detection results in SystemInfo struct:
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
}
Termux is checked FIRST (runs on Linux but is special):
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
}
Use the right function for each context:
// 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)
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
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
}
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
}
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"
}
// 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
}
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
}
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
installer/internal/system/detect.go for OS detectioninstaller/internal/system/exec.go for command runninginstaller/internal/system/backup.go for backup/restoreinstaller/internal/system/*_test.go for patternsBubbletea TUI patterns for Gentleman.Dots installer. Trigger: When editing Go files in installer/internal/tui/, working on TUI screens, or adding new UI features.
Docker-based E2E testing patterns for Gentleman.Dots installer. Trigger: When editing files in installer/e2e/, writing E2E tests, or adding platform support.
Installation step patterns for Gentleman.Dots TUI installer. Trigger: When editing installer.go, adding installation steps, or modifying the installation flow.
Vim Trainer RPG system patterns for Gentleman.Dots. Trigger: When editing files in installer/internal/tui/trainer/, adding exercises, modules, or game mechanics.
Go testing patterns for Gentleman.Dots, including Bubbletea TUI testing. Trigger: When writing Go tests, using teatest, or adding test coverage.