package platform import ( "fmt" "io" "os" "path/filepath" "strconv" "github.com/Eriyc/rules_wails/pkg/wails3kit/updates" ) type restorePoint struct { path string backup string hadPrior bool } func applyBundle(request helperRequest) error { backupDir, err := os.MkdirTemp("", "wails3kit-backup-*") if err != nil { return err } restores := make([]restorePoint, 0, len(request.Bundle.Files)) for _, file := range request.Bundle.Files { source := filepath.Join(request.StagedPath, filepath.FromSlash(file.Path)) target := filepath.Join(request.InstallRoot, filepath.FromSlash(file.Path)) modeValue, err := strconv.ParseUint(file.Mode, 8, 32) if err != nil { rollback(restores) return err } mode := os.FileMode(modeValue) restore, err := backupTarget(backupDir, target) if err != nil { rollback(restores) return err } restores = append(restores, restore) if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil { rollback(restores) return err } if err := os.RemoveAll(target); err != nil { rollback(restores) return err } if err := copyFile(source, target, mode); err != nil { rollback(restores) return err } } return nil } func backupTarget(backupDir string, target string) (restorePoint, error) { point := restorePoint{path: target} info, err := os.Stat(target) if err != nil { if os.IsNotExist(err) { return point, nil } return point, err } if info.IsDir() { return point, fmt.Errorf("%w: target %s is a directory", updates.ErrInvalidArtifact, target) } backupPath := filepath.Join(backupDir, filepath.Base(target)) if err := copyFile(target, backupPath, info.Mode()); err != nil { return point, err } point.hadPrior = true point.backup = backupPath return point, nil } func rollback(restores []restorePoint) { for index := len(restores) - 1; index >= 0; index-- { restore := restores[index] if restore.hadPrior { info, err := os.Stat(restore.backup) if err == nil { _ = os.RemoveAll(restore.path) _ = os.MkdirAll(filepath.Dir(restore.path), 0o755) _ = copyFile(restore.backup, restore.path, info.Mode()) } continue } _ = os.RemoveAll(restore.path) } } func copyFile(sourcePath string, destinationPath string, mode os.FileMode) error { source, err := os.Open(sourcePath) if err != nil { return err } defer source.Close() return writeFile(destinationPath, source, mode) } func writeFile(path string, source io.Reader, mode os.FileMode) error { file, err := os.Create(path) if err != nil { return err } defer file.Close() if _, err := io.Copy(file, source); err != nil { return err } return file.Chmod(mode) }