init
This commit is contained in:
125
internal/collector/collector.go
Normal file
125
internal/collector/collector.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Category 采集器分类
|
||||
type Category string
|
||||
|
||||
const (
|
||||
CategoryRuntime Category = "runtime" // 编程语言运行时
|
||||
CategoryPackage Category = "package" // 包管理器
|
||||
CategoryEditor Category = "editor" // 编辑器/IDE
|
||||
CategoryShell Category = "shell" // Shell 配置
|
||||
CategoryGit Category = "git" // Git 配置
|
||||
CategoryEnv Category = "env" // 环境变量
|
||||
CategoryFont Category = "font" // 字体
|
||||
CategorySSH Category = "ssh" // SSH/GPG 密钥
|
||||
CategoryCustom Category = "custom" // 自定义
|
||||
)
|
||||
|
||||
// Collector 定义了所有采集器必须实现的接口
|
||||
type Collector interface {
|
||||
// Name 返回采集器名称(唯一标识)
|
||||
Name() string
|
||||
|
||||
// DisplayName 返回采集器的显示名称
|
||||
DisplayName() string
|
||||
|
||||
// Description 返回采集器的描述信息
|
||||
Description() string
|
||||
|
||||
// Category 返回采集器所属分类
|
||||
Category() Category
|
||||
|
||||
// IsAvailable 检测当前系统是否支持此采集器
|
||||
IsAvailable(ctx context.Context) bool
|
||||
|
||||
// Scan 扫描当前环境,返回扫描结果
|
||||
Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error)
|
||||
|
||||
// Capture 捕获环境数据,写入到指定目录
|
||||
Capture(ctx context.Context, targetDir string, opts CaptureOptions) error
|
||||
|
||||
// Restore 从指定目录还原环境
|
||||
Restore(ctx context.Context, sourceDir string, opts RestoreOptions) error
|
||||
|
||||
// Verify 验证还原后的环境是否正确
|
||||
Verify(ctx context.Context) (*VerifyResult, error)
|
||||
}
|
||||
|
||||
// ScanOptions 扫描选项
|
||||
type ScanOptions struct {
|
||||
Detailed bool `json:"detailed"`
|
||||
Filters map[string]string `json:"filters,omitempty"`
|
||||
Timeout time.Duration `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
// CaptureOptions 捕获选项
|
||||
type CaptureOptions struct {
|
||||
IncludePatterns []string `json:"include_patterns,omitempty"`
|
||||
ExcludePatterns []string `json:"exclude_patterns,omitempty"`
|
||||
Encrypt bool `json:"encrypt"`
|
||||
}
|
||||
|
||||
// RestoreOptions 还原选项
|
||||
type RestoreOptions struct {
|
||||
ConflictStrategy ConflictStrategy `json:"conflict_strategy"`
|
||||
DryRun bool `json:"dry_run"`
|
||||
Force bool `json:"force"`
|
||||
}
|
||||
|
||||
// ConflictStrategy 冲突处理策略
|
||||
type ConflictStrategy string
|
||||
|
||||
const (
|
||||
ConflictSkip ConflictStrategy = "skip"
|
||||
ConflictOverwrite ConflictStrategy = "overwrite"
|
||||
ConflictMerge ConflictStrategy = "merge"
|
||||
ConflictPrompt ConflictStrategy = "prompt"
|
||||
ConflictNewest ConflictStrategy = "newest"
|
||||
)
|
||||
|
||||
// PlatformInfo 平台信息
|
||||
type PlatformInfo struct {
|
||||
OS string `json:"os"`
|
||||
Version string `json:"os_version"`
|
||||
Arch string `json:"arch"`
|
||||
}
|
||||
|
||||
// ScanResult 扫描结果
|
||||
type ScanResult struct {
|
||||
Collector string `json:"collector"`
|
||||
Category Category `json:"category"`
|
||||
Items []ScanItem `json:"items"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Platform PlatformInfo `json:"platform"`
|
||||
Errors []string `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
// ScanItem 单个扫描项
|
||||
type ScanItem struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Properties map[string]string `json:"properties,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Sensitive bool `json:"sensitive,omitempty"`
|
||||
Children []ScanItem `json:"children,omitempty"`
|
||||
}
|
||||
|
||||
// VerifyResult 验证结果
|
||||
type VerifyResult struct {
|
||||
Success bool `json:"success"`
|
||||
Items []VerifyItem `json:"items"`
|
||||
}
|
||||
|
||||
// VerifyItem 验证项
|
||||
type VerifyItem struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"` // ok, missing, version_mismatch, error
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
121
internal/collector/registry.go
Normal file
121
internal/collector/registry.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Registry 采集器注册中心
|
||||
type Registry struct {
|
||||
collectors map[string]Collector
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewRegistry 创建采集器注册中心
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
collectors: make(map[string]Collector),
|
||||
}
|
||||
}
|
||||
|
||||
// Register 注册一个采集器
|
||||
func (r *Registry) Register(c Collector) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
name := c.Name()
|
||||
if _, exists := r.collectors[name]; exists {
|
||||
return fmt.Errorf("collector already registered: %s", name)
|
||||
}
|
||||
|
||||
r.collectors[name] = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取指定名称的采集器
|
||||
func (r *Registry) Get(name string) (Collector, bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
c, ok := r.collectors[name]
|
||||
return c, ok
|
||||
}
|
||||
|
||||
// List 列出所有已注册的采集器
|
||||
func (r *Registry) List() []Collector {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
result := make([]Collector, 0, len(r.collectors))
|
||||
for _, c := range r.collectors {
|
||||
result = append(result, c)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ListByCategory 按分类列出采集器
|
||||
func (r *Registry) ListByCategory(cat Category) []Collector {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
var result []Collector
|
||||
for _, c := range r.collectors {
|
||||
if c.Category() == cat {
|
||||
result = append(result, c)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Available 返回当前系统上可用的采集器
|
||||
func (r *Registry) Available(ctx context.Context) []Collector {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
var result []Collector
|
||||
for _, c := range r.collectors {
|
||||
if c.IsAvailable(ctx) {
|
||||
result = append(result, c)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ScanAll 使用所有可用的采集器扫描
|
||||
func (r *Registry) ScanAll(ctx context.Context, opts ScanOptions) ([]*ScanResult, error) {
|
||||
available := r.Available(ctx)
|
||||
|
||||
var (
|
||||
results []*ScanResult
|
||||
mu sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
errs []error
|
||||
)
|
||||
|
||||
for _, c := range available {
|
||||
wg.Add(1)
|
||||
go func(collector Collector) {
|
||||
defer wg.Done()
|
||||
|
||||
result, err := collector.Scan(ctx, opts)
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("%s: %w", collector.Name(), err))
|
||||
return
|
||||
}
|
||||
results = append(results, result)
|
||||
}(c)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if len(errs) > 0 && len(results) == 0 {
|
||||
return nil, fmt.Errorf("all collectors failed: %v", errs)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
87
internal/collector/registry_test.go
Normal file
87
internal/collector/registry_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegistry_Register(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
|
||||
mock := &mockCollector{name: "test"}
|
||||
err := r.Register(mock)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Duplicate registration should fail
|
||||
err = r.Register(mock)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for duplicate registration")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistry_Get(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
|
||||
mock := &mockCollector{name: "test"}
|
||||
_ = r.Register(mock)
|
||||
|
||||
c, ok := r.Get("test")
|
||||
if !ok {
|
||||
t.Fatal("expected to find collector")
|
||||
}
|
||||
if c.Name() != "test" {
|
||||
t.Fatalf("expected name 'test', got '%s'", c.Name())
|
||||
}
|
||||
|
||||
_, ok = r.Get("nonexistent")
|
||||
if ok {
|
||||
t.Fatal("expected not to find collector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistry_List(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
|
||||
_ = r.Register(&mockCollector{name: "a", category: CategoryRuntime})
|
||||
_ = r.Register(&mockCollector{name: "b", category: CategoryEditor})
|
||||
_ = r.Register(&mockCollector{name: "c", category: CategoryRuntime})
|
||||
|
||||
all := r.List()
|
||||
if len(all) != 3 {
|
||||
t.Fatalf("expected 3 collectors, got %d", len(all))
|
||||
}
|
||||
|
||||
runtime := r.ListByCategory(CategoryRuntime)
|
||||
if len(runtime) != 2 {
|
||||
t.Fatalf("expected 2 runtime collectors, got %d", len(runtime))
|
||||
}
|
||||
}
|
||||
|
||||
// mockCollector implements Collector for testing
|
||||
type mockCollector struct {
|
||||
name string
|
||||
category Category
|
||||
available bool
|
||||
}
|
||||
|
||||
func (m *mockCollector) Name() string { return m.name }
|
||||
func (m *mockCollector) DisplayName() string { return m.name }
|
||||
func (m *mockCollector) Description() string { return "mock collector" }
|
||||
func (m *mockCollector) Category() Category { return m.category }
|
||||
func (m *mockCollector) IsAvailable(ctx context.Context) bool {
|
||||
return m.available
|
||||
}
|
||||
func (m *mockCollector) Scan(ctx context.Context, opts ScanOptions) (*ScanResult, error) {
|
||||
return &ScanResult{Collector: m.name, Category: m.category}, nil
|
||||
}
|
||||
func (m *mockCollector) Capture(ctx context.Context, targetDir string, opts CaptureOptions) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockCollector) Restore(ctx context.Context, sourceDir string, opts RestoreOptions) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockCollector) Verify(ctx context.Context) (*VerifyResult, error) {
|
||||
return &VerifyResult{Success: true}, nil
|
||||
}
|
||||
57
internal/config/paths.go
Normal file
57
internal/config/paths.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
// AppName 应用名称
|
||||
AppName = "devpack"
|
||||
|
||||
// DefaultConfigFileName 默认配置文件名
|
||||
DefaultConfigFileName = "config.yaml"
|
||||
|
||||
// DefaultProfileName 默认 Profile 名
|
||||
DefaultProfileName = "default"
|
||||
)
|
||||
|
||||
// Paths DevPack 路径管理
|
||||
type Paths struct {
|
||||
Home string // ~/.devpack
|
||||
Config string // ~/.devpack/config.yaml
|
||||
Profiles string // ~/.devpack/profiles
|
||||
Packs string // ~/.devpack/packs
|
||||
Logs string // ~/.devpack/logs
|
||||
Temp string // ~/.devpack/tmp
|
||||
}
|
||||
|
||||
// DefaultPaths 返回默认路径配置
|
||||
func DefaultPaths() (*Paths, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
devpackHome := filepath.Join(home, ".devpack")
|
||||
|
||||
return &Paths{
|
||||
Home: devpackHome,
|
||||
Config: filepath.Join(devpackHome, DefaultConfigFileName),
|
||||
Profiles: filepath.Join(devpackHome, "profiles"),
|
||||
Packs: filepath.Join(devpackHome, "packs"),
|
||||
Logs: filepath.Join(devpackHome, "logs"),
|
||||
Temp: filepath.Join(devpackHome, "tmp"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EnsureDirs 确保所有目录存在
|
||||
func (p *Paths) EnsureDirs() error {
|
||||
dirs := []string{p.Home, p.Profiles, p.Packs, p.Logs, p.Temp}
|
||||
for _, dir := range dirs {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
65
internal/platform/darwin.go
Normal file
65
internal/platform/darwin.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// DarwinPlatform macOS 平台实现
|
||||
type DarwinPlatform struct {
|
||||
homeDir string
|
||||
}
|
||||
|
||||
// NewDarwinPlatform 创建 macOS 平台实例
|
||||
func NewDarwinPlatform() *DarwinPlatform {
|
||||
home, _ := os.UserHomeDir()
|
||||
return &DarwinPlatform{homeDir: home}
|
||||
}
|
||||
|
||||
func (p *DarwinPlatform) OS() string { return "darwin" }
|
||||
func (p *DarwinPlatform) Arch() string { return "amd64" } // TODO: detect properly
|
||||
func (p *DarwinPlatform) HomeDir() string { return p.homeDir }
|
||||
|
||||
func (p *DarwinPlatform) ConfigDir() string {
|
||||
return p.homeDir + "/Library/Application Support"
|
||||
}
|
||||
|
||||
func (p *DarwinPlatform) DataDir() string {
|
||||
return p.homeDir + "/Library/Application Support"
|
||||
}
|
||||
|
||||
func (p *DarwinPlatform) GetEnvVar(key string) string {
|
||||
return os.Getenv(key)
|
||||
}
|
||||
|
||||
func (p *DarwinPlatform) SetEnvVar(ctx context.Context, key, value string) error {
|
||||
// On macOS, set via launchctl and shell profile
|
||||
cmd := exec.CommandContext(ctx, "launchctl", "setenv", key, value)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (p *DarwinPlatform) AddToPath(ctx context.Context, dir string) error {
|
||||
// TODO: Add to shell profile
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *DarwinPlatform) IsAdmin() bool {
|
||||
return os.Geteuid() == 0
|
||||
}
|
||||
|
||||
func (p *DarwinPlatform) PackageManagers() []string {
|
||||
var pms []string
|
||||
if _, err := exec.LookPath("brew"); err == nil {
|
||||
pms = append(pms, "homebrew")
|
||||
}
|
||||
return pms
|
||||
}
|
||||
|
||||
func (p *DarwinPlatform) DefaultShell() string {
|
||||
shell := os.Getenv("SHELL")
|
||||
if shell != "" {
|
||||
return shell
|
||||
}
|
||||
return "/bin/zsh"
|
||||
}
|
||||
82
internal/platform/linux.go
Normal file
82
internal/platform/linux.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// LinuxPlatform Linux 平台实现
|
||||
type LinuxPlatform struct {
|
||||
homeDir string
|
||||
}
|
||||
|
||||
// NewLinuxPlatform 创建 Linux 平台实例
|
||||
func NewLinuxPlatform() *LinuxPlatform {
|
||||
home, _ := os.UserHomeDir()
|
||||
return &LinuxPlatform{homeDir: home}
|
||||
}
|
||||
|
||||
func (p *LinuxPlatform) OS() string { return "linux" }
|
||||
func (p *LinuxPlatform) Arch() string { return "amd64" } // TODO: detect properly
|
||||
func (p *LinuxPlatform) HomeDir() string { return p.homeDir }
|
||||
|
||||
func (p *LinuxPlatform) ConfigDir() string {
|
||||
if dir := os.Getenv("XDG_CONFIG_HOME"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return p.homeDir + "/.config"
|
||||
}
|
||||
|
||||
func (p *LinuxPlatform) DataDir() string {
|
||||
if dir := os.Getenv("XDG_DATA_HOME"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return p.homeDir + "/.local/share"
|
||||
}
|
||||
|
||||
func (p *LinuxPlatform) GetEnvVar(key string) string {
|
||||
return os.Getenv(key)
|
||||
}
|
||||
|
||||
func (p *LinuxPlatform) SetEnvVar(ctx context.Context, key, value string) error {
|
||||
// TODO: Add to shell profile
|
||||
return os.Setenv(key, value)
|
||||
}
|
||||
|
||||
func (p *LinuxPlatform) AddToPath(ctx context.Context, dir string) error {
|
||||
// TODO: Add to shell profile
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *LinuxPlatform) IsAdmin() bool {
|
||||
return os.Geteuid() == 0
|
||||
}
|
||||
|
||||
func (p *LinuxPlatform) PackageManagers() []string {
|
||||
var pms []string
|
||||
if _, err := exec.LookPath("apt"); err == nil {
|
||||
pms = append(pms, "apt")
|
||||
}
|
||||
if _, err := exec.LookPath("dnf"); err == nil {
|
||||
pms = append(pms, "dnf")
|
||||
}
|
||||
if _, err := exec.LookPath("yum"); err == nil {
|
||||
pms = append(pms, "yum")
|
||||
}
|
||||
if _, err := exec.LookPath("pacman"); err == nil {
|
||||
pms = append(pms, "pacman")
|
||||
}
|
||||
if _, err := exec.LookPath("snap"); err == nil {
|
||||
pms = append(pms, "snap")
|
||||
}
|
||||
return pms
|
||||
}
|
||||
|
||||
func (p *LinuxPlatform) DefaultShell() string {
|
||||
shell := os.Getenv("SHELL")
|
||||
if shell != "" {
|
||||
return shell
|
||||
}
|
||||
return "/bin/bash"
|
||||
}
|
||||
57
internal/platform/platform.go
Normal file
57
internal/platform/platform.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Platform 平台适配接口
|
||||
type Platform interface {
|
||||
// OS 返回操作系统标识
|
||||
OS() string
|
||||
|
||||
// Arch 返回架构标识
|
||||
Arch() string
|
||||
|
||||
// HomeDir 返回用户主目录
|
||||
HomeDir() string
|
||||
|
||||
// ConfigDir 返回配置文件目录
|
||||
ConfigDir() string
|
||||
|
||||
// DataDir 返回数据目录
|
||||
DataDir() string
|
||||
|
||||
// GetEnvVar 获取环境变量
|
||||
GetEnvVar(key string) string
|
||||
|
||||
// SetEnvVar 设置用户级环境变量
|
||||
SetEnvVar(ctx context.Context, key, value string) error
|
||||
|
||||
// AddToPath 添加目录到 PATH
|
||||
AddToPath(ctx context.Context, dir string) error
|
||||
|
||||
// IsAdmin 是否以管理员身份运行
|
||||
IsAdmin() bool
|
||||
|
||||
// PackageManagers 返回可用的包管理器名称
|
||||
PackageManagers() []string
|
||||
|
||||
// DefaultShell 返回默认 Shell
|
||||
DefaultShell() string
|
||||
}
|
||||
|
||||
// Detect 检测当前平台并返回对应的 Platform 实现
|
||||
func Detect() Platform {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return NewWindowsPlatform()
|
||||
case "darwin":
|
||||
return NewDarwinPlatform()
|
||||
case "linux":
|
||||
return NewLinuxPlatform()
|
||||
default:
|
||||
// Fallback to Linux
|
||||
return NewLinuxPlatform()
|
||||
}
|
||||
}
|
||||
83
internal/platform/windows.go
Normal file
83
internal/platform/windows.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// WindowsPlatform Windows 平台实现
|
||||
type WindowsPlatform struct {
|
||||
homeDir string
|
||||
}
|
||||
|
||||
// NewWindowsPlatform 创建 Windows 平台实例
|
||||
func NewWindowsPlatform() *WindowsPlatform {
|
||||
return &WindowsPlatform{
|
||||
homeDir: os.Getenv("USERPROFILE"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) OS() string { return "windows" }
|
||||
func (p *WindowsPlatform) Arch() string { return os.Getenv("PROCESSOR_ARCHITECTURE") }
|
||||
|
||||
func (p *WindowsPlatform) HomeDir() string {
|
||||
return p.homeDir
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) ConfigDir() string {
|
||||
if dir := os.Getenv("APPDATA"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return p.homeDir + "\\AppData\\Roaming"
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) DataDir() string {
|
||||
if dir := os.Getenv("LOCALAPPDATA"); dir != "" {
|
||||
return dir
|
||||
}
|
||||
return p.homeDir + "\\AppData\\Local"
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) GetEnvVar(key string) string {
|
||||
return os.Getenv(key)
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) SetEnvVar(ctx context.Context, key, value string) error {
|
||||
cmd := exec.CommandContext(ctx, "setx", key, value)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) AddToPath(ctx context.Context, dir string) error {
|
||||
currentPath := os.Getenv("PATH")
|
||||
newPath := dir + ";" + currentPath
|
||||
return p.SetEnvVar(ctx, "PATH", newPath)
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) IsAdmin() bool {
|
||||
// Check if running as administrator on Windows
|
||||
cmd := exec.Command("net", "session")
|
||||
err := cmd.Run()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) PackageManagers() []string {
|
||||
var pms []string
|
||||
if _, err := exec.LookPath("scoop"); err == nil {
|
||||
pms = append(pms, "scoop")
|
||||
}
|
||||
if _, err := exec.LookPath("choco"); err == nil {
|
||||
pms = append(pms, "chocolatey")
|
||||
}
|
||||
if _, err := exec.LookPath("winget"); err == nil {
|
||||
pms = append(pms, "winget")
|
||||
}
|
||||
return pms
|
||||
}
|
||||
|
||||
func (p *WindowsPlatform) DefaultShell() string {
|
||||
if _, err := exec.LookPath("pwsh"); err == nil {
|
||||
return "pwsh" // PowerShell 7+
|
||||
}
|
||||
return "powershell" // Windows PowerShell 5.1
|
||||
}
|
||||
Reference in New Issue
Block a user