123 lines
2.7 KiB
Go
123 lines
2.7 KiB
Go
|
package disk
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"io/fs"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"regexp"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
|
||
|
"go.jolheiser.com/eget/forge"
|
||
|
|
||
|
"github.com/adrg/xdg"
|
||
|
"github.com/mholt/archiver/v3"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
amd64Re = regexp.MustCompile(`amd64|x86_64|64`)
|
||
|
linuxRe = regexp.MustCompile(`linux|linux64`)
|
||
|
windowsRe = regexp.MustCompile(`windows|win64`)
|
||
|
)
|
||
|
|
||
|
func Install(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, asset.Name); err != nil {
|
||
|
return fmt.Errorf("could not unpack download: %w", err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func unpack(src, name string) error {
|
||
|
dest := filepath.Join(xdg.DataHome, "eget", 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 {
|
||
|
if filepath.Ext(src) == binExt {
|
||
|
return os.Rename(src, filepath.Join(dest, name+binExt))
|
||
|
}
|
||
|
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)
|
||
|
})
|
||
|
}
|