package disk import ( "fmt" "io" "io/fs" "net/http" "os" "path/filepath" "runtime" "strings" "go.jolheiser.com/eget/forge" "github.com/adrg/xdg" "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 { return filepath.Join(xdg.DataHome, "eget", name) }