diff --git a/handler/handler.go b/handler/handler.go index 76c0db5..1fa1024 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -31,16 +31,18 @@ var ( ) type Query struct { - User, Program, AsProgram, Release string - MoveToPath, Search, Insecure bool - SudoMove bool // deprecated: not used, now automatically detected + User, Program, Release string + AsProgram, Select string + MoveToPath, Search, Insecure bool + SudoMove bool // deprecated: not used, now automatically detected } type Result struct { Query - Timestamp time.Time - Assets Assets - M1Asset bool + ResolvedResolve string + Timestamp time.Time + Assets Assets + M1Asset bool } func (q Query) cacheKey() string { @@ -90,6 +92,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.Error(w, cleaned, http.StatusInternalServerError) } switch qtype { + case "json": + w.Header().Set("Content-Type", "application/json") + ext = "json" + script = "" case "script": w.Header().Set("Content-Type", "text/x-shellscript") ext = "sh" @@ -112,6 +118,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { Release: "", Insecure: r.URL.Query().Get("insecure") == "1", AsProgram: r.URL.Query().Get("as"), + Select: r.URL.Query().Get("select"), } // set query from route path := strings.TrimPrefix(r.URL.Path, "/") @@ -120,6 +127,9 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { q.MoveToPath = true path = strings.TrimRight(path, "!") } + if r.URL.Query().Get("move") == "1" { + q.MoveToPath = true // also allow move=1 if bang in urls cause issues + } var rest string q.User, rest = splitHalf(path, "/") q.Program, q.Release = splitHalf(rest, "@") @@ -160,6 +170,12 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { showError(err.Error(), http.StatusBadGateway) return } + // no render script? just output as json + if script == "" { + b, _ := json.MarshalIndent(result, "", " ") + w.Write(b) + return + } // load template t, err := template.New("installer").Parse(script) if err != nil { @@ -172,7 +188,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { showError("Template error: "+err.Error(), http.StatusInternalServerError) return } - log.Printf("serving script %s/%s@%s (%s)", q.User, q.Program, q.Release, ext) + log.Printf("serving script %s/%s@%s (%s)", result.User, result.Program, result.Release, ext) // ready w.Write(buff.Bytes()) } @@ -228,10 +244,8 @@ func (h *Handler) get(url string, v interface{}) error { b, _ := io.ReadAll(resp.Body) return errors.New(http.StatusText(resp.StatusCode) + " " + string(b)) } - if err := json.NewDecoder(resp.Body).Decode(v); err != nil { return fmt.Errorf("download failed: %s: %s", url, err) } - return nil } diff --git a/handler/handler_execute.go b/handler/handler_execute.go index f04292c..f10cafd 100644 --- a/handler/handler_execute.go +++ b/handler/handler_execute.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "net/http" + "sort" "strings" "time" ) @@ -55,10 +56,11 @@ func (h *Handler) execute(q Query) (Result, error) { q.Release = release } result := Result{ - Timestamp: ts, - Query: q, - Assets: assets, - M1Asset: assets.HasM1(), + Timestamp: ts, + Query: q, + ResolvedResolve: release, + Assets: assets, + M1Asset: assets.HasM1(), } //success store results h.cacheMut.Lock() @@ -110,8 +112,7 @@ func (h *Handler) getAssetsNoCache(q Query) (string, Assets, error) { if l := len(sumIndex); l > 0 { log.Printf("fetched %d asset shasums", l) } - assets := Assets{} - index := map[string]bool{} + index := map[string]Asset{} for _, ga := range ghas { url := ga.BrowserDownloadURL //only binary containers are supported @@ -142,7 +143,11 @@ func (h *Handler) getAssetsNoCache(q Query) (string, Assets, error) { log.Printf("fetched asset has unknown os: %s", ga.Name) continue } - log.Printf("fetched asset: %s", ga.Name) + // user selecting a particular asset? + if q.Select != "" && !strings.Contains(ga.Name, q.Select) { + log.Printf("select excludes asset: %s", ga.Name) + continue + } asset := Asset{ OS: os, Arch: arch, @@ -152,16 +157,30 @@ func (h *Handler) getAssetsNoCache(q Query) (string, Assets, error) { SHA256: sumIndex[ga.Name], } //there can only be 1 file for each OS/Arch - if index[asset.Key()] { - continue + key := asset.Key() + other, exists := index[key] + if exists { + gnu := func(s string) bool { return strings.Contains(s, "gnu") } + musl := func(s string) bool { return strings.Contains(s, "musl") } + g2m := gnu(other.Name) && !musl(other.Name) && !gnu(asset.Name) && musl(asset.Name) + // prefer musl over glib for portability, override with select=gnu + if !g2m { + continue + } } - index[asset.Key()] = true - //include! - assets = append(assets, asset) + index[key] = asset } - if len(assets) == 0 { + if len(index) == 0 { return release, nil, errors.New("no downloads found for this release") } + assets := Assets{} + for _, a := range index { + log.Printf("including asset: %s (%s)", a.Name, a.Key()) + assets = append(assets, a) + } + sort.Slice(assets, func(i, j int) bool { + return assets[i].Key() < assets[j].Key() + }) return release, assets, nil } diff --git a/handler/strings.go b/handler/strings.go index cfd881c..7bf0ca8 100644 --- a/handler/strings.go +++ b/handler/strings.go @@ -6,7 +6,7 @@ import ( ) var ( - archRe = regexp.MustCompile(`(arm64|arm|386|amd64|x86_64|aarch64|32|64)`) + archRe = regexp.MustCompile(`(arm64|arm|386|686|amd64|x86_64|aarch64|32|64)`) fileExtRe = regexp.MustCompile(`(\.tar)?(\.[a-z][a-z0-9]+)$`) posixOSRe = regexp.MustCompile(`(darwin|linux|(net|free|open)bsd|mac|osx|windows|win)`) checksumRe = regexp.MustCompile(`(checksums|sha256sums)`) @@ -30,7 +30,7 @@ func getArch(s string) string { //arch modifications if a == "64" || a == "x86_64" || a == "" { a = "amd64" //default - } else if a == "32" { + } else if a == "32" || a == "686" { a = "386" } else if a == "aarch64" { a = "arm64" diff --git a/scripts/install.sh.tmpl b/scripts/install.sh.tmpl index 5a7101d..980eb39 100644 --- a/scripts/install.sh.tmpl +++ b/scripts/install.sh.tmpl @@ -19,7 +19,7 @@ function install { PROG="{{ .Program }}" ASPROG="{{ .AsProgram }}" MOVE="{{ .MoveToPath }}" - RELEASE="{{ .Release }}" + RELEASE="{{ .Release }}" # {{ .ResolvedResolve }} INSECURE="{{ .Insecure }}" OUT_DIR="{{ if .MoveToPath }}/usr/local/bin{{ else }}$(pwd){{ end }}" GH="https://github.com" diff --git a/scripts/install.txt.tmpl b/scripts/install.txt.tmpl index bb601f5..71243a3 100644 --- a/scripts/install.txt.tmpl +++ b/scripts/install.txt.tmpl @@ -2,10 +2,11 @@ repository: https://github.com/{{ .User }}/{{ .Program }} user: {{ .User }} program: {{ .Program }}{{if .AsProgram }} as: {{ .AsProgram }}{{end}} -release: {{ .Release }} +release: {{ .ResolvedResolve }} move-into-path: {{ .MoveToPath }} sudo-move: {{ .SudoMove }} used-search: {{ .Search }} +asset-select: {{ .Select }} release assets: {{ range .Assets }} {{ .Key }}