From 25f1f56bdebfeecc66f7b5df3a87e5962f303e26 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Mon, 20 Jul 2026 17:35:21 +0100 Subject: [PATCH] appview/pages: add spindle selector in repo/fork pages both pages now have an "advanced" section for users to configure spindles, the default selection is "No Spindle" Signed-off-by: oppiliappan --- appview/pages/pages.go | 4 +- appview/pages/templates/repo/fork.html | 58 +++++++++++++++++++++- appview/pages/templates/repo/new.html | 67 +++++++++++++++++++++++++- appview/repo/repo.go | 22 +++++++++ appview/state/state.go | 24 +++++++++ 5 files changed, 171 insertions(+), 4 deletions(-) diff --git a/appview/pages/pages.go b/appview/pages/pages.go index 25391ec6..c861df6a 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -704,7 +704,8 @@ func (p *Pages) SpindleDashboard(w io.Writer, params SpindleDashboardParams) err type NewRepoParams struct { BaseParams - Knots []string + Knots []string + Spindles []string } func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { @@ -714,6 +715,7 @@ func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { type ForkRepoParams struct { BaseParams Knots []string + Spindles []string RepoInfo repoinfo.RepoInfo } diff --git a/appview/pages/templates/repo/fork.html b/appview/pages/templates/repo/fork.html index 0f8ccecd..4cb7b286 100644 --- a/appview/pages/templates/repo/fork.html +++ b/appview/pages/templates/repo/fork.html @@ -124,7 +124,63 @@ -
+ {{/* Step 3 — Advanced */}} +
+ +
+
+ {{ i "plus" "size-4" }} +
+
+
+

Advanced

+
+
+ +
+
+
+ + {{/* Spindle list */}} +
+ +
+
+ + +
+ {{ range .Spindles }} +
+ + +
+ {{ end }} +
+

+ A spindle runs your CI workflows. + You can also register your own spindle. +

+
+ +
+
+
+
+ +
-
+

Configuration

Repository settings and hosting.
@@ -75,6 +76,30 @@
{{ end }} +{{ define "step-3" }} +
+ +
+
+ {{ i "plus" "w-4 h-4" }} +
+
+ +
+

Advanced

+
+
+ +
+
+
+ {{ template "spindle" . }} +
+
+
+
+{{ end }} + {{ define "name" }}
@@ -171,8 +196,46 @@
{{ end }} +{{ define "spindle" }} + +
+ +
+
+ + +
+ {{ range .Spindles }} +
+ + +
+ {{ end }} +
+

+ A spindle runs your CI workflows. + You can also register your own spindle. +

+
+{{ end }} + {{ define "numberCircle" }} -
+
{{.}}
{{ end }} diff --git a/appview/repo/repo.go b/appview/repo/repo.go index a989858b..414835ec 100644 --- a/appview/repo/repo.go +++ b/appview/repo/repo.go @@ -1418,9 +1418,15 @@ func (rp *Repo) ForkRepo(w http.ResponseWriter, r *http.Request) { user := rp.oauth.GetMultiAccountUser(r) knots := rp.acl.KnotsForUser(r.Context(), user.Did) + spindles, err := rp.enforcer.GetSpindlesForUser(user.Did) + if err != nil { + l.Error("failed to fetch spindles", "err", err) + } + rp.pages.ForkRepo(w, pages.ForkRepoParams{ BaseParams: pages.BaseParamsFromContext(r.Context()), Knots: knots, + Spindles: spindles, RepoInfo: rp.repoResolver.GetRepoInfo(r, user), }) @@ -1439,6 +1445,21 @@ func (rp *Repo) ForkRepo(w http.ResponseWriter, r *http.Request) { return } + // optional spindle selection; validate the user is a member if provided + spindle := r.FormValue("spindle") + if spindle != "" { + validSpindles, err := rp.enforcer.GetSpindlesForUser(user.Did) + if err != nil { + l.Error("failed to fetch spindles", "err", err) + rp.pages.Notice(w, "repo", "Failed to configure spindle. Try again later.") + return + } + if !slices.Contains(validSpindles, spindle) { + rp.pages.Notice(w, "repo", "Invalid spindle selection.") + return + } + } + // choose a name for a fork forkName := strings.ToLower(r.FormValue("repo_name")) if forkName == "" { @@ -1530,6 +1551,7 @@ func (rp *Repo) ForkRepo(w http.ResponseWriter, r *http.Request) { Rkey: rkey, Source: forkSource, Description: forkDescription, + Spindle: spindle, Created: time.Now(), Labels: rp.config.Label.DefaultLabelDefs, RepoDid: repoDid, diff --git a/appview/state/state.go b/appview/state/state.go index 73b662d4..80aadc13 100644 --- a/appview/state/state.go +++ b/appview/state/state.go @@ -7,6 +7,7 @@ import ( "fmt" "log/slog" "net/http" + "slices" "strings" "time" @@ -449,9 +450,15 @@ func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { user := s.oauth.GetMultiAccountUser(r) knots := s.aclService.KnotsForUser(r.Context(), user.Did) + spindles, err := s.enforcer.GetSpindlesForUser(user.Did) + if err != nil { + s.logger.Error("failed to fetch spindles", "err", err) + } + s.pages.NewRepo(w, pages.NewRepoParams{ BaseParams: pages.BaseParamsFromContext(r.Context()), Knots: knots, + Spindles: spindles, }) case http.MethodPost: @@ -494,6 +501,22 @@ func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { return } + // optional spindle selection; validate the user is a member if provided + spindle := r.FormValue("spindle") + if spindle != "" { + validSpindles, err := s.enforcer.GetSpindlesForUser(user.Did) + if err != nil { + l.Error("failed to fetch spindles", "err", err) + s.pages.Notice(w, "repo", "Failed to configure spindle. Try again later.") + return + } + if !slices.Contains(validSpindles, spindle) { + s.pages.Notice(w, "repo", "Invalid spindle selection.") + return + } + } + l = l.With("spindle", spindle) + // ACL validation if !s.aclService.IsRepoCreateAllowed(r.Context(), domain, user.Did) { l.Info("unauthorized") @@ -570,6 +593,7 @@ func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { Knot: domain, Rkey: rkey, Description: description, + Spindle: spindle, Created: time.Now(), Labels: s.config.Label.DefaultLabelDefs, RepoDid: repoDid, -- 2.51.2