Browse Source

feat: Update installation script and improve error handling

- Updated `install.sh` to include versioning and enhanced bilingual messages for better user feedback.
- Refactored platform detection logic to dynamically set binary names based on the version.
- Added checks for system requirements, including curl installation and write permissions.
- Improved error handling with context-specific messages for better debugging.
- Cleaned up the `go.mod` file by consolidating dependency requirements.

This commit enhances the installation process and user experience by providing clearer feedback and ensuring necessary prerequisites are met.
pull/20/head
Xx 6 months ago
parent
commit
af6c9f05de
  1. 8
      .vscode/settings.json
  2. BIN
      bin/cursor_id_modifier_v2.0.0_linux_amd64
  3. BIN
      bin/cursor_id_modifier_v2.0.0_mac_intel
  4. BIN
      bin/cursor_id_modifier_v2.0.0_mac_m1
  5. BIN
      bin/cursor_id_modifier_v2.0.0_windows_amd64.exe
  6. 6
      go.mod
  7. 112
      install.sh
  8. 334
      main.go
  9. 53
      scripts/build_all.bat

8
.vscode/settings.json

@ -6,12 +6,18 @@
"fatih", "fatih",
"gcflags", "gcflags",
"GOARCH", "GOARCH",
"IMAGENAME",
"ldflags", "ldflags",
"LOCALAPPDATA", "LOCALAPPDATA",
"mktemp",
"msys",
"pgrep",
"pkill", "pkill",
"runas", "runas",
"setlocal", "setlocal",
"taskkill", "taskkill",
"trimpath"
"tasklist",
"trimpath",
"xattr"
] ]
} }

BIN
bin/cursor_id_modifier_v2.0.0_linux_amd64

BIN
bin/cursor_id_modifier_v2.0.0_mac_intel

BIN
bin/cursor_id_modifier_v2.0.0_mac_m1

BIN
bin/cursor_id_modifier_v2.0.0_windows_amd64.exe

6
go.mod

@ -2,12 +2,10 @@ module cursor-id-modifier
go 1.21 go 1.21
require (
github.com/fatih/color v1.15.0
golang.org/x/sys v0.13.0
)
require github.com/fatih/color v1.15.0
require ( require (
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-isatty v0.0.17 // indirect
golang.org/x/sys v0.13.0 // indirect
) )

112
install.sh

@ -1,75 +1,141 @@
#!/bin/bash #!/bin/bash
# Error handling function / 错误处理函数
# Version / 版本号
VERSION="v2.0.0"
# Bilingual message functions / 双语消息函数
error() { error() {
echo "Error/错误: $1" >&2
echo "❌ Error: $1"
echo "❌ 错误:$2"
exit 1 exit 1
} }
info() {
echo "ℹ️ $1"
echo "ℹ️ $2"
}
success() {
echo "$1"
echo "$2"
}
# Detect OS and architecture / 检测操作系统和架构 # Detect OS and architecture / 检测操作系统和架构
detect_platform() { detect_platform() {
# Get lowercase OS name and architecture / 获取小写操作系统名称和架构
OS=$(uname -s | tr '[:upper:]' '[:lower:]') OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m) ARCH=$(uname -m)
# Set binary name based on platform / 根据平台设置二进制文件名
case "$OS" in case "$OS" in
linux*) linux*)
case "$ARCH" in case "$ARCH" in
x86_64) BINARY_NAME="cursor_id_modifier_v2.0.0_linux_amd64" ;;
*) error "Unsupported Linux architecture/不支持的Linux架构: $ARCH" ;;
x86_64) BINARY_NAME="cursor_id_modifier_${VERSION}_linux_amd64" ;;
*) error "Unsupported Linux architecture: $ARCH" "不支持的Linux架构:$ARCH" ;;
esac esac
;; ;;
darwin*) darwin*)
case "$ARCH" in case "$ARCH" in
x86_64) BINARY_NAME="cursor_id_modifier_v2.0.0_mac_intel" ;;
arm64) BINARY_NAME="cursor_id_modifier_v2.0.0_mac_m1" ;;
*) error "Unsupported macOS architecture/不支持的macOS架构: $ARCH" ;;
x86_64) BINARY_NAME="cursor_id_modifier_${VERSION}_darwin_amd64_intel" ;;
arm64) BINARY_NAME="cursor_id_modifier_${VERSION}_darwin_arm64_m1" ;;
*) error "Unsupported macOS architecture: $ARCH" "不支持的macOS架构:$ARCH" ;;
esac
;;
msys*|mingw*|cygwin*)
case "$ARCH" in
x86_64) BINARY_NAME="cursor_id_modifier_${VERSION}_windows_amd64.exe" ;;
*) error "Unsupported Windows architecture: $ARCH" "不支持的Windows架构:$ARCH" ;;
esac esac
;; ;;
*) *)
error "Unsupported operating system/不支持的操作系统: $OS"
error "Unsupported operating system: $OS" "不支持的操作系统:$OS"
;; ;;
esac esac
} }
# Check root privileges / 检查root权限
if [ "$(id -u)" -ne 0 ]; then
error "This script must be run with sudo or as root/此脚本必须使用sudo或root权限运行"
# Check system requirements / 检查系统要求
check_requirements() {
info "Checking system requirements..." "正在检查系统要求..."
# Check curl
if ! command -v curl >/dev/null 2>&1; then
error "curl is required. Please install curl first." \
"需要安装 curl。请先安装 curl 后再运行此脚本。"
fi
# Check write permissions / 检查写入权限
if [ ! -w "$INSTALL_DIR" ]; then
error "No write permission for $INSTALL_DIR. Please run with sudo." \
"没有 $INSTALL_DIR 的写入权限。请使用 sudo 运行此脚本。"
}
}
# Verify binary / 验证二进制文件
verify_binary() {
info "Verifying binary..." "正在验证二进制文件..."
if [ ! -f "$TEMP_DIR/$BINARY_NAME" ]; then
error "Binary file download failed or does not exist" \
"二进制文件下载失败或不存在"
fi fi
# Check file size / 检查文件大小
local size=$(wc -c < "$TEMP_DIR/$BINARY_NAME")
if [ "$size" -lt 1000000 ]; then # At least 1MB / 至少1MB
error "Downloaded file size is abnormal, download might be incomplete" \
"下载的文件大小异常,可能下载不完整"
}
}
# Main installation process / 主安装流程
main() {
info "Starting installation of cursor-id-modifier ${VERSION}..." \
"开始安装 cursor-id-modifier ${VERSION}..."
# Initialize installation / 初始化安装 # Initialize installation / 初始化安装
detect_platform detect_platform
INSTALL_DIR="/usr/local/bin" INSTALL_DIR="/usr/local/bin"
[ -d "$INSTALL_DIR" ] || mkdir -p "$INSTALL_DIR" [ -d "$INSTALL_DIR" ] || mkdir -p "$INSTALL_DIR"
# Download binary / 下载二进制文件
echo "Downloading cursor-id-modifier for/正在下载 $OS ($ARCH)..."
# Check requirements / 检查要求
check_requirements
# Create temp directory / 创建临时目录
TEMP_DIR=$(mktemp -d) TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT
# Download binary / 下载二进制文件
info "Downloading cursor-id-modifier ($OS-$ARCH)..." \
"正在下载 cursor-id-modifier ($OS-$ARCH)..."
DOWNLOAD_URL="https://github.com/yuaotian/go-cursor-help/raw/main/bin/$BINARY_NAME" DOWNLOAD_URL="https://github.com/yuaotian/go-cursor-help/raw/main/bin/$BINARY_NAME"
if ! curl -fsSL "$DOWNLOAD_URL" -o "$TEMP_DIR/$BINARY_NAME"; then if ! curl -fsSL "$DOWNLOAD_URL" -o "$TEMP_DIR/$BINARY_NAME"; then
error "Failed to download binary/下载二进制文件失败"
error "Failed to download binary" "下载二进制文件失败"
fi fi
# Verify download / 验证下载
verify_binary
# Set permissions / 设置权限 # Set permissions / 设置权限
info "Setting execution permissions..." "正在设置执行权限..."
if ! chmod +x "$TEMP_DIR/$BINARY_NAME"; then if ! chmod +x "$TEMP_DIR/$BINARY_NAME"; then
error "Failed to make binary executable/无法设置可执行权限"
error "Failed to set executable permissions" "无法设置可执行权限"
fi fi
# Handle macOS security / 处理macOS安全设置 # Handle macOS security / 处理macOS安全设置
if [ "$OS" = "darwin" ]; then if [ "$OS" = "darwin" ]; then
echo "Removing macOS quarantine attribute/移除macOS隔离属性..."
info "Handling macOS security settings..." "正在处理macOS安全设置..."
xattr -d com.apple.quarantine "$TEMP_DIR/$BINARY_NAME" 2>/dev/null || true xattr -d com.apple.quarantine "$TEMP_DIR/$BINARY_NAME" 2>/dev/null || true
fi fi
# Install binary / 安装二进制文件 # Install binary / 安装二进制文件
info "Installing binary..." "正在安装二进制文件..."
if ! mv "$TEMP_DIR/$BINARY_NAME" "$INSTALL_DIR/cursor-id-modifier"; then if ! mv "$TEMP_DIR/$BINARY_NAME" "$INSTALL_DIR/cursor-id-modifier"; then
error "Failed to install binary/安装二进制文件失败"
error "Failed to install binary" "安装二进制文件失败"
fi fi
# Cleanup / 清理
rm -rf "$TEMP_DIR"
success "Installation successful! You can now run 'cursor-id-modifier' from anywhere." \
"安装成功!现在可以在任何位置运行 'cursor-id-modifier'。"
success "For help, run 'cursor-id-modifier --help'" \
"如需帮助,请运行 'cursor-id-modifier --help'"
}
echo "✅ Installation successful! You can now run 'cursor-id-modifier' from anywhere."
echo "✅ 安装成功!现在可以在任何位置运行 'cursor-id-modifier'。"
# Start installation / 开始安装
main

334
main.go

@ -1,4 +1,3 @@
// 主程序包 / Main package
package main package main
// 导入所需的包 / Import required packages // 导入所需的包 / Import required packages
@ -9,6 +8,7 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"os" "os"
"os/exec" "os/exec"
"os/user" "os/user"
@ -16,9 +16,10 @@ import (
"runtime" "runtime"
"strings" "strings"
"time" "time"
"github.com/fatih/color" "github.com/fatih/color"
"golang.org/x/sys/windows"
"context"
"errors"
"runtime/debug"
) )
// 语言类型和常量 / Language type and constants // 语言类型和常量 / Language type and constants
@ -30,6 +31,12 @@ const (
// Version constant // Version constant
Version = "1.0.1" Version = "1.0.1"
// 定义错误类型常量
ErrPermission = "permission_error"
ErrConfig = "config_error"
ErrProcess = "process_error"
ErrSystem = "system_error"
) )
// TextResource 存储多语言文本 / TextResource stores multilingual text // TextResource 存储多语言文本 / TextResource stores multilingual text
@ -57,9 +64,11 @@ type StorageConfig struct {
// AppError 定义错误类型 / AppError defines error types // AppError 定义错误类型 / AppError defines error types
type AppError struct { type AppError struct {
Type string
Op string Op string
Path string Path string
Err error Err error
Context map[string]interface{}
} }
// ProgressSpinner 用于显示进度动画 / ProgressSpinner for showing progress animation // ProgressSpinner 用于显示进度动画 / ProgressSpinner for showing progress animation
@ -69,6 +78,14 @@ type ProgressSpinner struct {
message string message string
} }
// SpinnerConfig 定义进度条配置
type SpinnerConfig struct {
Frames []string
Delay time.Duration
}
// 全局变量 / Global variables // 全局变量 / Global variables
var ( var (
currentLanguage = CN // 默认为中文 / Default to Chinese currentLanguage = CN // 默认为中文 / Default to Chinese
@ -103,13 +120,13 @@ var (
// Error implementation for AppError // Error implementation for AppError
func (e *AppError) Error() string { func (e *AppError) Error() string {
if e.Path != "" {
return fmt.Sprintf("%s: %v [Path: %s]", e.Op, e.Err, e.Path)
if e.Context != nil {
return fmt.Sprintf("[%s] %s: %v (context: %v)", e.Type, e.Op, e.Err, e.Context)
} }
return fmt.Sprintf("%s: %v", e.Op, e.Err)
return fmt.Sprintf("[%s] %s: %v", e.Type, e.Op, e.Err)
} }
// NewStorageConfig 创建新的配置实例 / Creates a new configuration instance
// NewStorageConfig 创建新的实例 / Creates a new configuration instance
func NewStorageConfig() *StorageConfig { func NewStorageConfig() *StorageConfig {
return &StorageConfig{ return &StorageConfig{
TelemetryMacMachineId: generateMachineId(), TelemetryMacMachineId: generateMachineId(),
@ -120,7 +137,7 @@ func NewStorageConfig() *StorageConfig {
} }
} }
// 生成类似原始machineId的字符串(64位小十六进制) / Generate a string similar to the original machineId (64-bit lowercase hex)
// 生成类似原始machineId的字符串(64位小十六进制) / Generate a string similar to the original machineId (64-bit lowercase hex)
func generateMachineId() string { func generateMachineId() string {
data := make([]byte, 32) data := make([]byte, 32)
if _, err := rand.Read(data); err != nil { if _, err := rand.Read(data); err != nil {
@ -168,6 +185,11 @@ func (s *ProgressSpinner) Stop() {
fmt.Println() fmt.Println()
} }
// Start starts the spinner animation
func (s *ProgressSpinner) Start() {
s.current = 0
}
// File and system operations // File and system operations
func getConfigPath() (string, error) { func getConfigPath() (string, error) {
@ -196,17 +218,32 @@ func getConfigPath() (string, error) {
func safeWriteFile(path string, data []byte, perm os.FileMode) error { func safeWriteFile(path string, data []byte, perm os.FileMode) error {
dir := filepath.Dir(path) dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0755); err != nil { if err := os.MkdirAll(dir, 0755); err != nil {
return &AppError{"create directory", dir, err}
return &AppError{
Type: ErrSystem,
Op: "create directory",
Path: dir,
Err: err,
}
} }
tmpPath := path + ".tmp" tmpPath := path + ".tmp"
if err := os.WriteFile(tmpPath, data, perm); err != nil { if err := os.WriteFile(tmpPath, data, perm); err != nil {
return &AppError{"write temporary file", tmpPath, err}
return &AppError{
Type: ErrSystem,
Op: "write temporary file",
Path: tmpPath,
Err: err,
}
} }
if err := os.Rename(tmpPath, path); err != nil { if err := os.Rename(tmpPath, path); err != nil {
os.Remove(tmpPath) os.Remove(tmpPath)
return &AppError{"rename file", path, err}
return &AppError{
Type: ErrSystem,
Op: "rename file",
Path: path,
Err: err,
}
} }
return nil return nil
@ -218,24 +255,36 @@ func setFilePermissions(filePath string) error {
// Process management functions // Process management functions
func killCursorProcesses() error {
if runtime.GOOS == "windows" {
// First try graceful shutdown
exec.Command("taskkill", "/IM", "Cursor.exe").Run()
exec.Command("taskkill", "/IM", "cursor.exe").Run()
type ProcessManager struct {
config *SystemConfig
}
time.Sleep(time.Second)
func (pm *ProcessManager) killCursorProcesses() error {
ctx, cancel := context.WithTimeout(context.Background(), pm.config.Timeout)
defer cancel()
// Force kill any remaining instances
exec.Command("taskkill", "/F", "/IM", "Cursor.exe").Run()
exec.Command("taskkill", "/F", "/IM", "cursor.exe").Run()
} else {
exec.Command("pkill", "-f", "Cursor").Run()
exec.Command("pkill", "-f", "cursor").Run()
for attempt := 0; attempt < pm.config.RetryAttempts; attempt++ {
if err := pm.killProcess(ctx); err != nil {
time.Sleep(pm.config.RetryDelay)
continue
} }
return nil return nil
} }
return &AppError{
Type: ErrProcess,
Op: "kill_processes",
Err: errors.New("failed to kill all Cursor processes after retries"),
}
}
func (pm *ProcessManager) killProcess(ctx context.Context) error {
if runtime.GOOS == "windows" {
return pm.killWindowsProcess(ctx)
}
return pm.killUnixProcess(ctx)
}
func checkCursorRunning() bool { func checkCursorRunning() bool {
var cmd *exec.Cmd var cmd *exec.Cmd
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
@ -278,11 +327,11 @@ func printCyberpunkBanner() {
banner := ` banner := `
` `
cyan.Println(banner) cyan.Println(banner)
yellow.Println("\t\t>> Cursor ID Modifier v1.0 <<") yellow.Println("\t\t>> Cursor ID Modifier v1.0 <<")
@ -350,11 +399,21 @@ func saveConfig(config *StorageConfig) error {
content, err := json.MarshalIndent(config, "", " ") content, err := json.MarshalIndent(config, "", " ")
if err != nil { if err != nil {
return &AppError{"generate JSON", "", err}
return &AppError{
Type: ErrSystem,
Op: "generate JSON",
Path: "",
Err: err,
}
} }
if err := os.Chmod(configPath, 0666); err != nil && !os.IsNotExist(err) { if err := os.Chmod(configPath, 0666); err != nil && !os.IsNotExist(err) {
return &AppError{"modify file permissions", configPath, err}
return &AppError{
Type: ErrSystem,
Op: "modify file permissions",
Path: configPath,
Err: err,
}
} }
if err := safeWriteFile(configPath, content, 0666); err != nil { if err := safeWriteFile(configPath, content, 0666); err != nil {
@ -386,18 +445,23 @@ func readExistingConfig() (*StorageConfig, error) {
return &config, nil return &config, nil
} }
func loadAndUpdateConfig() (*StorageConfig, error) {
func loadAndUpdateConfig(ui *UI) (*StorageConfig, error) {
configPath, err := getConfigPath() configPath, err := getConfigPath()
if err != nil { if err != nil {
return nil, err return nil, err
} }
text := texts[currentLanguage] text := texts[currentLanguage]
showProgress(text.ReadingConfig)
ui.showProgress(text.ReadingConfig)
_, err = os.ReadFile(configPath) _, err = os.ReadFile(configPath)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return nil, &AppError{"read config file", configPath, err}
return nil, &AppError{
Type: ErrSystem,
Op: "read config file",
Path: configPath,
Err: err,
}
} }
showProgress(text.GeneratingIds) showProgress(text.GeneratingIds)
@ -409,9 +473,13 @@ func loadAndUpdateConfig() (*StorageConfig, error) {
func checkAdminPrivileges() (bool, error) { func checkAdminPrivileges() (bool, error) {
switch runtime.GOOS { switch runtime.GOOS {
case "windows": case "windows":
cmd := exec.Command("net", "session")
err := cmd.Run()
return err == nil, nil
cmd := exec.Command("whoami", "/groups")
output, err := cmd.Output()
if err != nil {
return false, err
}
return strings.Contains(string(output), "S-1-16-12288") ||
strings.Contains(string(output), "S-1-5-32-544"), nil
case "darwin", "linux": case "darwin", "linux":
currentUser, err := user.Current() currentUser, err := user.Current()
@ -425,33 +493,6 @@ func checkAdminPrivileges() (bool, error) {
} }
} }
func selfElevate() error {
verb := "runas"
exe, err := os.Executable()
if err != nil {
return err
}
cwd, err := os.Getwd()
if err != nil {
return err
}
// 将字符串转换为UTF-16指针
verbPtr, _ := windows.UTF16PtrFromString(verb)
exePtr, _ := windows.UTF16PtrFromString(exe)
cwdPtr, _ := windows.UTF16PtrFromString(cwd)
argPtr, _ := windows.UTF16PtrFromString("")
var showCmd int32 = 1 //SW_NORMAL
err = windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
if err != nil {
return err
}
os.Exit(0)
return nil
}
// Utility functions // Utility functions
func detectLanguage() Language { func detectLanguage() Language {
@ -472,31 +513,57 @@ func waitExit() {
bufio.NewReader(os.Stdin).ReadString('\n') bufio.NewReader(os.Stdin).ReadString('\n')
} }
func handleError(msg string, err error) {
if appErr, ok := err.(*AppError); ok {
color.Red("%s: %v", msg, appErr)
} else {
color.Red("%s: %v", msg, err)
// 错误处理函数
func handleError(err error) {
if err == nil {
return
}
logger := log.New(os.Stderr, "", log.LstdFlags)
switch e := err.(type) {
case *AppError:
logger.Printf("[ERROR] %v\n", e)
if e.Type == ErrPermission {
showPrivilegeError()
}
default:
logger.Printf("[ERROR] Unexpected error: %v\n", err)
} }
} }
// Main program entry // Main program entry
func main() { func main() {
// 初始化错误恢复
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
color.Red(texts[currentLanguage].ErrorPrefix, r)
fmt.Println("\nAn error occurred! / 发生错误!")
log.Printf("Panic recovered: %v\n", r)
debug.PrintStack()
waitExit() waitExit()
} }
}() }()
// 始化配置
config := initConfig()
// 初始化组件
ui := NewUI(&config.UI)
pm := &ProcessManager{
config: &SystemConfig{
RetryAttempts: 3,
RetryDelay: time.Second,
Timeout: 30 * time.Second,
},
}
// 权限检查
os.Stdout.Sync() os.Stdout.Sync()
currentLanguage = detectLanguage() currentLanguage = detectLanguage()
isAdmin, err := checkAdminPrivileges() isAdmin, err := checkAdminPrivileges()
if err != nil { if err != nil {
handleError("permission check failed", err)
handleError(err)
waitExit() waitExit()
return return
} }
@ -504,7 +571,7 @@ func main() {
if !isAdmin && runtime.GOOS == "windows" { if !isAdmin && runtime.GOOS == "windows" {
fmt.Println("Requesting administrator privileges... / 请求管理员权限...") fmt.Println("Requesting administrator privileges... / 请求管理员权限...")
if err := selfElevate(); err != nil { if err := selfElevate(); err != nil {
handleError("failed to elevate privileges", err)
handleError(err)
showPrivilegeError() showPrivilegeError()
waitExit() waitExit()
return return
@ -518,7 +585,7 @@ func main() {
if checkCursorRunning() { if checkCursorRunning() {
fmt.Println("\nDetected running Cursor instance(s). Closing... / 检测到正在运行的 Cursor 实例,正在关闭...") fmt.Println("\nDetected running Cursor instance(s). Closing... / 检测到正在运行的 Cursor 实例,正在关闭...")
if err := killCursorProcesses(); err != nil {
if err := pm.killCursorProcesses(); err != nil {
fmt.Println("Warning: Could not close all Cursor instances. Please close them manually. / 警告:无法关闭所有 Cursor 实例,请手动关闭。") fmt.Println("Warning: Could not close all Cursor instances. Please close them manually. / 警告:无法关闭所有 Cursor 实例,请手动关闭。")
waitExit() waitExit()
return return
@ -540,17 +607,17 @@ func main() {
oldConfig = nil oldConfig = nil
} }
config, err := loadAndUpdateConfig()
storageConfig, err := loadAndUpdateConfig(ui)
if err != nil { if err != nil {
handleError("configuration update failed", err)
handleError(err)
waitExit() waitExit()
return return
} }
showIdComparison(oldConfig, config)
showIdComparison(oldConfig, storageConfig)
if err := saveConfig(config); err != nil {
handleError("failed to save configuration", err)
if err := saveConfig(storageConfig); err != nil {
handleError(err)
waitExit() waitExit()
return return
} }
@ -559,3 +626,118 @@ func main() {
fmt.Println("\nOperation completed! / 操作完成!") fmt.Println("\nOperation completed! / 操作完成!")
waitExit() waitExit()
} }
// 优化配置结构
type Config struct {
Storage StorageConfig
UI UIConfig
System SystemConfig
}
type UIConfig struct {
Language Language
Theme string
Spinner SpinnerConfig
}
type SystemConfig struct {
RetryAttempts int
RetryDelay time.Duration
Timeout time.Duration
}
// 配置初始化函数
func initConfig() *Config {
return &Config{
Storage: StorageConfig{
Version: Version,
},
UI: UIConfig{
Language: detectLanguage(),
Theme: "default",
Spinner: SpinnerConfig{
Frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
Delay: 100 * time.Millisecond,
},
},
System: SystemConfig{
RetryAttempts: 3,
RetryDelay: time.Second,
Timeout: 30 * time.Second,
},
}
}
// UI 组件���化
type UI struct {
config *UIConfig
spinner *ProgressSpinner
}
func NewUI(config *UIConfig) *UI {
return &UI{
config: config,
spinner: NewProgressSpinner(""),
}
}
func (ui *UI) showProgress(message string) {
ui.spinner.message = message
ui.spinner.Start()
defer ui.spinner.Stop()
ticker := time.NewTicker(ui.config.Spinner.Delay)
defer ticker.Stop()
for i := 0; i < 15; i++ {
<-ticker.C
ui.spinner.Spin()
}
}
func (pm *ProcessManager) killWindowsProcess(ctx context.Context) error {
// 使用 taskkill 命令结束进程
exec.CommandContext(ctx, "taskkill", "/IM", "Cursor.exe").Run()
time.Sleep(pm.config.RetryDelay)
exec.CommandContext(ctx, "taskkill", "/F", "/IM", "Cursor.exe").Run()
return nil
}
func (pm *ProcessManager) killUnixProcess(ctx context.Context) error {
exec.CommandContext(ctx, "pkill", "-f", "Cursor").Run()
exec.CommandContext(ctx, "pkill", "-f", "cursor").Run()
return nil
}
func selfElevate() error {
switch runtime.GOOS {
case "windows":
// 使用 cmd 实现 Windows 下的提权
verb := "runas"
exe, _ := os.Executable()
cwd, _ := os.Getwd()
args := strings.Join(os.Args[1:], " ")
cmd := exec.Command("cmd", "/C", "start", verb, exe, args)
cmd.Dir = cwd
return cmd.Run()
case "darwin", "linux":
// Unix 系统使用 sudo
exe, err := os.Executable()
if err != nil {
return err
}
cmd := exec.Command("sudo", append([]string{exe}, os.Args[1:]...)...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
}

53
scripts/build_all.bat

@ -15,15 +15,6 @@ set "LDFLAGS=-s -w"
set "BUILDMODE=pie" set "BUILDMODE=pie"
set "GCFLAGS=-N -l" set "GCFLAGS=-N -l"
:: 检查是否安装了必要的交叉编译工具
where gcc >nul 2>nul
if %errorlevel% neq 0 (
echo %RED%错误: 未找到 gcc,这可能会影响 Mac 系统的交叉编译%RESET%
echo %YELLOW%请安装 MinGW-w64 或其他 gcc 工具链%RESET%
pause
exit /b 1
)
:: 设置 CGO :: 设置 CGO
set CGO_ENABLED=0 set CGO_ENABLED=0
@ -32,31 +23,38 @@ echo %YELLOW%开始构建 version %VERSION%%RESET%
echo %YELLOW%使用优化标志: LDFLAGS=%LDFLAGS%, BUILDMODE=%BUILDMODE%%RESET% echo %YELLOW%使用优化标志: LDFLAGS=%LDFLAGS%, BUILDMODE=%BUILDMODE%%RESET%
echo %YELLOW%CGO_ENABLED=%CGO_ENABLED%%RESET% echo %YELLOW%CGO_ENABLED=%CGO_ENABLED%%RESET%
:: 仅在必要时清理旧文件
if "%1"=="clean" (
echo 清理旧构建文件...
if exist "..\bin" rd /s /q "..\bin"
:: 清理旧的构建文件
echo %YELLOW%清理旧的构建文件...%RESET%
if exist "..\bin" (
rd /s /q "..\bin"
echo %GREEN%清理完成%RESET%
) else (
echo %YELLOW%bin 目录不存在,无需清理%RESET%
) )
:: 创建输出目录 :: 创建输出目录
if not exist "..\bin" mkdir "..\bin" 2>nul
mkdir "..\bin" 2>nul
:: 定义目标平台数组 :: 定义目标平台数组
set platforms[0].os=windows set platforms[0].os=windows
set platforms[0].arch=amd64 set platforms[0].arch=amd64
set platforms[0].ext=.exe set platforms[0].ext=.exe
set platforms[0].suffix=
set platforms[1].os=darwin set platforms[1].os=darwin
set platforms[1].arch=amd64 set platforms[1].arch=amd64
set platforms[1].ext= set platforms[1].ext=
set platforms[1].suffix=_intel
set platforms[2].os=darwin set platforms[2].os=darwin
set platforms[2].arch=arm64 set platforms[2].arch=arm64
set platforms[2].ext= set platforms[2].ext=
set platforms[2].suffix=_m1
set platforms[3].os=linux set platforms[3].os=linux
set platforms[3].arch=amd64 set platforms[3].arch=amd64
set platforms[3].ext= set platforms[3].ext=
set platforms[3].suffix=
:: 设置开始时间 :: 设置开始时间
set start_time=%time% set start_time=%time%
@ -68,6 +66,7 @@ for /L %%i in (0,1,3) do (
set "os=!platforms[%%i].os!" set "os=!platforms[%%i].os!"
set "arch=!platforms[%%i].arch!" set "arch=!platforms[%%i].arch!"
set "ext=!platforms[%%i].ext!" set "ext=!platforms[%%i].ext!"
set "suffix=!platforms[%%i].suffix!"
echo. echo.
echo Building for !os! !arch!... echo Building for !os! !arch!...
@ -75,38 +74,22 @@ for /L %%i in (0,1,3) do (
set GOOS=!os! set GOOS=!os!
set GOARCH=!arch! set GOARCH=!arch!
:: 为 darwin 系统设置特殊编译参数和文件名
if "!os!"=="darwin" (
set "extra_flags=-tags ios"
if "!arch!"=="amd64" (
set "outfile=..\bin\cursor_id_modifier_v%VERSION%_mac_intel!ext!"
) else (
set "outfile=..\bin\cursor_id_modifier_v%VERSION%_mac_m1!ext!"
)
) else (
set "extra_flags="
set "outfile=..\bin\cursor_id_modifier_v%VERSION%_!os!_!arch!!ext!"
)
:: 构建输出文件名
set "outfile=..\bin\cursor_id_modifier_v%VERSION%_!os!_!arch!!suffix!!ext!"
go build -trimpath !extra_flags! -buildmode=%BUILDMODE% -ldflags="%LDFLAGS%" -gcflags="%GCFLAGS%" -o "!outfile!" ..\main.go
:: 执行构建
go build -trimpath -buildmode=%BUILDMODE% -ldflags="%LDFLAGS%" -gcflags="%GCFLAGS%" -o "!outfile!" ..\main.go
if !errorlevel! equ 0 ( if !errorlevel! equ 0 (
echo %GREEN%Build successful: !outfile!%RESET% echo %GREEN%Build successful: !outfile!%RESET%
) else ( ) else (
echo %RED%Build failed for !os! !arch!%RESET% echo %RED%Build failed for !os! !arch!%RESET%
echo %YELLOW%如果是 Mac 系统编译失败,请确保:%RESET%
echo %YELLOW%1. 已安装 MinGW-w64%RESET%
echo %YELLOW%2. 已设置 GOARCH 和 GOOS%RESET%
echo %YELLOW%3. CGO_ENABLED=0%RESET%
) )
) )
:: 计算总耗时 :: 计算总耗时
set end_time=%time% set end_time=%time%
set options="tokens=1-4 delims=:.,"
for /f %options% %%a in ("%start_time%") do set start_s=%%a&set start_m=%%b&set start_h=%%c
for /f %options% %%a in ("%end_time%") do set end_s=%%a&set end_m=%%b&set end_h=%%c
set /a duration = (end_h - start_h) * 3600 + (end_m - start_m) * 60 + (end_s - start_s)
set /a duration = %end_time:~0,2% * 3600 + %end_time:~3,2% * 60 + %end_time:~6,2% - (%start_time:~0,2% * 3600 + %start_time:~3,2% * 60 + %start_time:~6,2%)
echo. echo.
echo %GREEN%所有构建完成! 总耗时: %duration%%RESET% echo %GREEN%所有构建完成! 总耗时: %duration%%RESET%

Loading…
Cancel
Save