128 lines
2.7 KiB
Go
128 lines
2.7 KiB
Go
package disk
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"go.jolheiser.com/eget/forge"
|
|
|
|
"github.com/mholt/archiver/v3"
|
|
)
|
|
|
|
func Install(name string, asset forge.Asset) error {
|
|
tmp, err := os.MkdirTemp(os.TempDir(), "eget-*")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.RemoveAll(tmp)
|
|
|
|
resp, err := http.Get(asset.DownloadURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
tmpDest := filepath.Join(tmp, asset.Name)
|
|
fi, err := os.Create(tmpDest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := io.Copy(fi, resp.Body); err != nil {
|
|
return err
|
|
}
|
|
if err := fi.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := unpack(tmpDest, name); err != nil {
|
|
return fmt.Errorf("could not unpack download: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func unpack(src, name string) error {
|
|
dest := Path(name)
|
|
if err := os.MkdirAll(dest, os.ModePerm); err != nil {
|
|
return fmt.Errorf("could not make all dest dirs: %w", err)
|
|
}
|
|
var binExt string
|
|
if runtime.GOOS == "windows" {
|
|
binExt = ".exe"
|
|
}
|
|
uaIface, err := archiver.ByExtension(src)
|
|
if err != nil {
|
|
ext := filepath.Ext(src)
|
|
if ext == binExt || len(ext) > 3 {
|
|
dest = filepath.Join(dest, name+binExt)
|
|
if err := os.Rename(src, dest); err != nil {
|
|
return err
|
|
}
|
|
return os.Chmod(dest, 0o755)
|
|
}
|
|
return err
|
|
}
|
|
u, ok := uaIface.(archiver.Unarchiver)
|
|
if !ok {
|
|
dest = filepath.Join(dest, name+binExt)
|
|
d, ok := uaIface.(archiver.Decompressor)
|
|
if !ok {
|
|
return fmt.Errorf("format specified by source filename is not an archive or compression format: %s (%T)", src, uaIface)
|
|
}
|
|
c := archiver.FileCompressor{Decompressor: d}
|
|
if err := c.DecompressFile(src, dest); err != nil {
|
|
return err
|
|
}
|
|
return os.Chmod(dest, 0o755)
|
|
}
|
|
|
|
tmpDest := filepath.Join(filepath.Dir(src), name)
|
|
if err := u.Unarchive(src, tmpDest); err != nil {
|
|
return fmt.Errorf("could not unarchive source: %w", err)
|
|
}
|
|
|
|
tmpSrc := tmpDest
|
|
infos, err := os.ReadDir(tmpSrc)
|
|
if err != nil {
|
|
return fmt.Errorf("could not read unarchived dir: %w", err)
|
|
}
|
|
if len(infos) == 1 && infos[0].IsDir() {
|
|
tmpSrc = filepath.Join(tmpDest, infos[0].Name())
|
|
}
|
|
|
|
return moveDir(tmpSrc, dest)
|
|
}
|
|
|
|
func moveDir(src, dest string) error {
|
|
return filepath.WalkDir(src, func(walkPath string, walkEntry fs.DirEntry, walkErr error) error {
|
|
if walkErr != nil {
|
|
return walkErr
|
|
}
|
|
if walkEntry.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
rel := strings.TrimPrefix(walkPath, src)
|
|
destPath := filepath.Join(dest, rel)
|
|
if err := os.MkdirAll(filepath.Dir(destPath), os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
return os.Rename(walkPath, destPath)
|
|
})
|
|
}
|
|
|
|
func Path(name string) string {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
home = "."
|
|
}
|
|
return filepath.Join(home, ".eget", name)
|
|
}
|