Author | Jakob Wakeling <[email protected]> |
Date | 2023-12-25 06:52:40 |
Commit | 28a8f1350e700754422e9fb338cd5021b529d9a8 |
Parent | d0367aad8e31d8c388f418ef898228986d84d2d8 |
Support custom default branches
Diffstat
M | res/admin/repo_edit.html | | | 4 | ++++ |
M | res/repo/create.html | | | 4 | ++++ |
M | res/repo/edit.html | | | 4 | ++++ |
M | src/admin/repos.go | | | 13 | ++++++++----- |
M | src/goit/db.go | | | 22 | ++++++++++++++++++++-- |
M | src/goit/repo.go | | | 99 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
M | src/repo/create.go | | | 12 | +++++++----- |
M | src/repo/edit.go | | | 14 | +++++++++----- |
8 files changed, 125 insertions, 47 deletions
diff --git a/res/admin/repo_edit.html b/res/admin/repo_edit.html index dcad042..e5b9364 100644 --- a/res/admin/repo_edit.html +++ b/res/admin/repo_edit.html @@ -24,6 +24,10 @@ <td style="text-align:right; vertical-align:top;"><label for="description">Description</label></td> <td><textarea name="description" spellcheck="false">{{.Edit.Description}}</textarea></td> </tr> + <tr> + <td style="text-align: right;"><label for="branch">Default Branch</label></td> + <td><input type="text" name="branch" value="{{.Edit.DefaultBranch}}" placeholder="master"></td> + </tr> <tr> <td style="text-align: right;"><label for="visibility">Visibility:</label></td> <td> diff --git a/res/repo/create.html b/res/repo/create.html index 918190f..21af36f 100644 --- a/res/repo/create.html +++ b/res/repo/create.html @@ -22,6 +22,10 @@ <td style="text-align: right; vertical-align: top;"><label for="description">Description</label></td> <td><textarea name="description"></textarea></td> </tr> + <tr> + <td style="text-align: right;"><label for="branch">Default Branch</label></td> + <td><input type="text" name="branch" placeholder="master"></td> + </tr> <tr> <td style="text-align: right;"><label for="visibility">Visibility</label></td> <td> diff --git a/res/repo/edit.html b/res/repo/edit.html index 4e46261..b09ea4d 100644 --- a/res/repo/edit.html +++ b/res/repo/edit.html @@ -24,6 +24,10 @@ <td style="text-align:right; vertical-align:top;"><label for="description">Description</label></td> <td><textarea name="description" spellcheck="false">{{.Edit.Description}}</textarea></td> </tr> + <tr> + <td style="text-align: right;"><label for="branch">Default Branch</label></td> + <td><input type="text" name="branch" value="{{.Edit.DefaultBranch}}" placeholder="master"></td> + </tr> <tr> <td style="text-align: right;"><label for="visibility">Visibility</label></td> <td> diff --git a/src/admin/repos.go b/src/admin/repos.go index 7833bde..80f557c 100644 --- a/src/admin/repos.go +++ b/src/admin/repos.go @@ -110,9 +110,10 @@ func HandleRepoEdit(w http.ResponseWriter, r *http.Request) { Title, Name string Edit struct { - Id, Owner, Name, Description, Upstream string - IsPrivate, IsMirror bool - Message string + Id, Owner, Name, Description string + DefaultBranch, Upstream string + IsPrivate, IsMirror bool + Message string } Transfer struct{ Owner, Message string } @@ -130,6 +131,7 @@ func HandleRepoEdit(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.DefaultBranch = repo.DefaultBranch data.Edit.Upstream = repo.Upstream data.Edit.IsPrivate = repo.IsPrivate data.Edit.IsMirror = repo.IsMirror @@ -139,6 +141,7 @@ func HandleRepoEdit(w http.ResponseWriter, r *http.Request) { case "edit": data.Edit.Name = r.FormValue("reponame") data.Edit.Description = r.FormValue("description") + data.Edit.DefaultBranch = util.If(r.FormValue("branch") == "", "master", r.FormValue("branch")) data.Edit.Upstream = r.FormValue("upstream") data.Edit.IsPrivate = r.FormValue("visibility") == "private" data.Edit.IsMirror = r.FormValue("mirror") == "mirror" @@ -156,8 +159,8 @@ func HandleRepoEdit(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, Upstream: data.Edit.Upstream, - IsPrivate: data.Edit.IsPrivate, IsMirror: data.Edit.IsMirror, + Name: data.Edit.Name, Description: data.Edit.Description, DefaultBranch: data.Edit.DefaultBranch, + Upstream: data.Edit.Upstream, IsPrivate: data.Edit.IsPrivate, IsMirror: data.Edit.IsMirror, }); err != nil { log.Println("[/admin/repo/edit]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) diff --git a/src/goit/db.go b/src/goit/db.go index 546e84e..e3abea7 100644 --- a/src/goit/db.go +++ b/src/goit/db.go @@ -32,7 +32,7 @@ import ( */ func dbUpdate(db *sql.DB) error { - latestVersion := 1 + latestVersion := 2 var version int if err := db.QueryRow("PRAGMA user_version").Scan(&version); err != nil { @@ -68,6 +68,7 @@ func dbUpdate(db *sql.DB) error { name TEXT UNIQUE NOT NULL, name_lower TEXT UNIQUE NOT NULL, description TEXT NOT NULL, + default_branch TEXT NOT NULL, upstream TEXT NOT NULL, is_private BOOLEAN NOT NULL, is_mirror BOOLEAN NOT NULL @@ -83,8 +84,25 @@ func dbUpdate(db *sql.DB) error { for { switch version { + case 1: /* 1 -> 2 */ + log.Println("Migrating database from version 1 to 2") + + if _, err := db.Exec( + "ALTER TABLE repos ADD COLUMN default_branch TEXT NOT NULL DEFAULT 'master'", + ); err != nil { + return err + } + + version = 2 default: /* No required migrations */ - return nil + goto done } } + +done: + if _, err := db.Exec(fmt.Sprint("PRAGMA user_version = ", version)); err != nil { + return err + } + + return nil } diff --git a/src/goit/repo.go b/src/goit/repo.go index f0d86da..32efa11 100644 --- a/src/goit/repo.go +++ b/src/goit/repo.go @@ -14,22 +14,26 @@ import ( "github.com/Jamozed/Goit/src/util" "github.com/go-git/go-git/v5" gitconfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" ) type Repo struct { - Id int64 `json:"id"` - OwnerId int64 `json:"owner_id"` - Name string `json:"name"` - Description string `json:"description"` - Upstream string `json:"upstream"` - IsPrivate bool `json:"is_private"` - IsMirror bool `json:"is_mirror"` + Id int64 `json:"id"` + OwnerId int64 `json:"owner_id"` + Name string `json:"name"` + Description string `json:"description"` + DefaultBranch string `json:"default_branch"` + Upstream string `json:"upstream"` + IsPrivate bool `json:"is_private"` + IsMirror bool `json:"is_mirror"` } func GetRepos() ([]Repo, error) { repos := []Repo{} - rows, err := db.Query("SELECT id, owner_id, name, description, upstream, is_private, is_mirror FROM repos") + rows, err := db.Query( + "SELECT id, owner_id, name, description, default_branch, upstream, is_private, is_mirror FROM repos", + ) if err != nil { return nil, err } @@ -39,7 +43,7 @@ func GetRepos() ([]Repo, error) { for rows.Next() { r := Repo{} if err := rows.Scan( - &r.Id, &r.OwnerId, &r.Name, &r.Description, &r.Upstream, &r.IsPrivate, &r.IsMirror, + &r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.IsPrivate, &r.IsMirror, ); err != nil { return nil, err } @@ -58,8 +62,11 @@ func GetRepo(rid int64) (*Repo, error) { r := &Repo{} if err := db.QueryRow( - "SELECT id, owner_id, name, description, upstream, is_private, is_mirror FROM repos WHERE id = ?", rid, - ).Scan(&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.Upstream, &r.IsPrivate, &r.IsMirror); err != nil { + `SELECT id, owner_id, name, description, default_branch, upstream, is_private, is_mirror FROM repos + WHERE id = ?`, rid, + ).Scan( + &r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.IsPrivate, &r.IsMirror, + ); err != nil { if !errors.Is(err, sql.ErrNoRows) { return nil, err } @@ -74,8 +81,11 @@ func GetRepoByName(name string) (*Repo, error) { r := &Repo{} if err := db.QueryRow( - "SELECT id, owner_id, name, description, upstream, is_private, is_mirror FROM repos WHERE name = ?", name, - ).Scan(&r.Id, &r.OwnerId, &r.Name, &r.Description, &r.Upstream, &r.IsPrivate, &r.IsMirror); err != nil { + `SELECT id, owner_id, name, description, default_branch, upstream, is_private, is_mirror FROM repos + WHERE name = ?`, name, + ).Scan( + &r.Id, &r.OwnerId, &r.Name, &r.Description, &r.DefaultBranch, &r.Upstream, &r.IsPrivate, &r.IsMirror, + ); err != nil { if !errors.Is(err, sql.ErrNoRows) { return nil, err } @@ -93,9 +103,9 @@ func CreateRepo(repo Repo) (int64, error) { } res, err := tx.Exec( - `INSERT INTO repos (owner_id, name, name_lower, description, upstream, is_private, is_mirror) - VALUES (?, ?, ?, ?, ?, ?, ?)`, repo.OwnerId, repo.Name, strings.ToLower(repo.Name), repo.Description, - repo.Upstream, repo.IsPrivate, repo.IsMirror, + `INSERT INTO repos (owner_id, name, name_lower, description, default_branch, upstream, is_private, is_mirror) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, repo.OwnerId, repo.Name, strings.ToLower(repo.Name), repo.Description, + repo.DefaultBranch, repo.Upstream, repo.IsPrivate, repo.IsMirror, ) if err != nil { tx.Rollback() @@ -108,7 +118,9 @@ func CreateRepo(repo Repo) (int64, error) { return -1, err } - if err := tx.Commit(); err != nil { + ref := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.NewBranchReferenceName(repo.DefaultBranch)) + if err := r.Storer.SetReference(ref); err != nil { + tx.Rollback() os.RemoveAll(RepoPath(repo.Name, true)) return -1, err } @@ -120,12 +132,18 @@ func CreateRepo(repo Repo) (int64, error) { Mirror: util.If(repo.IsMirror, true, false), Fetch: []gitconfig.RefSpec{gitconfig.RefSpec("+refs/heads/*:refs/heads/*")}, }); err != nil { - log.Println("[repo/upstream]", err.Error()) + tx.Rollback() + os.RemoveAll(RepoPath(repo.Name, true)) + return -1, err } } - rid, _ := res.LastInsertId() + if err := tx.Commit(); err != nil { + os.RemoveAll(RepoPath(repo.Name, true)) + return -1, err + } + rid, _ := res.LastInsertId() return rid, nil } @@ -175,9 +193,9 @@ func UpdateRepo(rid int64, repo Repo) error { } if _, err := tx.Exec( - `UPDATE repos SET name = ?, name_lower = ?, description = ?, upstream = ?, is_private = ?, is_mirror = ? - WHERE id = ?`, repo.Name, strings.ToLower(repo.Name), repo.Description, repo.Upstream, repo.IsPrivate, - repo.IsMirror, rid, + `UPDATE repos SET name = ?, name_lower = ?, description = ?, default_branch = ?, upstream = ?, is_private = ?, + is_mirror = ? WHERE id = ?`, repo.Name, strings.ToLower(repo.Name), repo.Description, repo.DefaultBranch, + repo.Upstream, repo.IsPrivate, repo.IsMirror, rid, ); err != nil { tx.Rollback() return err @@ -195,13 +213,32 @@ 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 { + var r *git.Repository + if repo.DefaultBranch != old.DefaultBranch { + if r == nil { + r, err = git.PlainOpen(RepoPath(repo.Name, true)) + if err != nil { + tx.Rollback() + return err + } + } + + ref := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.NewBranchReferenceName(repo.DefaultBranch)) + if err := r.Storer.SetReference(ref); err != nil { tx.Rollback() return err } + } + + /* If the upstream URL has been removed, remove the remote */ + if repo.Upstream == "" && old.Upstream != "" { + if r == nil { + r, err = git.PlainOpen(RepoPath(repo.Name, true)) + if err != nil { + tx.Rollback() + return err + } + } if err := r.DeleteRemote("origin"); err != nil { tx.Rollback() @@ -211,10 +248,12 @@ func UpdateRepo(rid int64, repo Repo) error { /* 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 r == nil { + 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) { diff --git a/src/repo/create.go b/src/repo/create.go index 43fba60..6f380ea 100644 --- a/src/repo/create.go +++ b/src/repo/create.go @@ -29,9 +29,10 @@ func HandleCreate(w http.ResponseWriter, r *http.Request) { } data := struct { - Title, Message string - Name, Description, Url string - IsPrivate, IsMirror bool + Title, Message string + Name, Description string + DefaultBranch, Url string + IsPrivate, IsMirror bool CsrfField template.HTML }{ @@ -43,6 +44,7 @@ func HandleCreate(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodPost { data.Name = r.FormValue("reponame") data.Description = r.FormValue("description") + data.DefaultBranch = util.If(r.FormValue("branch") == "", "master", r.FormValue("branch")) data.Url = r.FormValue("url") data.IsPrivate = r.FormValue("visibility") == "private" data.IsMirror = r.FormValue("mirror") == "mirror" @@ -58,8 +60,8 @@ func HandleCreate(w http.ResponseWriter, r *http.Request) { } else if exists { data.Message = "Name \"" + data.Name + "\" is taken" } else if rid, err := goit.CreateRepo(goit.Repo{ - OwnerId: user.Id, Name: data.Name, Description: data.Description, Upstream: data.Url, - IsPrivate: data.IsPrivate, IsMirror: data.IsMirror, + OwnerId: user.Id, Name: data.Name, Description: data.Description, DefaultBranch: data.DefaultBranch, + Upstream: data.Url, IsPrivate: data.IsPrivate, IsMirror: data.IsMirror, }); err != nil { log.Println("[/repo/create]", err.Error()) goit.HttpError(w, http.StatusInternalServerError) diff --git a/src/repo/edit.go b/src/repo/edit.go index a12c81c..a709f73 100644 --- a/src/repo/edit.go +++ b/src/repo/edit.go @@ -15,6 +15,7 @@ import ( "github.com/Jamozed/Goit/src/cron" "github.com/Jamozed/Goit/src/goit" + "github.com/Jamozed/Goit/src/util" "github.com/go-chi/chi/v5" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -60,9 +61,10 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { Title string Edit struct { - Id, Owner, Name, Description, Upstream string - IsPrivate, IsMirror bool - Message string + Id, Owner, Name, Description string + DefaultBranch, Upstream string + IsPrivate, IsMirror bool + Message string } Transfer struct{ Owner, Message string } @@ -80,6 +82,7 @@ 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.DefaultBranch = repo.DefaultBranch data.Edit.Upstream = repo.Upstream data.Edit.IsPrivate = repo.IsPrivate data.Edit.IsMirror = repo.IsMirror @@ -112,6 +115,7 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { case "edit": data.Edit.Name = r.FormValue("reponame") data.Edit.Description = r.FormValue("description") + data.Edit.DefaultBranch = util.If(r.FormValue("branch") == "", "master", r.FormValue("branch")) data.Edit.Upstream = r.FormValue("upstream") data.Edit.IsPrivate = r.FormValue("visibility") == "private" data.Edit.IsMirror = r.FormValue("mirror") == "mirror" @@ -129,8 +133,8 @@ 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, Upstream: data.Edit.Upstream, - IsPrivate: data.Edit.IsPrivate, IsMirror: data.Edit.IsMirror, + Name: data.Edit.Name, Description: data.Edit.Description, DefaultBranch: data.Edit.DefaultBranch, + 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)