diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | README_ZH.md | 2 | ||||
-rw-r--r-- | cmd/web.go | 6 | ||||
-rw-r--r-- | gogs.go | 2 | ||||
-rw-r--r-- | models/release.go | 116 | ||||
-rw-r--r-- | models/repo.go | 2 | ||||
-rw-r--r-- | modules/auth/repo.go | 23 | ||||
-rw-r--r-- | modules/middleware/repo.go | 8 | ||||
-rw-r--r-- | routers/repo/release.go | 136 | ||||
-rw-r--r-- | templates/VERSION | 2 | ||||
-rw-r--r-- | templates/release/edit.tmpl | 70 | ||||
-rw-r--r-- | templates/release/list.tmpl | 12 | ||||
-rw-r--r-- | templates/release/new.tmpl | 2 |
13 files changed, 299 insertions, 84 deletions
@@ -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 ebffa33efb..5f57d8a6df 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 2b14b0778e..27b3d8cf92 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) { @@ -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 e5e81b9b67..32e8c92cc4 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 7eab83c217..0f01813536 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 26ab7551ca..92ba64a27b 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 c1acc827ee..6c77ed2a77 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 27df3f6d16..b6149b63a2 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 1eb3e84514..8e45ca5921 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 0000000000..e437092c8c --- /dev/null +++ b/templates/release/edit.tmpl @@ -0,0 +1,70 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +{{template "repo/nav" .}} +{{template "repo/toolbar" .}} +<div id="body" class="container"> + <div id="release"> + <h4 id="release-head">Edit Release</h4> + {{template "base/alert" .}} + <form id="release-new-form" action="{{.RepoLink}}/releases/edit/{{.Release.TagName}}" method="post" class="form form-inline"> + {{.CsrfTokenHtml}} + <div class="form-group"> + <b>{{.Release.TagName}}</b> + <span class="target-at">@</span> + <div class="btn-group" id="release-new-target-select"> + <button type="button" class="btn btn-default"><i class="fa fa-code-fork fa-lg fa-m"></i> + <span class="target-text">Target : </span> + <strong id="release-new-target-name"> {{.Release.Target}}</strong> + </button> + <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> + <span class="caret"></span> + </button> + <div class="dropdown-menu clone-group-btn" id="release-new-target-branch-list"> + <ul class="list-group"> + {{range .Branches}} + <li class="list-group-item"> + <a href="#" rel="{{.}}"><i class="fa fa-code-fork"></i>{{.}}</a> + </li> + {{end}} + </ul> + </div> + <input id="tag-target" type="hidden" name="tag_target" value="{{.Release.Target}}"/> + </div> + <p class="help-block">Choose an existing tag, or create a new tag on publish</p> + </div> + <div class="form-group" style="display: block"> + <input class="form-control input-lg" id="release-new-title" name="title" type="text" placeholder="release title" value="{{.Release.Title}}" /> + </div> + <div class="form-group col-md-8" style="display: block" id="release-new-content-div"> + <div class="md-help pull-right"> + Content with <a href="https://help.github.com/articles/markdown-basics">Markdown</a> + </div> + <ul class="nav nav-tabs" data-init="tabs"> + <li class="release-write active"><a href="#release-textarea" data-toggle="tab">Write</a></li> + <li class="release-preview"><a href="#release-preview" data-toggle="tab" data-ajax="/api/v1/markdown" data-ajax-name="release-preview" data-ajax-context="{{.RepoLink}}" data-ajax-method="post" data-preview="#release-preview">Preview</a></li> + </ul> + <div class="tab-content"> + <div class="tab-pane active" id="release-textarea"> + <div class="form-group"> + <textarea class="form-control" name="content" id="release-new-content" rows="10" placeholder="Write some content" data-ajax-rel="release-preview" data-ajax-val="val" data-ajax-field="text">{{.Release.Note}}</textarea> + </div> + </div> + <div class="tab-pane release-preview-content" id="release-preview">loading...</div> + </div> + </div> + <div class="text-right form-group col-md-8" style="display: block"> + <hr/> + <label for="release-new-pre-release"> + <input id="release-new-pre-release" type="checkbox" name="prerelease" {{if .Release.IsPrerelease}}checked{{end}}/> + <strong>This is a pre-release</strong> + </label> + <p class="help-block">We’ll point out that this release is identified as non-production ready.</p> + </div> + <div class="text-right form-group col-md-8" style="display: block"> + <button class="btn-success btn">Publish release</button> + <input class="btn btn-default" type="submit" name="draft" value="Save draft"/> + </div> + </form> + </div> +</div> +{{template "base/footer" .}}
\ No newline at end of file diff --git a/templates/release/list.tmpl b/templates/release/list.tmpl index 2607dfacd9..0f02508fb9 100644 --- a/templates/release/list.tmpl +++ b/templates/release/list.tmpl @@ -14,17 +14,23 @@ <li class="release-item clearfix" id="release-{{.Sha1}}"> {{if .PublisherId}} <div class="col-md-2 text-right"> - {{if .IsPrerelease}}<span class="btn btn-warning status pre-release">Pre-Release</span>{{else}}<span class="btn btn-success status stable">Stable</span>{{end}} + {{if .IsDraft}} + <span class="btn btn-primary status pre-release">Draft</span> + {{else if .IsPrerelease}} + <span class="btn btn-warning status pre-release">Pre-Release</span> + {{else}} + <span class="btn btn-success status stable">Stable</span> + {{end}} <a class="tag" href="{{$.RepoLink}}/src/{{.TagName}}" rel="nofollow"><i class="fa fa-tag"></i>{{.TagName}}</a> <a class="commit" href="{{$.RepoLink}}/src/{{.Sha1}}" rel="nofollow"><i class="fa fa-code"></i>{{ShortSha .Sha1}}</a> </div> <div class="col-md-10"> - <h4 class="title"><a href="{{$.RepoLink}}/src/{{.TagName}}">{{.Title}}</a></h4> + <h4 class="title"><a href="{{$.RepoLink}}/src/{{.TagName}}">{{.Title}}</a> <small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName}}" rel="nofollow">edit</a>)</small></h4> <p class="info"> <span class="author"><img class="avatar" src="{{.Publisher.AvatarLink}}" alt="" width="20"> <a href="/user/{{.Publisher.Name}}">{{.Publisher.Name}}</a></span> {{if .Created}}<span class="time">{{TimeSince .Created}}</span>{{end}} - <span class="ahead"><strong>{{.NumCommitsBehind}}</strong> commits since this release</span> + <span class="ahead"><strong>{{.NumCommitsBehind}}</strong> commits to {{.Target}} since this release</span> </p> <div class="markdown desc"> {{str2html .Note}} diff --git a/templates/release/new.tmpl b/templates/release/new.tmpl index 06cb28c78d..6c5cf40ceb 100644 --- a/templates/release/new.tmpl +++ b/templates/release/new.tmpl @@ -62,7 +62,7 @@ </div> <div class="text-right form-group col-md-8" style="display: block"> <button class="btn-success btn">Publish release</button> - <!-- <input class="btn btn-default" type="submit" name="draft" value="Save Draft"/> --> + <input class="btn btn-default" type="submit" name="draft" value="Save draft"/> </div> </form> </div> |