- {{/* Spindle list */}}
+ {{/* Spindle */}}
-
-
+
+ {{ template "repo/fragments/spindleInput" (dict "Spindles" .Spindles "Current" "") }}
- A spindle runs your CI workflows.
- You can also register your own spindle.
+ Optional. A spindle runs your CI workflows; leave this empty for none.
+ Its operator has to have allowed you as a member, otherwise it will
+ ignore this repository. You can also
+ run your own spindle.
diff --git a/appview/pages/templates/repo/fragments/spindleInput.html b/appview/pages/templates/repo/fragments/spindleInput.html
new file mode 100644
index 000000000..802248878
--- /dev/null
+++ b/appview/pages/templates/repo/fragments/spindleInput.html
@@ -0,0 +1,20 @@
+{{ define "repo/fragments/spindleInput" }}
+ {{/* free text: membership lives on the spindle, so the appview can't offer a
+ closed list. .Spindles is only a type-ahead of recently used spindles. */}}
+
+
+{{ end }}
diff --git a/appview/pages/templates/repo/new.html b/appview/pages/templates/repo/new.html
index 42b1cf290..0dc823e47 100644
--- a/appview/pages/templates/repo/new.html
+++ b/appview/pages/templates/repo/new.html
@@ -199,36 +199,14 @@
{{ define "spindle" }}
-
{{ end }}
diff --git a/appview/pages/templates/repo/settings/pipelines.html b/appview/pages/templates/repo/settings/pipelines.html
index 750c0963c..a7885c5fb 100644
--- a/appview/pages/templates/repo/settings/pipelines.html
+++ b/appview/pages/templates/repo/settings/pipelines.html
@@ -20,8 +20,10 @@
Spindle
- Choose a spindle to execute your workflows on. Only repository owners
- can configure spindles. Spindles can be selfhosted,
+ The spindle to execute your workflows on; leave it empty to disable
+ pipelines. Its operator has to have allowed you as a member, otherwise
+ it will ignore this repository. Only repository owners can configure
+ spindles. Spindles can be selfhosted,
click to learn more.
@@ -33,25 +35,8 @@
{{ else }}
{{ end }}
-
-{{ define "list" }}
-
- Your spindles
-
- {{ range $spindle := .Spindles }}
- {{ template "spindles/fragments/spindleListing" . }}
- {{ else }}
-
- No spindles registered yet
-
- {{ end }}
-
-
-
-{{ end }}
diff --git a/appview/repo/repo.go b/appview/repo/repo.go
index a184d3cd5..5187ce90f 100644
--- a/appview/repo/repo.go
+++ b/appview/repo/repo.go
@@ -114,26 +114,19 @@ func (rp *Repo) EditSpindle(w http.ResponseWriter, r *http.Request) {
return
}
- newSpindle := r.FormValue("spindle")
- removingSpindle := newSpindle == "[[none]]" // see pages/templates/repo/settings/pipelines.html for more info on why we use this value
- client, err := rp.oauth.AuthorizedClient(r)
+ // an empty field removes the spindle; membership is the spindle's call, we
+ // only check that the value is a host we can talk to
+ newSpindle, err := models.ValidateSpindle(r.FormValue("spindle"), rp.config.Core.Dev)
if err != nil {
- fail("Failed to authorize. Try again later.", err)
+ rp.pages.Notice(w, errorId, err.Error())
return
}
+ removingSpindle := newSpindle == ""
- if !removingSpindle {
- // ensure that this is a valid spindle for this user
- validSpindles, err := rp.enforcer.GetSpindlesForUser(user.Did)
- if err != nil {
- fail("Failed to find spindles. Try again later.", err)
- return
- }
-
- if !slices.Contains(validSpindles, newSpindle) {
- fail("Failed to configure spindle.", fmt.Errorf("%s is not a valid spindle: %q", newSpindle, validSpindles))
- return
- }
+ client, err := rp.oauth.AuthorizedClient(r)
+ if err != nil {
+ fail("Failed to authorize. Try again later.", err)
+ return
}
newRepo := *f
@@ -1445,19 +1438,12 @@ 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
- }
+ // optional spindle selection; the spindle itself decides whether to accept
+ // this repo, we only check that the value is a host we can talk to
+ spindle, err := models.ValidateSpindle(r.FormValue("spindle"), rp.config.Core.Dev)
+ if err != nil {
+ rp.pages.Notice(w, "repo", err.Error())
+ return
}
// choose a name for a fork
diff --git a/appview/state/state.go b/appview/state/state.go
index 844efa48a..3995f37cd 100644
--- a/appview/state/state.go
+++ b/appview/state/state.go
@@ -7,7 +7,6 @@ import (
"fmt"
"log/slog"
"net/http"
- "slices"
"strings"
"time"
@@ -501,19 +500,12 @@ 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
- }
+ // optional spindle selection; the spindle itself decides whether to accept
+ // this repo, we only check that the value is a host we can talk to
+ spindle, err := models.ValidateSpindle(r.FormValue("spindle"), s.config.Core.Dev)
+ if err != nil {
+ s.pages.Notice(w, "repo", err.Error())
+ return
}
l = l.With("spindle", spindle)
diff --git a/docs/DOCS.md b/docs/DOCS.md
index 574ff1feb..d61a359c2 100644
--- a/docs/DOCS.md
+++ b/docs/DOCS.md
@@ -1542,6 +1542,12 @@ cache (and read from it), configure the cache (prefix
Spindle will now start, connect to the Jetstream server, and begin processing pipelines.
+Spindles are not registered with the appview. To point a repository at
+yours, type its hostname into the spindle field under the repository's
+pipeline settings (or when creating or forking a repo); recently used
+spindles are offered as suggestions. The spindle picks the repo up from
+the network and runs its pipelines if its owner is a member.
+
### Managing members
An invite-only spindle (the default, see `SPINDLE_SERVER_INVITE_ONLY`) only
@@ -2692,9 +2698,12 @@ git push local-dev main
```
The above VM should already be running a spindle on
-`localhost:6555`. Head to http://localhost:3000/settings/spindles and
-hit "Verify". You can then configure each repository to use
-this spindle and run CI jobs.
+`localhost:6555`. Spindles aren't registered with the appview:
+type `localhost:6555` into the spindle field on a repository's
+pipeline settings (or when creating the repo) and it will run
+that repo's CI jobs, as long as the spindle allows you as a
+member (see [Managing members](#managing-members), or run it
+with `SPINDLE_SERVER_INVITE_ONLY=false`).
Of interest when debugging spindles: