diff --git a/README.md b/README.md index d34aa5a68..bd480090a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language ![Demo](http://gowalker.org/public/gogs_demo.gif) -##### Current version: 0.4.3 Alpha +##### Current version: 0.4.4 Alpha ### NOTICES diff --git a/README_ZH.md b/README_ZH.md index ebffa33ef..5f57d8a6d 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 ![Demo](http://gowalker.org/public/gogs_demo.gif) -##### 当前版本:0.4.3 Alpha +##### 当前版本:0.4.4 Alpha ## 开发目的 diff --git a/cmd/web.go b/cmd/web.go index 2b14b0778..27b3d8cf9 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -228,11 +228,13 @@ func runWeb(*cli.Context) { }) r.Post("/comment/:action", repo.Comment) - r.Get("/releases/new", repo.ReleasesNew) + r.Get("/releases/new", repo.NewRelease) + r.Get("/releases/edit/:tagname", repo.EditRelease) }, reqSignIn, middleware.RepoAssignment(true)) m.Group("/:username/:reponame", func(r martini.Router) { - r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.ReleasesNewPost) + r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) + r.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) }, reqSignIn, middleware.RepoAssignment(true, true)) m.Group("/:username/:reponame", func(r martini.Router) { diff --git a/gogs.go b/gogs.go index ebb5f8213..48202d147 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.4.3.0612 Alpha" +const APP_VER = "0.4.4.0612 Alpha" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/release.go b/models/release.go index e5e81b9b6..32e8c92cc 100644 --- a/models/release.go +++ b/models/release.go @@ -6,15 +6,16 @@ package models import ( "errors" + "sort" "strings" "time" - // "github.com/Unknwon/com" "github.com/gogits/git" ) var ( ErrReleaseAlreadyExist = errors.New("Release already exist") + ErrReleaseNotExist = errors.New("Release does not exist") ) // Release represents a release of repository. @@ -23,22 +24,17 @@ type Release struct { RepoId int64 PublisherId int64 Publisher *User `xorm:"-"` - Title string TagName string LowerTagName string Target string + Title string Sha1 string `xorm:"VARCHAR(40)"` NumCommits int NumCommitsBehind int `xorm:"-"` Note string `xorm:"TEXT"` + IsDraft bool `xorm:"NOT NULL DEFAULT false"` IsPrerelease bool - Created time.Time `xorm:"created"` -} - -// GetReleasesByRepoId returns a list of releases of repository. -func GetReleasesByRepoId(repoId int64) (rels []*Release, err error) { - err = orm.Desc("created").Find(&rels, Release{RepoId: repoId}) - return rels, err + Created time.Time `xorm:"CREATED"` } // IsReleaseExist returns true if release with given tag name already exists. @@ -50,6 +46,33 @@ func IsReleaseExist(repoId int64, tagName string) (bool, error) { return orm.Get(&Release{RepoId: repoId, LowerTagName: strings.ToLower(tagName)}) } +func createTag(gitRepo *git.Repository, rel *Release) error { + // Only actual create when publish. + if !rel.IsDraft { + if !gitRepo.IsTagExist(rel.TagName) { + commit, err := gitRepo.GetCommitOfBranch(rel.Target) + if err != nil { + return err + } + + if err = gitRepo.CreateTag(rel.TagName, commit.Id.String()); err != nil { + return err + } + } else { + commit, err := gitRepo.GetCommitOfTag(rel.TagName) + if err != nil { + return err + } + + rel.NumCommits, err = commit.CommitsCount() + if err != nil { + return err + } + } + } + return nil +} + // CreateRelease creates a new release of repository. func CreateRelease(gitRepo *git.Repository, rel *Release) error { isExist, err := IsReleaseExist(rel.RepoId, rel.TagName) @@ -59,28 +82,65 @@ func CreateRelease(gitRepo *git.Repository, rel *Release) error { return ErrReleaseAlreadyExist } - if !gitRepo.IsTagExist(rel.TagName) { - commit, err := gitRepo.GetCommitOfBranch(rel.Target) - if err != nil { - return err - } + if err = createTag(gitRepo, rel); err != nil { + return err + } + rel.LowerTagName = strings.ToLower(rel.TagName) + _, err = orm.InsertOne(rel) + return err +} - if err = gitRepo.CreateTag(rel.TagName, commit.Id.String()); err != nil { - return err - } - } else { - commit, err := gitRepo.GetCommitOfTag(rel.TagName) - if err != nil { - return err - } +// GetRelease returns release by given ID. +func GetRelease(repoId int64, tagName string) (*Release, error) { + isExist, err := IsReleaseExist(repoId, tagName) + if err != nil { + return nil, err + } else if !isExist { + return nil, ErrReleaseNotExist + } - rel.NumCommits, err = commit.CommitsCount() - if err != nil { - return err - } + rel := &Release{RepoId: repoId, LowerTagName: strings.ToLower(tagName)} + _, err = orm.Get(rel) + return rel, err +} + +// GetReleasesByRepoId returns a list of releases of repository. +func GetReleasesByRepoId(repoId int64) (rels []*Release, err error) { + err = orm.Desc("created").Find(&rels, Release{RepoId: repoId}) + return rels, err +} + +type ReleaseSorter struct { + rels []*Release +} + +func (rs *ReleaseSorter) Len() int { + return len(rs.rels) +} + +func (rs *ReleaseSorter) Less(i, j int) bool { + diffNum := rs.rels[i].NumCommits - rs.rels[j].NumCommits + if diffNum != 0 { + return diffNum > 0 } + return rs.rels[i].Created.After(rs.rels[j].Created) +} - rel.LowerTagName = strings.ToLower(rel.TagName) - _, err = orm.InsertOne(rel) +func (rs *ReleaseSorter) Swap(i, j int) { + rs.rels[i], rs.rels[j] = rs.rels[j], rs.rels[i] +} + +// SortReleases sorts releases by number of commits and created time. +func SortReleases(rels []*Release) { + sorter := &ReleaseSorter{rels: rels} + sort.Sort(sorter) +} + +// UpdateRelease updates information of a release. +func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) { + if err = createTag(gitRepo, rel); err != nil { + return err + } + _, err = orm.Id(rel.Id).AllCols().Update(rel) return err } diff --git a/models/repo.go b/models/repo.go index 7eab83c21..0f0181353 100644 --- a/models/repo.go +++ b/models/repo.go @@ -835,7 +835,7 @@ func GetCollaborativeRepos(uname string) ([]*Repository, error) { if infos[0] == uname { continue } - + u, err := GetUserByName(infos[0]) if err != nil { return nil, err diff --git a/modules/auth/repo.go b/modules/auth/repo.go index 26ab7551c..92ba64a27 100644 --- a/modules/auth/repo.go +++ b/modules/auth/repo.go @@ -208,6 +208,7 @@ type NewReleaseForm struct { Target string `form:"tag_target" binding:"Required"` Title string `form:"title" binding:"Required"` Content string `form:"content" binding:"Required"` + Draft string `form:"draft"` Prerelease bool `form:"prerelease"` } @@ -225,3 +226,25 @@ func (f *NewReleaseForm) Validate(errors *binding.Errors, req *http.Request, con data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) validate(errors, data, f) } + +type EditReleaseForm struct { + Target string `form:"tag_target" binding:"Required"` + Title string `form:"title" binding:"Required"` + Content string `form:"content" binding:"Required"` + Draft string `form:"draft"` + Prerelease bool `form:"prerelease"` +} + +func (f *EditReleaseForm) Name(field string) string { + names := map[string]string{ + "Target": "Target", + "Title": "Release title", + "Content": "Release content", + } + return names[field] +} + +func (f *EditReleaseForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { + data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) + validate(errors, data, f) +} diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index c1acc827e..6c77ed2a7 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -21,21 +21,17 @@ import ( func RepoAssignment(redirect bool, args ...bool) martini.Handler { return func(ctx *Context, params martini.Params) { - log.Trace(fmt.Sprint(args)) // valid brachname var validBranch bool // display bare quick start if it is a bare repo var displayBare bool if len(args) >= 1 { - // Note: argument has wrong value in Go1.3 martini. - // validBranch = args[0] - validBranch = true + validBranch = args[0] } if len(args) >= 2 { - // displayBare = args[1] - displayBare = true + displayBare = args[1] } var ( diff --git a/routers/repo/release.go b/routers/repo/release.go index 27df3f6d1..b6149b63a 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -5,7 +5,7 @@ package repo import ( - "sort" + "github.com/go-martini/martini" "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" @@ -14,27 +14,6 @@ import ( "github.com/gogits/gogs/modules/middleware" ) -type ReleaseSorter struct { - rels []*models.Release -} - -func (rs *ReleaseSorter) Len() int { - return len(rs.rels) -} - -func (rs *ReleaseSorter) Less(i, j int) bool { - diffNum := rs.rels[i].NumCommits - rs.rels[j].NumCommits - if diffNum != 0 { - return diffNum > 0 - } - - return rs.rels[i].Created.Second() > rs.rels[j].Created.Second() -} - -func (rs *ReleaseSorter) Swap(i, j int) { - rs.rels[i], rs.rels[j] = rs.rels[j], rs.rels[i] -} - func Releases(ctx *middleware.Context) { ctx.Data["Title"] = "Releases" ctx.Data["IsRepoToolbarReleases"] = true @@ -57,53 +36,76 @@ func Releases(ctx *middleware.Context) { return } - var tags ReleaseSorter - tags.rels = make([]*models.Release, len(rawTags)) + // Temproray cache commits count of used branches to speed up. + countCache := make(map[string]int) + + tags := make([]*models.Release, len(rawTags)) for i, rawTag := range rawTags { for _, rel := range rels { + if rel.IsDraft && !ctx.Repo.IsOwner { + continue + } if rel.TagName == rawTag { rel.Publisher, err = models.GetUserById(rel.PublisherId) if err != nil { ctx.Handle(500, "release.Releases(GetUserById)", err) return } - rel.NumCommitsBehind = commitsCount - rel.NumCommits + // Get corresponding target if it's not the current branch. + if ctx.Repo.BranchName != rel.Target { + // Get count if not exists. + if _, ok := countCache[rel.Target]; !ok { + commit, err := ctx.Repo.GitRepo.GetCommitOfTag(rel.TagName) + if err != nil { + ctx.Handle(500, "release.Releases(GetCommitOfTag)", err) + return + } + countCache[rel.Target], err = commit.CommitsCount() + if err != nil { + ctx.Handle(500, "release.Releases(CommitsCount2)", err) + return + } + } + rel.NumCommitsBehind = countCache[rel.Target] - rel.NumCommits + } else { + rel.NumCommitsBehind = commitsCount - rel.NumCommits + } + rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink) - tags.rels[i] = rel + tags[i] = rel break } } - if tags.rels[i] == nil { + if tags[i] == nil { commit, err := ctx.Repo.GitRepo.GetCommitOfTag(rawTag) if err != nil { - ctx.Handle(500, "release.Releases(GetCommitOfTag)", err) + ctx.Handle(500, "release.Releases(GetCommitOfTag2)", err) return } - tags.rels[i] = &models.Release{ + tags[i] = &models.Release{ Title: rawTag, TagName: rawTag, Sha1: commit.Id.String(), } - tags.rels[i].NumCommits, err = ctx.Repo.GitRepo.CommitsCount(commit.Id.String()) + + tags[i].NumCommits, err = ctx.Repo.GitRepo.CommitsCount(commit.Id.String()) if err != nil { ctx.Handle(500, "release.Releases(CommitsCount)", err) return } - tags.rels[i].NumCommitsBehind = commitsCount - tags.rels[i].NumCommits + tags[i].NumCommitsBehind = commitsCount - tags[i].NumCommits } } - - sort.Sort(&tags) - - ctx.Data["Releases"] = tags.rels + models.SortReleases(tags) + ctx.Data["Releases"] = tags ctx.HTML(200, "release/list") } -func ReleasesNew(ctx *middleware.Context) { +func NewRelease(ctx *middleware.Context) { if !ctx.Repo.IsOwner { - ctx.Handle(404, "release.ReleasesNew", nil) + ctx.Handle(403, "release.ReleasesNew", nil) return } @@ -113,9 +115,9 @@ func ReleasesNew(ctx *middleware.Context) { ctx.HTML(200, "release/new") } -func ReleasesNewPost(ctx *middleware.Context, form auth.NewReleaseForm) { +func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { if !ctx.Repo.IsOwner { - ctx.Handle(404, "release.ReleasesNew", nil) + ctx.Handle(403, "release.ReleasesNew", nil) return } @@ -148,6 +150,7 @@ func ReleasesNewPost(ctx *middleware.Context, form auth.NewReleaseForm) { Sha1: ctx.Repo.Commit.Id.String(), NumCommits: commitsCount, Note: form.Content, + IsDraft: len(form.Draft) > 0, IsPrerelease: form.Prerelease, } @@ -163,3 +166,58 @@ func ReleasesNewPost(ctx *middleware.Context, form auth.NewReleaseForm) { ctx.Redirect(ctx.Repo.RepoLink + "/releases") } + +func EditRelease(ctx *middleware.Context, params martini.Params) { + if !ctx.Repo.IsOwner { + ctx.Handle(403, "release.ReleasesEdit", nil) + return + } + + tagName := params["tagname"] + rel, err := models.GetRelease(ctx.Repo.Repository.Id, tagName) + if err != nil { + if err == models.ErrReleaseNotExist { + ctx.Handle(404, "release.ReleasesEdit(GetRelease)", err) + } else { + ctx.Handle(500, "release.ReleasesEdit(GetRelease)", err) + } + return + } + ctx.Data["Release"] = rel + + ctx.Data["Title"] = "Edit Release" + ctx.Data["IsRepoToolbarReleases"] = true + ctx.HTML(200, "release/edit") +} + +func EditReleasePost(ctx *middleware.Context, params martini.Params, form auth.EditReleaseForm) { + if !ctx.Repo.IsOwner { + ctx.Handle(403, "release.EditReleasePost", nil) + return + } + + tagName := params["tagname"] + rel, err := models.GetRelease(ctx.Repo.Repository.Id, tagName) + if err != nil { + if err == models.ErrReleaseNotExist { + ctx.Handle(404, "release.EditReleasePost(GetRelease)", err) + } else { + ctx.Handle(500, "release.EditReleasePost(GetRelease)", err) + } + return + } + ctx.Data["Release"] = rel + + ctx.Data["Title"] = "Edit Release" + ctx.Data["IsRepoToolbarReleases"] = true + + rel.Title = form.Title + rel.Note = form.Content + rel.IsDraft = len(form.Draft) > 0 + rel.IsPrerelease = form.Prerelease + if err = models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil { + ctx.Handle(500, "release.EditReleasePost(UpdateRelease)", err) + return + } + ctx.Redirect(ctx.Repo.RepoLink + "/releases") +} diff --git a/templates/VERSION b/templates/VERSION index 1eb3e8451..8e45ca592 100644 --- a/templates/VERSION +++ b/templates/VERSION @@ -1 +1 @@ -0.4.3.0612 Alpha \ No newline at end of file +0.4.4.0612 Alpha \ No newline at end of file diff --git a/templates/release/edit.tmpl b/templates/release/edit.tmpl new file mode 100644 index 000000000..e437092c8 --- /dev/null +++ b/templates/release/edit.tmpl @@ -0,0 +1,70 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +{{template "repo/nav" .}} +{{template "repo/toolbar" .}} +
+
+

Edit Release

+ {{template "base/alert" .}} +
+ {{.CsrfTokenHtml}} +
+ {{.Release.TagName}} + @ +
+ + + + +
+

Choose an existing tag, or create a new tag on publish

+
+
+ +
+
+
+ Content with Markdown +
+ +
+
+
+ +
+
+
loading...
+
+
+
+
+ +

We’ll point out that this release is identified as non-production ready.

+
+
+ + +
+
+
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/release/list.tmpl b/templates/release/list.tmpl index 2607dfacd..0f02508fb 100644 --- a/templates/release/list.tmpl +++ b/templates/release/list.tmpl @@ -14,17 +14,23 @@
  • {{if .PublisherId}}
    - {{if .IsPrerelease}}Pre-Release{{else}}Stable{{end}} + {{if .IsDraft}} + Draft + {{else if .IsPrerelease}} + Pre-Release + {{else}} + Stable + {{end}} {{.TagName}} {{ShortSha .Sha1}}
    -

    {{.Title}}

    +

    {{.Title}} (edit)

       {{.Publisher.Name}} {{if .Created}}{{TimeSince .Created}}{{end}} - {{.NumCommitsBehind}} commits since this release + {{.NumCommitsBehind}} commits to {{.Target}} since this release

    {{str2html .Note}} diff --git a/templates/release/new.tmpl b/templates/release/new.tmpl index 06cb28c78..6c5cf40ce 100644 --- a/templates/release/new.tmpl +++ b/templates/release/new.tmpl @@ -62,7 +62,7 @@
    - +