chore: work

Signed-off-by: jolheiser <john.olheiser@gmail.com>
main
jolheiser 2023-01-12 23:07:57 -06:00
parent 9a2a4201fc
commit fe91dc2dd1
Signed by: jolheiser
GPG Key ID: B853ADA5DA7BBF7A
4 changed files with 235 additions and 0 deletions

View File

@ -1,5 +1,11 @@
package forge
import (
"fmt"
"net/url"
"strings"
)
type Forge string
const (
@ -18,5 +24,17 @@ type Asset struct {
}
type Forger interface {
Name() string
Latest() (Release, error)
}
func splitURI(uri string) (string, []string, error) {
u, err := url.ParseRequestURI(uri)
if err != nil {
return "", nil, fmt.Errorf("could not parse URI: %w", err)
}
u.Scheme = "https"
paths := strings.FieldsFunc(u.Path, func(r rune) bool { return r == '/' })
u.Path = ""
return u.String(), paths, nil
}

View File

@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
)
var _ Forger = (*Gitea)(nil)
@ -23,6 +25,23 @@ type GiteaRelease struct {
} `json:"assets"`
}
func NewGitea(uri string) (Gitea, error) {
var g Gitea
base, paths, err := splitURI(uri)
if err != nil {
return g, err
}
g.BaseURL = base
g.Owner = paths[0]
g.Repo = paths[1]
return g, nil
}
func (g Gitea) Name() string {
return g.Repo
}
func (g Gitea) Latest() (Release, error) {
var release Release

83
meta/meta.go 100644
View File

@ -0,0 +1,83 @@
package meta
import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"github.com/adrg/xdg"
)
type Meta struct {
Packages []Package `json:"packages"`
}
type Package struct {
Name string `json:"name"`
Version string `json:"version"`
}
func Add(pkg Package) error {
m, err := Read()
if err != nil {
return err
}
for _, p := range m.Packages {
if strings.EqualFold(pkg.Name, p.Name) {
return errors.New("package already exists locally")
}
}
m.Packages = append(m.Packages, pkg)
return save(m)
}
func Read() (Meta, error) {
var m Meta
fp, err := metaPath()
if err != nil {
return m, fmt.Errorf("could not get meta file: %w", err)
}
fi, err := os.Open(fp)
if err != nil {
return m, fmt.Errorf("could not open meta file: %w", err)
}
defer fi.Close()
if err := json.NewDecoder(fi).Decode(&m); err != nil {
return m, fmt.Errorf("could not decode meta: %w", err)
}
return m, nil
}
func Remove(name string) error {
m, err := Read()
if err != nil {
return err
}
for idx, p := range m.Packages {
if strings.EqualFold(name, p.Name) {
m.Packages = append(m.Packages[:idx], m.Packages[idx+1:]...)
return save(m)
}
}
return fmt.Errorf("could not find package to remove for %q", name)
}
func metaPath() (string, error) {
return xdg.DataFile("eget/meta.json")
}
func save(m Meta) error {
fp, err := metaPath()
if err != nil {
return fmt.Errorf("could not get meta path: %w", err)
}
fi, err := os.Create(fp)
if err != nil {
return fmt.Errorf("could not create meta file: %w", err)
}
defer fi.Close()
return json.NewEncoder(fi).Encode(m)
}

115
web/web.go 100644
View File

@ -0,0 +1,115 @@
package web
import (
"errors"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"regexp"
"runtime"
"github.com/adrg/xdg"
"github.com/mholt/archiver/v3"
"go.jolheiser.com/eget/forge"
)
var (
amd64Re = regexp.MustCompile(`amd64|x86_64|64-bit`)
linuxRe = regexp.MustCompile(`linux`)
windowsRe = regexp.MustCompile(`windows`)
)
func Install(f forge.Forger) error {
var re *regexp.Regexp
switch runtime.GOOS {
case "linux":
re = linuxRe
case "windows":
re = windowsRe
default:
return fmt.Errorf("%q is not a supported OS", runtime.GOOS)
}
latest, err := f.Latest()
if err != nil {
return err
}
var asset forge.Asset
for _, a := range latest.Assets {
if amd64Re.MatchString(a.DownloadURL) && re.MatchString(a.DownloadURL) {
asset = a
}
}
if asset.Name == "" {
return errors.New("no release found for this OS")
}
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, f.Name()); err != nil {
return fmt.Errorf("could not unpack download: %w", err)
}
return nil
}
func detectRootDir(path string) bool {
infos, err := os.ReadDir(path)
if err != nil {
return false
}
return len(infos) != 1
}
func unpack(src, name string) error {
dest := filepath.Join(xdg.DataHome, name)
uaIface, err := archiver.ByExtension(src)
if err != nil {
return err
}
u, ok := uaIface.(archiver.Unarchiver)
if !ok {
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)
}
var binExt string
if runtime.GOOS == "windows" {
binExt = ".exe"
}
dest = filepath.Join(dest, name+binExt)
c := archiver.FileCompressor{Decompressor: d}
if err := c.DecompressFile(src, dest); err != nil {
return err
}
return os.Chmod(dest, 0o755)
}
return u.Unarchive(src, dest)
}