updated spm package
This commit is contained in:
parent
3f67a0a6de
commit
7373e47c1d
11 changed files with 594 additions and 475 deletions
180
spm/install.go
180
spm/install.go
|
@ -2,12 +2,18 @@ package spm
|
|||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DecompressToTemp(filePath string) (string, error) {
|
||||
|
@ -96,65 +102,159 @@ func DecompressToTemp(filePath string) (string, error) {
|
|||
return decompressDir, nil
|
||||
}
|
||||
|
||||
// tailLogFile continuously reads new lines from logFile until done is closed.
|
||||
// It prints each new line and searches for a percentage (e.g. " 47%") to call UpdateProgress.
|
||||
func tailLogFile(logFile string, done <-chan struct{}, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var offset int64
|
||||
re := regexp.MustCompile(`\s+(\d+)%`)
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
f, err := os.Open(logFile)
|
||||
if err != nil {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
// Seek to the last known offset.
|
||||
f.Seek(offset, io.SeekStart)
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fmt.Printf("[ROBOPROGRESS-LOG] %s\n", line)
|
||||
// Look for a percentage.
|
||||
if matches := re.FindStringSubmatch(line); len(matches) == 2 {
|
||||
var percent int
|
||||
_, err := fmt.Sscanf(matches[1], "%d", &percent)
|
||||
if err == nil {
|
||||
fmt.Printf("[ROBOPROGRESS] Parsed progress: %d%%\n", percent)
|
||||
UpdateProgress(percent, "Copying files to install directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the offset.
|
||||
newOffset, _ := f.Seek(0, io.SeekCurrent)
|
||||
offset = newOffset
|
||||
f.Close()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MoveFilesToInstallDir copies files from tempDir to installDir.
|
||||
// On Windows it uses robocopy with logging so that as much output as possible is printed,
|
||||
// and a separate goroutine tails the log file to update progress.
|
||||
// The log file is saved (not automatically deleted) so you can inspect it.
|
||||
func MoveFilesToInstallDir(tempDir, installDir, pkgType string) error {
|
||||
// Ensure tempDir exists before processing
|
||||
// Ensure tempDir exists.
|
||||
if _, err := os.Stat(tempDir); os.IsNotExist(err) {
|
||||
return fmt.Errorf("tempDir does not exist: %s", tempDir)
|
||||
}
|
||||
|
||||
// If the package type is "browser", set the subdirectory to "browser"
|
||||
// If package type is "browser", adjust installDir.
|
||||
if pkgType == "browser" {
|
||||
installDir = filepath.Join(installDir, "browser")
|
||||
}
|
||||
|
||||
// Count total files to copy
|
||||
var totalFiles, copiedFiles int
|
||||
err := filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
totalFiles++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
// Ensure destination exists.
|
||||
if err := os.MkdirAll(installDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("failed to create installDir: %w", err)
|
||||
}
|
||||
|
||||
// Copy files and track progress
|
||||
err = filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Create a temporary log file.
|
||||
logFile := filepath.Join(os.TempDir(), fmt.Sprintf("robocopy_%d.log", time.Now().UnixNano()))
|
||||
// Print out the log file path so you can locate it manually.
|
||||
fmt.Printf("[INFO] Robocopy log file: %s\n", logFile)
|
||||
|
||||
// Build robocopy command.
|
||||
// /E: copy subdirectories (including empty ones)
|
||||
// /TEE: output to console as well as to the log file.
|
||||
// /LOG:<logFile>: write output to the log file.
|
||||
// We remove extra suppression flags so that robocopy prints as much as possible.
|
||||
cmd := exec.Command("robocopy", tempDir, installDir, "/E", "/TEE", fmt.Sprintf("/LOG:%s", logFile))
|
||||
|
||||
// Start robocopy.
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start robocopy: %w", err)
|
||||
}
|
||||
|
||||
// Set up a goroutine to tail the log file.
|
||||
doneTail := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go tailLogFile(logFile, doneTail, &wg)
|
||||
|
||||
// Wait for robocopy to complete.
|
||||
err := cmd.Wait()
|
||||
// Signal the tail goroutine to stop.
|
||||
close(doneTail)
|
||||
wg.Wait()
|
||||
|
||||
// Robocopy returns exit codes less than 8 as success.
|
||||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
if exitCode := exitErr.ExitCode(); exitCode >= 8 {
|
||||
return fmt.Errorf("robocopy failed: exit status %d", exitCode)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("robocopy failed: %w", err)
|
||||
}
|
||||
}
|
||||
// Mark progress as complete.
|
||||
UpdateProgress(100, "Copying files to install directory")
|
||||
|
||||
// (Optional) If you want the log file to be removed automatically, uncomment the next line.
|
||||
// os.Remove(logFile)
|
||||
} else {
|
||||
// Non-Windows fallback: copy files one-by-one.
|
||||
var totalFiles, copiedFiles int
|
||||
err := filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
totalFiles++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(tempDir, path)
|
||||
err = filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(tempDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(installDir, relPath)
|
||||
if info.IsDir() {
|
||||
if err := os.MkdirAll(targetPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := copyFile(path, targetPath); err != nil {
|
||||
return err
|
||||
}
|
||||
copiedFiles++
|
||||
UpdateProgress(int(float64(copiedFiles)/float64(totalFiles)*100), "Copying files to install directory")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(installDir, relPath)
|
||||
if info.IsDir() {
|
||||
// Create directories in the install directory
|
||||
if err := os.MkdirAll(targetPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Copy files to the install directory
|
||||
if err := copyFile(path, targetPath); err != nil {
|
||||
return err
|
||||
}
|
||||
copiedFiles++
|
||||
UpdateProgress(int(float64(copiedFiles)/float64(totalFiles)*100), "Copying files to install directory")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Clean up temporary directory
|
||||
// Clean up temporary directory.
|
||||
UpdateProgress(100, "Cleaning up temporary files")
|
||||
return os.RemoveAll(tempDir)
|
||||
}
|
||||
|
@ -189,7 +289,7 @@ func copyFile(src, dst string) error {
|
|||
|
||||
// FinalizeInstall finalizes the installation by updating installed.ini.
|
||||
func FinalizeInstall(packageName, release, version, arch, osName string) error {
|
||||
installDir, err := GetDefaultInstallDir()
|
||||
installDir, err := GetInstallDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue