vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
| 1 | package web |
| 2 | |
| 3 | import ( |
| 4 | "net/http" |
| 5 | "strings" |
| 6 | |
| 7 | "rodent.software/vole/forge/access" |
| 8 | ) |
| 9 | |
| 10 | // AllowContributorCI records a maintainer's decision that the current tip of one |
| 11 | // contributor branch may run. The approval is per commit: a new push waits again. |
| 12 | // The queue-side operation is supplied by the server (Web.AllowCI), because the |
| 13 | // web package does not own workflow discovery or the job queue. |
| 14 | func (w *Web) AllowContributorCI(rw http.ResponseWriter, r *http.Request) { |
| 15 | ctx, ok := w.beginWrite(rw, r, maxTinyForm) |
| 16 | if !ok { |
| 17 | return |
| 18 | } |
| 19 | m := ctx.Repo |
| 20 | // Allowing CI is the approval capability: a contributor must not be able to |
| 21 | // release their own branch's work to a runner. |
| 22 | if !ctx.canApproveChange() { |
| 23 | w.refuse(rw, r, "only a maintainer or admin may allow CI") |
| 24 | return |
| 25 | } |
| 26 | ref := strings.TrimSpace(r.PostFormValue("ref")) |
| 27 | if _, ok := access.ContributorRefName(ref); !ok { |
| 28 | http.Error(rw, "that is not a contributor branch", http.StatusBadRequest) |
| 29 | return |
| 30 | } |
| 31 | tip, err := m.Git().RevParseRaw(ref) |
| 32 | if err != nil || tip == "" { |
| 33 | http.Error(rw, "that branch does not exist", http.StatusNotFound) |
| 34 | return |
| 35 | } |
| 36 | if w.AllowCI == nil { |
| 37 | w.serverError(rw, r, "CI is not wired into this server") |
| 38 | return |
| 39 | } |
| 40 | if err := w.AllowCI(m.Name, ref, tip, ctx.Name); err != nil { |
| 41 | http.Error(rw, err.Error(), http.StatusConflict) |
| 42 | return |
| 43 | } |
| 44 | w.audit("web.ci.allow", ctx.Name, m.Name, ref+" "+tip) |
| 45 | // The control lives on the change request now, so return there when the form |
| 46 | // named one. A direct CLI or old-form post still lands on the run history. |
| 47 | next := "/" + m.Name + "/runs" |
| 48 | if id := strings.TrimSpace(r.PostFormValue("request")); id != "" { |
| 49 | if req, err := w.requestAt(memoOf(r), m, id); err == nil { |
| 50 | next = "/" + m.Name + "/changes/" + req.ID |
| 51 | } |
| 52 | } |
| 53 | http.Redirect(rw, r, next, http.StatusSeeOther) |
| 54 | } |
| 55 |