1
0

完成一个简易的全局skill、command管理器

This commit is contained in:
2026-02-25 14:33:56 +08:00
parent f4cb809f9d
commit 2d327b5af8
60 changed files with 6053 additions and 1 deletions

View File

@@ -0,0 +1,134 @@
package installer
import (
"fmt"
"os"
"path/filepath"
"skillmgr/pkg/fileutil"
)
// Transaction 事务性安装
type Transaction struct {
stagingDir string
targetDir string
fileMap map[string]string // source → dest
}
// NewTransaction 创建事务
// 在系统临时目录创建 staging 目录
func NewTransaction(targetDir string, fileMap map[string]string) (*Transaction, error) {
// 在系统临时目录创建 staging 目录
stagingDir, err := os.MkdirTemp("", "skillmgr-*")
if err != nil {
return nil, fmt.Errorf("创建 staging 目录失败: %w", err)
}
return &Transaction{
stagingDir: stagingDir,
targetDir: targetDir,
fileMap: fileMap,
}, nil
}
// Stage 阶段:复制文件到 staging 目录
func (t *Transaction) Stage() error {
stagedCount := 0
for src, dest := range t.fileMap {
// 计算相对于 targetDir 的路径
relPath, err := filepath.Rel(t.targetDir, dest)
if err != nil {
return fmt.Errorf("计算相对路径失败: %w", err)
}
stagingDest := filepath.Join(t.stagingDir, relPath)
// 确保目标目录存在
if err := os.MkdirAll(filepath.Dir(stagingDest), 0755); err != nil {
return fmt.Errorf("创建 staging 子目录失败: %w", err)
}
// 复制文件
if err := fileutil.CopyFile(src, stagingDest); err != nil {
return fmt.Errorf("复制文件到 staging 失败: %w", err)
}
stagedCount++
}
// 验证 staging 完整性:检查文件数量是否与预期一致
if err := t.verifyStagingIntegrity(stagedCount); err != nil {
return fmt.Errorf("staging 验证失败: %w", err)
}
return nil
}
// verifyStagingIntegrity 验证 staging 目录中的文件数量
func (t *Transaction) verifyStagingIntegrity(expectedCount int) error {
actualCount := 0
err := filepath.Walk(t.stagingDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
actualCount++
}
return nil
})
if err != nil {
return fmt.Errorf("遍历 staging 目录失败: %w", err)
}
if actualCount != expectedCount {
return fmt.Errorf("文件数量不匹配: 预期 %d 个文件,实际 %d 个", expectedCount, actualCount)
}
return nil
}
// Commit 提交:将 staging 目录移动到目标位置
func (t *Transaction) Commit() error {
// 确保目标目录的父目录存在
if err := os.MkdirAll(filepath.Dir(t.targetDir), 0755); err != nil {
return fmt.Errorf("创建目标父目录失败: %w", err)
}
// 如果目标目录已存在,先删除(已经过用户确认)
if _, err := os.Stat(t.targetDir); err == nil {
if err := os.RemoveAll(t.targetDir); err != nil {
return fmt.Errorf("删除已存在的目标目录失败: %w", err)
}
}
// 尝试原子性移动 staging 目录到目标位置
if err := os.Rename(t.stagingDir, t.targetDir); err != nil {
// 如果跨文件系统Rename 会失败,改用复制
// 使用 defer 确保 staging 目录被清理
defer os.RemoveAll(t.stagingDir)
if err := fileutil.CopyDir(t.stagingDir, t.targetDir); err != nil {
return fmt.Errorf("复制 staging 到目标失败: %w", err)
}
}
return nil
}
// Rollback 回滚:清理 staging 目录
func (t *Transaction) Rollback() {
if t.stagingDir != "" {
os.RemoveAll(t.stagingDir)
}
}
// StagingDir 获取 staging 目录路径
func (t *Transaction) StagingDir() string {
return t.stagingDir
}
// TargetDir 获取目标目录路径
func (t *Transaction) TargetDir() string {
return t.targetDir
}