Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • main
1 result

Target

Select target project
  • dos/api
  • marvino/api
  • psentee/api
  • flow3r/api
4 results
Select Git revision
Show changes
Commits on Source (24)
......@@ -16,7 +16,9 @@ import (
"path"
"regexp"
"strings"
"strconv"
"time"
"math"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
......@@ -26,6 +28,24 @@ import (
"github.com/pelletier/go-toml/v2"
)
type appStyle struct {
Background string `json:"background",omitempty`
Color string `json:"color",omitempty`
}
type appPatch struct{
version int
repository string
zip []byte
targz []byte
}
type appStatus struct{
tested_version int
broken bool
patch *appPatch
}
type appRegistry struct {
shame []*appShame
apps []*appDescriptor
......@@ -48,6 +68,7 @@ type appInfo struct {
author string
description string
version int
slug string
commit string
stars int
flow3rSeed string
......@@ -57,6 +78,9 @@ type appInfo struct {
targz []byte
firstErr error
featured bool
status *appStatus
style *appStyle
}
type GLProject struct {
......@@ -94,7 +118,7 @@ func (s *server) getStars(ctx context.Context, repo string) (int, error) {
return project.StarCount, nil
}
func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, obj *object.Commit) ([]byte, error) {
func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, patch_source *string, obj *object.Commit) ([]byte, error) {
fi, err := obj.Files()
if err != nil {
return nil, fmt.Errorf("listing files: %w", err)
......@@ -133,6 +157,9 @@ func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, obj
if err != nil {
return nil, fmt.Errorf("Blob.Reader: %w", err)
}
if f.Name == "flow3r.toml" && patch_source != nil{
fo.Write([]byte(fmt.Sprintf("patch_source = \"%s\"\n\n", *patch_source)))
}
_, err = io.Copy(fo, rdr)
rdr.Close()
if err != nil {
......@@ -145,7 +172,7 @@ func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, obj
return buf.Bytes(), nil
}
func (s *server) targzApp(ctx context.Context, name, pathInRepo, repo string, obj *object.Commit) ([]byte, error) {
func (s *server) targzApp(ctx context.Context, name, pathInRepo, repo string, patch_source * string, obj *object.Commit) ([]byte, error) {
fi, err := obj.Files()
if err != nil {
return nil, fmt.Errorf("listing files: %w", err)
......@@ -209,10 +236,14 @@ func (s *server) targzApp(ctx context.Context, name, pathInRepo, repo string, ob
}
}
}
patch_bytes := []byte("")
if f.Name == "flow3r.toml" && patch_source != nil{
patch_bytes = []byte(fmt.Sprintf("patch_source = \"%s\"\n\n", *patch_source))
}
err = t.WriteHeader(&tar.Header{
Name: outPath,
Typeflag: tar.TypeReg,
Size: f.Size,
Size: f.Size + int64(len(patch_bytes)),
Mode: 0644,
})
if err != nil {
......@@ -225,6 +256,12 @@ func (s *server) targzApp(ctx context.Context, name, pathInRepo, repo string, ob
if err != nil {
return nil, fmt.Errorf("Blob.Reader: %w", err)
}
if len(patch_bytes) != 0{
t.Write(patch_bytes)
}
if err != nil {
return nil, fmt.Errorf("patch_source: %w", err)
}
_, err = io.Copy(t, rdr)
rdr.Close()
if err != nil {
......@@ -254,6 +291,7 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec
var data struct {
App struct {
Name string
Category string
Menu string
}
Entry struct {
......@@ -266,6 +304,7 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec
Description string
Version int
}
Style map[string]interface{}
}
dec := toml.NewDecoder(reader)
err = dec.Decode(&data)
......@@ -275,13 +314,19 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec
if data.App.Name == "" || len(data.App.Name) > 32 {
return nil, fmt.Errorf("app name invalid (must be non-empty and <= 32 chars)")
}
if data.App.Category == "" {
data.App.Category = data.App.Menu
}
sections := map[string]bool{
"Badge": true,
"Apps": true,
"Music": true,
"Media": true,
"Games": true,
"Demos": true,
}
if !sections[data.App.Menu] {
return nil, fmt.Errorf("app menu invalid (must be one of 'Badge', 'Apps', 'Music')")
if !sections[data.App.Category] {
return nil, fmt.Errorf("app category invalid (must be one of 'Badge', 'Apps', 'Music', 'Media', 'Games', 'Demos')")
}
if data.Entry.Class == "" {
return nil, fmt.Errorf("no entry class")
......@@ -293,18 +338,34 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec
return nil, fmt.Errorf("metadata description too long (must be <= 140 chars)")
}
return &appInfo{
ret := appInfo{
name: data.App.Name,
author: data.Metadata.Author,
menu: data.App.Menu,
menu: data.App.Category,
description: data.Metadata.Description,
version: data.Metadata.Version,
commit: obj.Hash.String(),
commitObj: obj,
}, nil
style: nil,
}
style := appStyle{}
style_exists := false
if bg_col, ok := data.Style["background"].(string); ok {
style.Background = bg_col
style_exists = true
}
if col, ok := data.Style["color"].(string); ok {
style.Color = col
style_exists = true
}
if style_exists {
ret.style = &style
}
return &ret, nil
}
func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appInfo, error) {
func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string, slug *string) (*appInfo, error) {
url := "https://git.flow3r.garden/" + repo
g, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
URL: url,
......@@ -382,19 +443,28 @@ func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appI
if err != nil {
return nil, fmt.Errorf("getting gitlab stars failed: %w", err)
}
var patch_source *string = nil
slug_str := repo
if slug != nil{
slug_str = *slug
psrc := fmt.Sprintf("https://git.flow3r.garden/%s", repo)
patch_source = &psrc
}
app := firstTime[highestVer]
app.stars = stars
zbytes, err := s.zipApp(ctx, app.name, pathInRepo, repo, app.commitObj)
zbytes, err := s.zipApp(ctx, app.name, pathInRepo, slug_str, patch_source, app.commitObj)
if err != nil {
return nil, fmt.Errorf("zipping failed: %w", err)
}
app.zip = zbytes
tbytes, err := s.targzApp(ctx, app.name, pathInRepo, repo, app.commitObj)
tbytes, err := s.targzApp(ctx, app.name, pathInRepo, slug_str, patch_source, app.commitObj)
if err != nil {
return nil, fmt.Errorf("targzing failed: %w", err)
}
app.targz = tbytes
app.slug = strings.ReplaceAll(slug_str, "/", "-")
// Calculate an 8 digit flow3r seed which can be used to install the
// app. This is based on md5 so ambitious hack3rs can force a certain
// app seed :)
......@@ -405,10 +475,30 @@ func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appI
}
app.firstErr = firstErr
return app, nil
}
func getFlags(obj *object.Commit) (*map[string]interface{}, error){
flags, err := obj.File("tags.json")
if err != nil {
return nil, err
}
flagreader, err := flags.Reader()
if err != nil {
return nil, err
}
flagbytes, err := io.ReadAll(flagreader)
if err != nil {
return nil, err
}
var flagmap map[string]interface{}
err = json.Unmarshal([]byte(flagbytes), &flagmap)
if err != nil {
return nil, err
}
return &flagmap, nil
}
func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
s.gitMu.Lock()
defer s.gitMu.Unlock()
......@@ -439,11 +529,17 @@ func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
if err != nil {
return nil, err
}
iter, err := obj.Files()
if err != nil {
return nil, err
}
flagmap, err := getFlags(obj)
if err != nil {
log.Printf("flags.json: couldn't read: %v", err)
}
var registry appRegistry
for {
f, err := iter.Next()
......@@ -501,8 +597,10 @@ func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
}
for _, app := range registry.apps {
info, err := s.getAppInfo(ctx, app.pathInRepo, app.repository)
printed := false
info, err := s.getAppInfo(ctx, app.pathInRepo, app.repository, nil)
if err != nil {
printed = true
log.Printf("App %q: %v", app.repository, err)
registry.shame = append(registry.shame, &appShame{
repository: app.repository,
......@@ -510,12 +608,44 @@ func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
})
continue
} else if info.firstErr != nil {
printed = true
log.Printf("App: %q: was okay, latest has error: %v", app.repository, info.firstErr)
registry.shame = append(registry.shame, &appShame{
repository: app.repository,
errorMsg: fmt.Sprintf("%v", info.firstErr),
})
} else if flagmap != nil {
if app_flags, ok := (*flagmap)[app.repository].(map[string]interface{}); ok{
if feat, ok := app_flags["featured"].(bool); ok {
info.featured = feat
}
if status, ok := app_flags["status"].(map[string]interface{}); ok {
info.status = new(appStatus)
if tested, ok := status["tested_version"].(float64); ok {
info.status.tested_version = int(math.Round(tested))
}
if broken, ok := status["broken"].(bool); ok {
info.status.broken = broken
}
if patch, ok := status["patch"].(string); ok {
printed = true
patch_info, err := s.getAppInfo(ctx, "", patch, &app.repository)
if err != nil {
log.Printf("App %q: couldn't get patch at %q: %v", app.repository, patch, err)
} else {
log.Printf("App: %q: okay, patch found", app.repository)
info.status.patch = &appPatch{
repository: patch,
zip: patch_info.zip,
targz: patch_info.targz,
version: patch_info.version,
}
}
}
}
}
}
if !printed{
log.Printf("App: %q: okay", app.repository)
}
app.appInfo = info
......@@ -526,6 +656,19 @@ func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
return &registry, nil
}
type jsonAppPatch struct{
Version int `json:"version"`
RepoURL string `json:"repoUrl"`
DownloadURL string `json:"downloadUrl"`
TarDownloadURL string `json:"tarDownloadUrl"`
}
type jsonAppStatus struct{
TestedVersion int `json:"tested_version"`
Broken bool `json:"broken"`
Patch *jsonAppPatch `json:"patch",omitempty`
}
type jsonApp struct {
RepoURL string `json:"repoUrl"`
Commit string `json:"commit"`
......@@ -537,11 +680,16 @@ type jsonApp struct {
Description string `json:"description"`
Version int `json:"version"`
Stars int `json:"stars"`
Slug string `json:"slug"`
Featured bool `json:"featured"`
Timestamp time.Time `json:"timestamp"`
Flow3rSeed string `json:"flow3rSeed"`
Status *jsonAppStatus `json:"status",omitempty`
Style *appStyle `json:"style",omitempty`
}
func makeJsonApp(a *appDescriptor) jsonApp {
return jsonApp{
ret := jsonApp{
RepoURL: "https://git.flow3r.garden/" + a.repository,
Commit: a.appInfo.commit,
DownloadURL: fmt.Sprintf("%sapps/zip/%s.zip", flagBaseURL, a.repository),
......@@ -551,10 +699,30 @@ func makeJsonApp(a *appDescriptor) jsonApp {
Author: a.appInfo.author,
Description: a.appInfo.description,
Version: a.appInfo.version,
Timestamp: a.appInfo.commitObj.Committer.When.UTC(),
Stars: a.appInfo.stars,
Flow3rSeed: a.appInfo.flow3rSeed,
Featured: a.appInfo.featured,
Slug: a.appInfo.slug,
Style: a.appInfo.style,
}
if a.appInfo.status != nil{
ret.Status = &jsonAppStatus{
Broken: a.appInfo.status.broken,
TestedVersion: a.appInfo.status.tested_version,
}
if a.appInfo.status.patch != nil{
pa := a.appInfo.status.patch.repository
ret.Status.Patch = &jsonAppPatch{
RepoURL: "https://git.flow3r.garden/" + pa,
DownloadURL: fmt.Sprintf("%sapps/zip/%s.zip", flagBaseURL, pa),
TarDownloadURL: fmt.Sprintf("%sapps/tar/%s.tar.gz", flagBaseURL, pa),
Version: a.appInfo.status.patch.version,
}
}
}
return ret
}
func (s *server) handleApps(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
......@@ -569,7 +737,7 @@ func (s *server) handleApps(w http.ResponseWriter, r *http.Request) {
}
resp := res{
DT: time.Now(),
DT: time.Now().UTC(),
}
for _, a := range apps {
if a.appInfo == nil {
......@@ -637,7 +805,7 @@ func (s *server) handleAppShame(w http.ResponseWriter, r *http.Request) {
}
resp := res{
DT: time.Now(),
DT: time.Now().UTC(),
}
for _, shame := range appShame {
// Guarenteed to be exactly 2 parts because of the regex
......@@ -673,12 +841,22 @@ func (s *server) handleAppZip(w http.ResponseWriter, r *http.Request) {
return
}
repository := fmt.Sprintf("%s/%s", orga, repo)
for _, app := range apps {
if app.repository == fmt.Sprintf("%s/%s", orga, repo) {
if app.repository == repository {
w.Header().Add("Content-Type", "application/zip")
w.Write(app.appInfo.zip)
return
}
if app.appInfo == nil || app.appInfo.status == nil || app.appInfo.status.patch == nil {
continue
}
if app.appInfo.status.patch.repository == repository {
w.Header().Add("Content-Type", "application/zip")
w.Write(app.appInfo.status.patch.zip)
return
}
}
http.NotFound(w, r)
......@@ -694,6 +872,7 @@ func (s *server) handleAppTargz(w http.ResponseWriter, r *http.Request) {
}
orga := matches[1]
repo := matches[2]
repository := fmt.Sprintf("%s/%s", orga, repo)
apps, err := s.getApps(ctx)
if err != nil {
......@@ -701,11 +880,21 @@ func (s *server) handleAppTargz(w http.ResponseWriter, r *http.Request) {
}
for _, app := range apps {
if app.repository == fmt.Sprintf("%s/%s", orga, repo) {
if app.repository == repository {
w.Header().Add("Content-Type", "application/x-gzip")
w.Header().Add("Content-Length", strconv.Itoa(len(app.appInfo.targz)))
w.Write(app.appInfo.targz)
return
}
if app.appInfo == nil || app.appInfo.status == nil || app.appInfo.status.patch == nil {
continue
}
if app.appInfo.status.patch.repository == repository {
w.Header().Add("Content-Type", "application/x-gzip")
w.Header().Add("Content-Length", strconv.Itoa(len(app.appInfo.status.patch.targz)))
w.Write(app.appInfo.status.patch.targz)
return
}
}
http.NotFound(w, r)
......
......@@ -10,6 +10,7 @@ import (
"net/http"
"regexp"
"strings"
"strconv"
)
func (s *server) cacheTarball(rel *GLRelease, data io.Reader) error {
......@@ -49,6 +50,7 @@ func (s *server) serveMirroredFile(w http.ResponseWriter, r *http.Request, rel *
b := s.cache[rel.TagName][artifact]
s.cacheMu.RUnlock()
w.Header().Add("Content-Type", "application/octet-stream")
w.Header().Add("Content-Length", strconv.Itoa(len(b)))
w.Write(b)
return
}
......@@ -103,6 +105,7 @@ func (s *server) serveMirroredFile(w http.ResponseWriter, r *http.Request, rel *
b := s.cache[rel.TagName][artifact]
s.cacheMu.RUnlock()
w.Header().Add("Content-Type", "application/octet-stream")
w.Header().Add("Content-Length", strconv.Itoa(len(b)))
w.Write(b)
return
}
......
......@@ -90,7 +90,39 @@ func (s *server) handleReleases(w http.ResponseWriter, r *http.Request) {
Tag: rel.TagName,
Partitions: partitions,
})
}
// append development release manually
resp = append(resp, release{
Name: "Development: Fresh and Buggy",
Tag: "dev",
Partitions: []partition{
{
Name: "bootloader",
URL: "https://git.flow3r.garden/api/v4/projects/226/jobs/artifacts/main/raw/recovery/build/bootloader/bootloader.bin?job=build_recovery",
Offset: "0x0",
},
{
Name: "partition-table",
URL: "https://git.flow3r.garden/api/v4/projects/226/jobs/artifacts/main/raw/recovery/build/partition_table/partition-table.bin?job=build_recovery",
Offset: "0x8000",
},
{
Name: "recovery",
URL: "https://git.flow3r.garden/api/v4/projects/226/jobs/artifacts/main/raw/recovery/build/flow3r-recovery.bin?job=build_recovery",
Offset: "0x10000",
},
{
Name: "flow3r",
URL: "https://git.flow3r.garden/api/v4/projects/226/jobs/artifacts/main/raw/build/flow3r.bin?job=build",
Offset: "0x90000",
},
},
})
w.Header().Add("Access-Control-Allow-Origin", "*")
w.Header().Add("Content-Type", "application/json")
j := json.NewEncoder(w)
......