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