Author | Jakob Wakeling <[email protected]> |
Date | 2023-12-16 04:51:02 |
Commit | fc3cc2ff51e7d7a6131587eb40f96215adb3a428 |
Parent | 1547a71d1d9e9b38a26ba2fd5073ef41efdb4d98 |
Allow editing of repositories' upstreams
Diffstat
M | res/repo/edit.html | | | 11 | +++++++++++ |
M | res/style.css | | | 7 | +++++-- |
M | src/cron/cron.go | | | 1 | + |
M | src/goit/repo.go | | | 39 | ++++++++++++++++++++++++++++++++++++++- |
M | src/repo/edit.go | | | 38 | ++++++++++++++++++++++++++++++++++---- |
5 files changed, 89 insertions, 7 deletions
diff --git a/res/repo/edit.html b/res/repo/edit.html index 6f99739..4e46261 100644 --- a/res/repo/edit.html +++ b/res/repo/edit.html @@ -33,6 +33,17 @@ </select> </td> </tr> + <tr> + <td style="text-align: right;"><label for="upstream">Upstream</label></td> + <td><input type="text" name="upstream" value="{{.Edit.Upstream}}" spellcheck="false"></td> + </tr> + <tr> + <td style="text-align: right;"><label for="mirror">Mirror</label></td> + <td> + <input type="checkbox" name="mirror" value="mirror" {{if .Edit.IsMirror}}checked{{end}}> + <span id="mirror-warn">Enabling mirror will replace any existing repository data</span> + </td> + </tr> <tr> <td></td> <td> diff --git a/res/style.css b/res/style.css index e5bb2d1..d84a048 100644 --- a/res/style.css +++ b/res/style.css @@ -22,8 +22,8 @@ table input[type="text"] { color: #888888; width: 24em; } table input[type="password"] { color: #888888; width: 24em; } table input[type="submit"] { color: #FF7E00; padding: 2px 1.6em; } table input[type="checkbox"] { - appearance: none; border: 2px solid #333333; border-radius: 3px; display:block; height: 1.375rem; margin: 0; - padding: 2px; width: 1.375rem; + appearance: none; border: 2px solid #333333; border-radius: 3px; display: inline-block; height: 1.375rem; + margin: 0; padding: 2px; vertical-align: top; width: 1.375rem; } table input[type="checkbox"]:hover { background-color: #222222; cursor: pointer; } table input[type="checkbox"]::after { content: ""; display: none; position:relative; } @@ -40,6 +40,9 @@ table textarea { max-height: 18rem; min-height: 6rem; padding: 2px; resize: vertical; width: 24em; } +table #mirror-warn { color: #FF7E00; display: none; vertical-align: middle; } +table input[type="checkbox"]:checked ~ #mirror-warn { display: inline; } + .term-fg1 { font-weight: bold; } /* Bold */ .term-fg2 { color: #888888; } /* Faint */ .term-fg3 { font-style: italic; } /* Italic */ diff --git a/src/cron/cron.go b/src/cron/cron.go index f768fd3..e9568f4 100644 --- a/src/cron/cron.go +++ b/src/cron/cron.go @@ -168,6 +168,7 @@ func (c *Cron) Add(rid int64, schedule Schedule, fn func()) uint64 { job.next = job.schedule.Next(time.Now().UTC()) c.jobs = append(c.jobs, job) + log.Println("[cron] added job", job.id, "for", job.rid) return job.id } diff --git a/src/goit/repo.go b/src/goit/repo.go index ac52cfa..7c22f94 100644 --- a/src/goit/repo.go +++ b/src/goit/repo.go @@ -189,9 +189,46 @@ func UpdateRepo(rid int64, repo Repo) error { } } + /* If the upstream URL has been removed, remove the remote */ + if repo.Upstream == "" && old.Upstream != "" { + r, err := git.PlainOpen(RepoPath(repo.Name, true)) + if err != nil { + tx.Rollback() + return err + } + + if err := r.DeleteRemote("origin"); err != nil { + tx.Rollback() + return err + } + } + + /* If the upstream URL has been added or changed, update the remote */ + if repo.Upstream != "" && repo.Upstream != old.Upstream { + r, err := git.PlainOpen(RepoPath(repo.Name, true)) + if err != nil { + tx.Rollback() + return err + } + + if err := r.DeleteRemote("origin"); err != nil && !errors.Is(err, git.ErrRemoteNotFound) { + tx.Rollback() + return err + } + + if _, err := r.CreateRemote(&gitconfig.RemoteConfig{ + Name: "origin", + URLs: []string{repo.Upstream}, + Mirror: util.If(repo.IsMirror, true, false), + Fetch: []gitconfig.RefSpec{gitconfig.RefSpec("+refs/heads/*:refs/heads/*")}, + }); err != nil { + log.Println("[repo/update]", err.Error()) + } + } + if err := tx.Commit(); err != nil { os.Rename(RepoPath(repo.Name, true), RepoPath(old.Name, true)) - log.Println("[repo/update]", "error while renaming, check repo \""+old.Name+"\"/\""+repo.Name+"\"") + log.Println("[repo/update]", "error while editing, check repo \""+old.Name+"\"/\""+repo.Name+"\"") return err } diff --git a/src/repo/edit.go b/src/repo/edit.go index c67d406..05598b9 100644 --- a/src/repo/edit.go +++ b/src/repo/edit.go @@ -12,6 +12,7 @@ import ( "path/filepath" "slices" + "github.com/Jamozed/Goit/src/cron" "github.com/Jamozed/Goit/src/goit" "github.com/Jamozed/Goit/src/util" "github.com/go-chi/chi/v5" @@ -60,9 +61,9 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { Editable bool Edit struct { - Id, Owner, Name, Description string - IsPrivate bool - Message string + Id, Owner, Name, Description, Upstream string + IsPrivate, IsMirror bool + Message string } Transfer struct{ Owner, Message string } @@ -83,7 +84,9 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { data.Edit.Owner = owner.FullName + " (" + owner.Name + ")[" + fmt.Sprint(owner.Id) + "]" data.Edit.Name = repo.Name data.Edit.Description = repo.Description + data.Edit.Upstream = repo.Upstream data.Edit.IsPrivate = repo.IsPrivate + data.Edit.IsMirror = repo.IsMirror gr, err := git.PlainOpen(goit.RepoPath(repo.Name, true)) if err != nil { @@ -113,7 +116,9 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { case "edit": data.Edit.Name = r.FormValue("reponame") data.Edit.Description = r.FormValue("description") + data.Edit.Upstream = r.FormValue("upstream") data.Edit.IsPrivate = r.FormValue("visibility") == "private" + data.Edit.IsMirror = r.FormValue("mirror") == "mirror" if data.Edit.Name == "" { data.Edit.Message = "Name cannot be empty" @@ -128,12 +133,37 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { } else if len(data.Edit.Description) > 256 { data.Edit.Message = "Description cannot exceed 256 characters" } else if err := goit.UpdateRepo(repo.Id, goit.Repo{ - Name: data.Edit.Name, Description: data.Edit.Description, IsPrivate: data.Edit.IsPrivate, + Name: data.Edit.Name, Description: data.Edit.Description, Upstream: data.Edit.Upstream, + IsPrivate: data.Edit.IsPrivate, IsMirror: data.Edit.IsMirror, }); err != nil { log.Println("[/repo/edit]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) return } else { + if (data.Edit.Upstream == "" && repo.Upstream != "") || !data.Edit.IsMirror { + goit.Cron.RemoveFor(repo.Id) + goit.Cron.Update() + } else if data.Edit.Upstream != "" && data.Edit.IsMirror && + (data.Edit.Upstream != repo.Upstream || !repo.IsMirror) { + goit.Cron.RemoveFor(repo.Id) + + goit.Cron.Add(repo.Id, cron.Immediate, func() { + if err := goit.Pull(repo.Id); err != nil { + log.Println("[cron:import]", err.Error()) + } + log.Println("[cron:import] imported", data.Name) + }) + + goit.Cron.Add(repo.Id, cron.Daily, func() { + if err := goit.Pull(repo.Id); err != nil { + log.Println("[cron:mirror]", err.Error()) + } + log.Println("[cron:mirror] updated", data.Edit.Name) + }) + + goit.Cron.Update() + } + http.Redirect(w, r, "/"+data.Edit.Name+"/edit", http.StatusFound) return }