diff --git a/server_apps.go b/server_apps.go index 3cbe71a3fd11159a5427aada1b0154e8c9aa2b03..ddb53acefd7244f6ccf41a16713a40d52e637ea1 100644 --- a/server_apps.go +++ b/server_apps.go @@ -8,11 +8,13 @@ import ( "log" "net/http" "regexp" + "strings" "time" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/storage/memory" "github.com/pelletier/go-toml/v2" ) @@ -28,6 +30,7 @@ type appInfo struct { author string description string version int + commit string } var ( @@ -35,44 +38,7 @@ var ( reAppRepo = regexp.MustCompile(`^([a-zA-Z\-_\.0-9]+)/([a-zA-Z\-_0-9]+)$`) ) -func (s *server) getAppInfo(ctx context.Context, repo string) (*appInfo, error) { - url := "https://git.flow3r.garden/" + repo - g, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ - URL: url, - }) - if err != nil { - return nil, fmt.Errorf("when cloning: %w", err) - } - - cfg, err := g.Config() - if err != nil { - return nil, fmt.Errorf("when getting config: %w", err) - } - if len(cfg.Branches) < 1 { - return nil, fmt.Errorf("no branches") - } - var bname string - for name, _ := range cfg.Branches { - bname = name - break - } - bname = "main" - h, err := g.ResolveRevision(plumbing.Revision(bname)) - if err != nil { - return nil, fmt.Errorf("resolving revision failed: %w", err) - } - err = g.Fetch(&git.FetchOptions{ - RefSpecs: []config.RefSpec{ - config.RefSpec(fmt.Sprintf("%s:refs/heads/%s", bname, bname)), - }, - }) - if err != nil && err != git.NoErrAlreadyUpToDate { - return nil, fmt.Errorf("fetch failed: %w", err) - } - obj, err := g.CommitObject(*h) - if err != nil { - return nil, fmt.Errorf("CommitObject(%v): %w", h, err) - } +func (s *server) parseAppToml(ctx context.Context, obj *object.Commit) (*appInfo, error) { f, err := obj.File("flow3r.toml") if err != nil { return nil, fmt.Errorf("toml open failed: %w", err) @@ -129,9 +95,76 @@ func (s *server) getAppInfo(ctx context.Context, repo string) (*appInfo, error) menu: data.App.Menu, description: data.Metadata.Description, version: data.Metadata.Version, + commit: obj.Hash.String(), }, nil } +func (s *server) getAppInfo(ctx context.Context, repo string) (*appInfo, error) { + url := "https://git.flow3r.garden/" + repo + g, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ + URL: url, + }) + if err != nil { + return nil, fmt.Errorf("when cloning: %w", err) + } + + cfg, err := g.Config() + if err != nil { + return nil, fmt.Errorf("when getting config: %w", err) + } + if len(cfg.Branches) < 1 { + return nil, fmt.Errorf("no branches") + } + var bname string + for name, _ := range cfg.Branches { + bname = name + break + } + h, err := g.ResolveRevision(plumbing.Revision(bname)) + if err != nil { + return nil, fmt.Errorf("resolving revision failed: %w", err) + } + err = g.Fetch(&git.FetchOptions{ + RefSpecs: []config.RefSpec{ + config.RefSpec(fmt.Sprintf("%s:refs/heads/%s", bname, bname)), + }, + }) + if err != nil && err != git.NoErrAlreadyUpToDate { + return nil, fmt.Errorf("fetch failed: %w", err) + } + obj, err := g.CommitObject(*h) + if err != nil { + return nil, fmt.Errorf("CommitObject(%v): %w", h, err) + } + + highestVer := 0 + highsetVerNil := true + firstTime := make(map[int]*appInfo) + for { + info, err := s.parseAppToml(ctx, obj) + if err == nil { + ver := info.version + firstTime[ver] = info + if ver > highestVer || highsetVerNil { + highestVer = ver + highsetVerNil = false + } + } + if len(obj.ParentHashes) == 0 { + break + } + obj, err = g.CommitObject(obj.ParentHashes[0]) + if err != nil { + return nil, fmt.Errorf("CommitObject(%v): %w", h, err) + } + } + + if highsetVerNil { + return nil, fmt.Errorf("no version") + } + return firstTime[highestVer], nil +} + func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { s.gitMu.Lock() defer s.gitMu.Unlock() @@ -218,7 +251,6 @@ func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { log.Printf("App: %q: okay", app.repository) } app.appInfo = info - log.Printf("%+v", app.appInfo) } log.Printf("Got %d apps", len(res)) @@ -233,7 +265,9 @@ func (s *server) handleApps(w http.ResponseWriter, r *http.Request) { } type app struct { - RepoURL string `json:"repoURL"` + RepoURL string `json:"repoUrl"` + Commit string `json:"commit"` + DownloadURL string `json:"downloadURL"` Name string `json:"name"` Menu string `json:"menu"` Author string `json:"author"` @@ -253,8 +287,14 @@ func (s *server) handleApps(w http.ResponseWriter, r *http.Request) { if a.appInfo == nil { continue } + // Guarenteed to be exactly 2 parts because of the regex + parts := strings.Split(a.repository, "/") + orga := parts[0] + repo := parts[1] resp.Apps = append(resp.Apps, app{ RepoURL: "https://git.flow3r.garden/" + a.repository, + Commit: a.appInfo.commit, + DownloadURL: fmt.Sprintf("https://git.flow3r.garden/%s/%s/-/archive/%s/%s-%s.zip", orga, repo, a.appInfo.commit, repo, a.appInfo.commit), Name: a.appInfo.name, Menu: a.appInfo.menu, Author: a.appInfo.author,