diff --git a/cmd/droned/assets/css/drone.css b/cmd/droned/assets/css/drone.css index 71fcae13b..7dfec911c 100644 --- a/cmd/droned/assets/css/drone.css +++ b/cmd/droned/assets/css/drone.css @@ -870,6 +870,14 @@ pre { font-size: 22px !IMPORTANT; line-height: 32px !IMPORTANT; } +.alert.alert-build-Success .actions, +.alert.alert-build-Error .actions, +.alert.alert-build-Failure .actions, +.alert.alert-build-Pending .actions, +.alert.alert-build-Started .actions { + float: right; + margin-top: -2px; +} .build-details { background: #FFF; margin-bottom: 40px; diff --git a/cmd/droned/assets/css/drone.less b/cmd/droned/assets/css/drone.less index 7d3fbe684..61f5484e9 100644 --- a/cmd/droned/assets/css/drone.less +++ b/cmd/droned/assets/css/drone.less @@ -184,7 +184,7 @@ body { border:0px; } -// +// // nav-repos // -------------------------------------------- @@ -364,7 +364,7 @@ body { margin-bottom: 10px; } -// +// // build list // -------------------------------------------- @@ -493,7 +493,7 @@ body { .btn.btn-Success { background:rgba(81, 163, 81, 0.75); } -.btn.btn-failure, +.btn.btn-failure, .btn.btn-Failure, .btn.btn-Error { background:rgba(189, 54, 47, 0.8); @@ -911,7 +911,7 @@ textarea { top: 6px; transition: all .15s ease; width: 35px; - z-index: 3; + z-index: 3; border-radius:5px; } @@ -1021,6 +1021,11 @@ pre { line-height: 32px !IMPORTANT; } } + + .actions { + float: right; + margin-top: -2px; + } } .build-details { @@ -1285,5 +1290,3 @@ pre { background: #999; cursor: pointer; } - - diff --git a/cmd/droned/drone.go b/cmd/droned/drone.go index b5641af8c..cea951b6a 100644 --- a/cmd/droned/drone.go +++ b/cmd/droned/drone.go @@ -136,6 +136,7 @@ func setupHandlers() { github = handler.NewGithubHandler(queue) gitlab = handler.NewGitlabHandler(queue) bitbucket = handler.NewBitbucketHandler(queue) + rebuild = handler.NewCommitRebuildHandler(queue) ) m := pat.New() @@ -226,7 +227,9 @@ func setupHandlers() { // handlers for repository, commits and build details m.Get("/:host/:owner/:name/commit/:commit/build/:label/out.txt", handler.RepoHandler(handler.BuildOut)) + m.Post("/:host/:owner/:name/commit/:commit/build/:label/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild)) m.Get("/:host/:owner/:name/commit/:commit/build/:label", handler.RepoHandler(handler.CommitShow)) + m.Post("/:host/:owner/:name/commit/:commit/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild)) m.Get("/:host/:owner/:name/commit/:commit", handler.RepoHandler(handler.CommitShow)) m.Get("/:host/:owner/:name/tree", handler.RepoHandler(handler.RepoDashboard)) m.Get("/:host/:owner/:name/status.svg", handler.ErrorHandler(handler.Badge)) diff --git a/pkg/database/repos.go b/pkg/database/repos.go index 0d9e2f15e..a344a3d67 100644 --- a/pkg/database/repos.go +++ b/pkg/database/repos.go @@ -90,3 +90,18 @@ func ListReposTeam(id int64) ([]*Repo, error) { err := meddler.QueryAll(db, &repos, repoTeamStmt, id) return repos, err } + +// Checks whether a user is admin of a repo +// Returns true if user owns repo or is on team that owns repo +// Returns true if the user is an admin member of the team. +func IsRepoAdmin(user *User, repo *Repo) (bool, error) { + if user == nil { + return false, nil + } + + if user.ID == repo.UserID { + return true, nil + } + + return IsMemberAdmin(user.ID, repo.TeamID) +} diff --git a/pkg/handler/commits.go b/pkg/handler/commits.go index c41620038..aad1120b8 100644 --- a/pkg/handler/commits.go +++ b/pkg/handler/commits.go @@ -8,6 +8,7 @@ import ( "github.com/drone/drone/pkg/channel" "github.com/drone/drone/pkg/database" . "github.com/drone/drone/pkg/model" + "github.com/drone/drone/pkg/queue" ) // Display a specific Commit. @@ -33,14 +34,20 @@ func CommitShow(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) err return err } + admin, err := database.IsRepoAdmin(u, repo) + if err != nil { + return err + } + data := struct { - User *User - Repo *Repo - Commit *Commit - Build *Build - Builds []*Build - Token string - }{u, repo, commit, builds[0], builds, ""} + User *User + Repo *Repo + Commit *Commit + Build *Build + Builds []*Build + Token string + IsAdmin bool + }{u, repo, commit, builds[0], builds, "", admin} // get the specific build requested by the user. instead // of a database round trip, we can just loop through the @@ -94,3 +101,66 @@ func saveFailedBuild(commit *Commit, msg string) error { return nil } + +type CommitRebuildHandler struct { + queue *queue.Queue +} + +func NewCommitRebuildHandler(queue *queue.Queue) *CommitRebuildHandler { + return &CommitRebuildHandler{ + queue: queue, + } +} + +// CommitRebuild re-queues a previously built commit. It finds the existing +// commit and build and injects them back into the queue. If the commit +// doesn't exist or has no builds, or if a build label has been passed but +// can't be located, it prints an error. Otherwise, it adds the build/commit +// to the queue and redirects back to the commit page. +func (h *CommitRebuildHandler) CommitRebuild(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) error { + hash := r.FormValue(":commit") + labl := r.FormValue(":label") + host := r.FormValue(":host") + + // get the commit from the database + commit, err := database.GetCommitHash(hash, repo.ID) + if err != nil { + return err + } + + // get the builds from the database. a commit can have + // multiple sub-builds (or matrix builds) + builds, err := database.ListBuilds(commit.ID) + if err != nil { + return err + } + + build := builds[0] + + if labl != "" { + // get the specific build requested by the user. instead + // of a database round trip, we can just loop through the + // list and extract the requested build. + build = nil + for _, b := range builds { + if b.Slug == labl { + build = b + break + } + } + } + + if build == nil { + return fmt.Errorf("Could not find build: %s", labl) + } + + h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build}) + + if labl != "" { + http.Redirect(w, r, fmt.Sprintf("/%s/%s/%s/commit/%s/build/%s", host, repo.Owner, repo.Name, hash, labl), http.StatusSeeOther) + } else { + http.Redirect(w, r, fmt.Sprintf("/%s/%s/%s/commit/%s", host, repo.Owner, repo.Name, hash), http.StatusSeeOther) + } + + return nil +} diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 2af9150c9..40de6fd3c 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -142,11 +142,9 @@ func (h RepoAdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // The User must own the repository OR be a member // of the Team that owns the repository. - if user.ID != repo.UserID { - if admin, _ := database.IsMemberAdmin(user.ID, repo.TeamID); admin == false { - RenderNotFound(w) - return - } + if admin, _ := database.IsRepoAdmin(user, repo); admin == false { + RenderNotFound(w) + return } if err = h(w, r, user, repo); err != nil { diff --git a/pkg/template/pages/repo_commit.html b/pkg/template/pages/repo_commit.html index 2dfd38b34..8a743f6f2 100644 --- a/pkg/template/pages/repo_commit.html +++ b/pkg/template/pages/repo_commit.html @@ -24,6 +24,18 @@ {{ else }} commit {{ .Commit.HashShort }} to {{.Commit.Branch}} branch {{ end }} + +