mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-10-20 23:37:31 +00:00
bump to 0.5 in master
This commit is contained in:
82
web/badge.go
82
web/badge.go
@@ -1,82 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/store"
|
||||
)
|
||||
|
||||
var (
|
||||
badgeSuccess = `<svg xmlns="http://www.w3.org/2000/svg" width="91" height="20"><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><rect rx="3" width="91" height="20" fill="#555"/><rect rx="3" x="37" width="54" height="20" fill="#4c1"/><path fill="#4c1" d="M37 0h4v20h-4z"/><rect rx="3" width="91" height="20" fill="url(#a)"/><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="19.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="19.5" y="14">build</text><text x="63" y="15" fill="#010101" fill-opacity=".3">success</text><text x="63" y="14">success</text></g></svg>`
|
||||
badgeFailure = `<svg xmlns="http://www.w3.org/2000/svg" width="83" height="20"><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><rect rx="3" width="83" height="20" fill="#555"/><rect rx="3" x="37" width="46" height="20" fill="#e05d44"/><path fill="#e05d44" d="M37 0h4v20h-4z"/><rect rx="3" width="83" height="20" fill="url(#a)"/><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="19.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="19.5" y="14">build</text><text x="59" y="15" fill="#010101" fill-opacity=".3">failure</text><text x="59" y="14">failure</text></g></svg>`
|
||||
badgeStarted = `<svg xmlns="http://www.w3.org/2000/svg" width="87" height="20"><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><rect rx="3" width="87" height="20" fill="#555"/><rect rx="3" x="37" width="50" height="20" fill="#dfb317"/><path fill="#dfb317" d="M37 0h4v20h-4z"/><rect rx="3" width="87" height="20" fill="url(#a)"/><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="19.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="19.5" y="14">build</text><text x="61" y="15" fill="#010101" fill-opacity=".3">started</text><text x="61" y="14">started</text></g></svg>`
|
||||
badgeError = `<svg xmlns="http://www.w3.org/2000/svg" width="76" height="20"><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><rect rx="3" width="76" height="20" fill="#555"/><rect rx="3" x="37" width="39" height="20" fill="#9f9f9f"/><path fill="#9f9f9f" d="M37 0h4v20h-4z"/><rect rx="3" width="76" height="20" fill="url(#a)"/><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="19.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="19.5" y="14">build</text><text x="55.5" y="15" fill="#010101" fill-opacity=".3">error</text><text x="55.5" y="14">error</text></g></svg>`
|
||||
badgeNone = `<svg xmlns="http://www.w3.org/2000/svg" width="75" height="20"><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><rect rx="3" width="75" height="20" fill="#555"/><rect rx="3" x="37" width="38" height="20" fill="#9f9f9f"/><path fill="#9f9f9f" d="M37 0h4v20h-4z"/><rect rx="3" width="75" height="20" fill="url(#a)"/><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="19.5" y="15" fill="#010101" fill-opacity=".3">build</text><text x="19.5" y="14">build</text><text x="55" y="15" fill="#010101" fill-opacity=".3">none</text><text x="55" y="14">none</text></g></svg>`
|
||||
)
|
||||
|
||||
func GetBadge(c *gin.Context) {
|
||||
repo, err := store.GetRepoOwnerName(c,
|
||||
c.Param("owner"),
|
||||
c.Param("name"),
|
||||
)
|
||||
if err != nil {
|
||||
c.AbortWithStatus(404)
|
||||
return
|
||||
}
|
||||
|
||||
// an SVG response is always served, even when error, so
|
||||
// we can go ahead and set the content type appropriately.
|
||||
c.Writer.Header().Set("Content-Type", "image/svg+xml")
|
||||
|
||||
// if no commit was found then display
|
||||
// the 'none' badge, instead of throwing
|
||||
// an error response
|
||||
branch := c.Query("branch")
|
||||
if len(branch) == 0 {
|
||||
branch = repo.Branch
|
||||
}
|
||||
|
||||
build, err := store.GetBuildLast(c, repo, branch)
|
||||
if err != nil {
|
||||
c.String(404, badgeNone)
|
||||
return
|
||||
}
|
||||
|
||||
switch build.Status {
|
||||
case model.StatusSuccess:
|
||||
c.String(200, badgeSuccess)
|
||||
case model.StatusFailure:
|
||||
c.String(200, badgeFailure)
|
||||
case model.StatusError, model.StatusKilled:
|
||||
c.String(200, badgeError)
|
||||
case model.StatusPending, model.StatusRunning:
|
||||
c.String(200, badgeStarted)
|
||||
default:
|
||||
c.String(404, badgeNone)
|
||||
}
|
||||
}
|
||||
|
||||
func GetCC(c *gin.Context) {
|
||||
repo, err := store.GetRepoOwnerName(c,
|
||||
c.Param("owner"),
|
||||
c.Param("name"),
|
||||
)
|
||||
if err != nil {
|
||||
c.AbortWithStatus(404)
|
||||
return
|
||||
}
|
||||
|
||||
builds, err := store.GetBuildList(c, repo)
|
||||
if err != nil || len(builds) == 0 {
|
||||
c.AbortWithStatus(404)
|
||||
return
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%d", httputil.GetURL(c.Request), repo.FullName, builds[0].Number)
|
||||
cc := model.NewCC(repo, builds[0], url)
|
||||
c.XML(200, cc)
|
||||
}
|
100
web/gitlab.go
100
web/gitlab.go
@@ -1,100 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/drone/drone/router/middleware/session"
|
||||
"github.com/drone/drone/shared/token"
|
||||
"github.com/drone/drone/store"
|
||||
)
|
||||
|
||||
func GetCommit(c *gin.Context) {
|
||||
repo := session.Repo(c)
|
||||
|
||||
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||
return repo.Hash, nil
|
||||
})
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if parsed.Text != repo.FullName {
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
commit := c.Param("sha")
|
||||
branch := c.Query("branch")
|
||||
if len(branch) == 0 {
|
||||
branch = repo.Branch
|
||||
}
|
||||
|
||||
build, err := store.GetBuildCommit(c, repo, commit, branch)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, build)
|
||||
}
|
||||
|
||||
func GetPullRequest(c *gin.Context) {
|
||||
repo := session.Repo(c)
|
||||
refs := fmt.Sprintf("refs/pull/%s/head", c.Param("number"))
|
||||
|
||||
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||
return repo.Hash, nil
|
||||
})
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if parsed.Text != repo.FullName {
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
build, err := store.GetBuildRef(c, repo, refs)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, build)
|
||||
}
|
||||
|
||||
func RedirectSha(c *gin.Context) {
|
||||
repo := session.Repo(c)
|
||||
|
||||
commit := c.Param("sha")
|
||||
branch := c.Query("branch")
|
||||
if len(branch) == 0 {
|
||||
branch = repo.Branch
|
||||
}
|
||||
|
||||
build, err := store.GetBuildCommit(c, repo, commit, branch)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/%s/%s/%d", repo.Owner, repo.Name, build.Number)
|
||||
c.Redirect(http.StatusSeeOther, path)
|
||||
}
|
||||
|
||||
func RedirectPullRequest(c *gin.Context) {
|
||||
repo := session.Repo(c)
|
||||
refs := fmt.Sprintf("refs/pull/%s/head", c.Param("number"))
|
||||
|
||||
build, err := store.GetBuildRef(c, repo, refs)
|
||||
if err != nil {
|
||||
c.AbortWithError(http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/%s/%s/%d", repo.Owner, repo.Name, build.Number)
|
||||
c.Redirect(http.StatusSeeOther, path)
|
||||
}
|
248
web/hook.go
248
web/hook.go
@@ -1,248 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/square/go-jose"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/drone/drone/bus"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/queue"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/token"
|
||||
"github.com/drone/drone/store"
|
||||
"github.com/drone/drone/yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
droneYml = os.Getenv("BUILD_CONFIG_FILE")
|
||||
droneSec string
|
||||
)
|
||||
|
||||
func init() {
|
||||
if droneYml == "" {
|
||||
droneYml = ".drone.yml"
|
||||
}
|
||||
droneSec = fmt.Sprintf("%s.sig", droneYml)
|
||||
}
|
||||
|
||||
var skipRe = regexp.MustCompile(`\[(?i:ci *skip|skip *ci)\]`)
|
||||
|
||||
func PostHook(c *gin.Context) {
|
||||
remote_ := remote.FromContext(c)
|
||||
|
||||
tmprepo, build, err := remote_.Hook(c.Request)
|
||||
if err != nil {
|
||||
log.Errorf("failure to parse hook. %s", err)
|
||||
c.AbortWithError(400, err)
|
||||
return
|
||||
}
|
||||
if build == nil {
|
||||
c.Writer.WriteHeader(200)
|
||||
return
|
||||
}
|
||||
if tmprepo == nil {
|
||||
log.Errorf("failure to ascertain repo from hook.")
|
||||
c.Writer.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
|
||||
// skip the build if any case-insensitive combination of the words "skip" and "ci"
|
||||
// wrapped in square brackets appear in the commit message
|
||||
skipMatch := skipRe.FindString(build.Message)
|
||||
if len(skipMatch) > 0 {
|
||||
log.Infof("ignoring hook. %s found in %s", skipMatch, build.Commit)
|
||||
c.Writer.WriteHeader(204)
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := store.GetRepoOwnerName(c, tmprepo.Owner, tmprepo.Name)
|
||||
if err != nil {
|
||||
log.Errorf("failure to find repo %s/%s from hook. %s", tmprepo.Owner, tmprepo.Name, err)
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
// get the token and verify the hook is authorized
|
||||
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||
return repo.Hash, nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failure to parse token from hook for %s. %s", repo.FullName, err)
|
||||
c.AbortWithError(400, err)
|
||||
return
|
||||
}
|
||||
if parsed.Text != repo.FullName {
|
||||
log.Errorf("failure to verify token from hook. Expected %s, got %s", repo.FullName, parsed.Text)
|
||||
c.AbortWithStatus(403)
|
||||
return
|
||||
}
|
||||
|
||||
if repo.UserID == 0 {
|
||||
log.Warnf("ignoring hook. repo %s has no owner.", repo.FullName)
|
||||
c.Writer.WriteHeader(204)
|
||||
return
|
||||
}
|
||||
var skipped = true
|
||||
if (build.Event == model.EventPush && repo.AllowPush) ||
|
||||
(build.Event == model.EventPull && repo.AllowPull) ||
|
||||
(build.Event == model.EventDeploy && repo.AllowDeploy) ||
|
||||
(build.Event == model.EventTag && repo.AllowTag) {
|
||||
skipped = false
|
||||
}
|
||||
|
||||
if skipped {
|
||||
log.Infof("ignoring hook. repo %s is disabled for %s events.", repo.FullName, build.Event)
|
||||
c.Writer.WriteHeader(204)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := store.GetUser(c, repo.UserID)
|
||||
if err != nil {
|
||||
log.Errorf("failure to find repo owner %s. %s", repo.FullName, err)
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
// if there is no email address associated with the pull request,
|
||||
// we lookup the email address based on the authors github login.
|
||||
//
|
||||
// my initial hesitation with this code is that it has the ability
|
||||
// to expose your email address. At the same time, your email address
|
||||
// is already exposed in the public .git log. So while some people will
|
||||
// a small number of people will probably be upset by this, I'm not sure
|
||||
// it is actually that big of a deal.
|
||||
if len(build.Email) == 0 {
|
||||
author, err := store.GetUserLogin(c, build.Author)
|
||||
if err == nil {
|
||||
build.Email = author.Email
|
||||
}
|
||||
}
|
||||
|
||||
// if the remote has a refresh token, the current access token
|
||||
// may be stale. Therefore, we should refresh prior to dispatching
|
||||
// the job.
|
||||
if refresher, ok := remote_.(remote.Refresher); ok {
|
||||
ok, _ := refresher.Refresh(user)
|
||||
if ok {
|
||||
store.UpdateUser(c, user)
|
||||
}
|
||||
}
|
||||
|
||||
// fetch the build file from the database
|
||||
raw, err := remote_.File(user, repo, build, droneYml)
|
||||
if err != nil {
|
||||
log.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
sec, err := remote_.File(user, repo, build, droneSec)
|
||||
if err != nil {
|
||||
log.Debugf("cannot find build secrets for %s. %s", repo.FullName, err)
|
||||
// NOTE we don't exit on failure. The sec file is optional
|
||||
}
|
||||
|
||||
axes, err := yaml.ParseMatrix(raw)
|
||||
if err != nil {
|
||||
c.String(500, "Failed to parse yaml file or calculate matrix. %s", err)
|
||||
return
|
||||
}
|
||||
if len(axes) == 0 {
|
||||
axes = append(axes, yaml.Axis{})
|
||||
}
|
||||
|
||||
netrc, err := remote_.Netrc(user, repo)
|
||||
if err != nil {
|
||||
c.String(500, "Failed to generate netrc file. %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// verify the branches can be built vs skipped
|
||||
branches := yaml.ParseBranch(raw)
|
||||
if !branches.Matches(build.Branch) && build.Event != model.EventTag && build.Event != model.EventDeploy {
|
||||
c.String(200, "Branch does not match restrictions defined in yaml")
|
||||
return
|
||||
}
|
||||
|
||||
// update some build fields
|
||||
build.Status = model.StatusPending
|
||||
build.RepoID = repo.ID
|
||||
|
||||
// and use a transaction
|
||||
var jobs []*model.Job
|
||||
for num, axis := range axes {
|
||||
jobs = append(jobs, &model.Job{
|
||||
BuildID: build.ID,
|
||||
Number: num + 1,
|
||||
Status: model.StatusPending,
|
||||
Environment: axis,
|
||||
})
|
||||
}
|
||||
err = store.CreateBuild(c, build, jobs...)
|
||||
if err != nil {
|
||||
log.Errorf("failure to save commit for %s. %s", repo.FullName, err)
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, build)
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%d", httputil.GetURL(c.Request), repo.FullName, build.Number)
|
||||
err = remote_.Status(user, repo, build, url)
|
||||
if err != nil {
|
||||
log.Errorf("error setting commit status for %s/%d", repo.FullName, build.Number)
|
||||
}
|
||||
|
||||
// get the previous build so that we can send
|
||||
// on status change notifications
|
||||
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||
secs, err := store.GetSecretList(c, repo)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
||||
}
|
||||
|
||||
var signed bool
|
||||
var verified bool
|
||||
|
||||
signature, err := jose.ParseSigned(string(sec))
|
||||
if err != nil {
|
||||
log.Debugf("cannot parse .drone.yml.sig file. %s", err)
|
||||
} else if len(sec) == 0 {
|
||||
log.Debugf("cannot parse .drone.yml.sig file. empty file")
|
||||
} else {
|
||||
signed = true
|
||||
output, err := signature.Verify([]byte(repo.Hash))
|
||||
if err != nil {
|
||||
log.Debugf("cannot verify .drone.yml.sig file. %s", err)
|
||||
} else if string(output) != string(raw) {
|
||||
log.Debugf("cannot verify .drone.yml.sig file. no match")
|
||||
} else {
|
||||
verified = true
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf(".drone.yml is signed=%v and verified=%v", signed, verified)
|
||||
|
||||
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
||||
for _, job := range jobs {
|
||||
queue.Publish(c, &queue.Work{
|
||||
Signed: signed,
|
||||
Verified: verified,
|
||||
User: user,
|
||||
Repo: repo,
|
||||
Build: build,
|
||||
BuildLast: last,
|
||||
Job: job,
|
||||
Netrc: netrc,
|
||||
Yaml: string(raw),
|
||||
Secrets: secs,
|
||||
System: &model.System{Link: httputil.GetURL(c.Request)},
|
||||
})
|
||||
}
|
||||
|
||||
}
|
149
web/login.go
149
web/login.go
@@ -1,149 +0,0 @@
|
||||
package web
|
||||
|
||||
//
|
||||
// import (
|
||||
// "net/http"
|
||||
// "time"
|
||||
//
|
||||
// "github.com/drone/drone/model"
|
||||
// "github.com/drone/drone/remote"
|
||||
// "github.com/drone/drone/shared/crypto"
|
||||
// "github.com/drone/drone/shared/httputil"
|
||||
// "github.com/drone/drone/shared/token"
|
||||
// "github.com/drone/drone/store"
|
||||
//
|
||||
// "github.com/Sirupsen/logrus"
|
||||
// "github.com/gin-gonic/gin"
|
||||
// )
|
||||
//
|
||||
// func GetLogin(c *gin.Context) {
|
||||
// remote := remote.FromContext(c)
|
||||
//
|
||||
// // when dealing with redirects we may need
|
||||
// // to adjust the content type. I cannot, however,
|
||||
// // remember why, so need to revisit this line.
|
||||
// c.Writer.Header().Del("Content-Type")
|
||||
//
|
||||
// tmpuser, err := remote.Login(c.Writer, c.Request)
|
||||
// if err != nil {
|
||||
// logrus.Errorf("cannot authenticate user. %s", err)
|
||||
// c.Redirect(303, "/login?error=oauth_error")
|
||||
// return
|
||||
// }
|
||||
// // this will happen when the user is redirected by
|
||||
// // the remote provide as part of the oauth dance.
|
||||
// if tmpuser == nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// var open = false // TODO get this from context
|
||||
//
|
||||
// // get the user from the database
|
||||
// u, err := store.GetUserLogin(c, tmpuser.Login)
|
||||
// if err != nil {
|
||||
//
|
||||
// // if self-registration is disabled we should
|
||||
// // return a notAuthorized error. the only exception
|
||||
// // is if no users exist yet in the system we'll proceed.
|
||||
// if !open {
|
||||
// logrus.Errorf("cannot register %s. registration closed", tmpuser.Login)
|
||||
// c.Redirect(303, "/login?error=access_denied")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // create the user account
|
||||
// u = &model.User{}
|
||||
// u.Login = tmpuser.Login
|
||||
// u.Token = tmpuser.Token
|
||||
// u.Secret = tmpuser.Secret
|
||||
// u.Email = tmpuser.Email
|
||||
// u.Avatar = tmpuser.Avatar
|
||||
// u.Hash = crypto.Rand()
|
||||
//
|
||||
// // insert the user into the database
|
||||
// if err := store.CreateUser(c, u); err != nil {
|
||||
// logrus.Errorf("cannot insert %s. %s", u.Login, err)
|
||||
// c.Redirect(303, "/login?error=internal_error")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // update the user meta data and authorization
|
||||
// // data and cache in the datastore.
|
||||
// u.Token = tmpuser.Token
|
||||
// u.Secret = tmpuser.Secret
|
||||
// u.Email = tmpuser.Email
|
||||
// u.Avatar = tmpuser.Avatar
|
||||
//
|
||||
// if err := store.UpdateUser(c, u); err != nil {
|
||||
// logrus.Errorf("cannot update %s. %s", u.Login, err)
|
||||
// c.Redirect(303, "/login?error=internal_error")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// exp := time.Now().Add(time.Hour * 72).Unix()
|
||||
// token := token.New(token.SessToken, u.Login)
|
||||
// tokenstr, err := token.SignExpires(u.Hash, exp)
|
||||
// if err != nil {
|
||||
// logrus.Errorf("cannot create token for %s. %s", u.Login, err)
|
||||
// c.Redirect(303, "/login?error=internal_error")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr)
|
||||
// redirect := httputil.GetCookie(c.Request, "user_last")
|
||||
// if len(redirect) == 0 {
|
||||
// redirect = "/"
|
||||
// }
|
||||
// c.Redirect(303, redirect)
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func GetLogout(c *gin.Context) {
|
||||
//
|
||||
// httputil.DelCookie(c.Writer, c.Request, "user_sess")
|
||||
// httputil.DelCookie(c.Writer, c.Request, "user_last")
|
||||
// c.Redirect(303, "/login")
|
||||
// }
|
||||
//
|
||||
// func GetLoginToken(c *gin.Context) {
|
||||
// remote := remote.FromContext(c)
|
||||
//
|
||||
// in := &tokenPayload{}
|
||||
// err := c.Bind(in)
|
||||
// if err != nil {
|
||||
// c.AbortWithError(http.StatusBadRequest, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// login, err := remote.Auth(in.Access, in.Refresh)
|
||||
// if err != nil {
|
||||
// c.AbortWithError(http.StatusUnauthorized, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// user, err := store.GetUserLogin(c, login)
|
||||
// if err != nil {
|
||||
// c.AbortWithError(http.StatusNotFound, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// exp := time.Now().Add(time.Hour * 72).Unix()
|
||||
// token := token.New(token.SessToken, user.Login)
|
||||
// tokenstr, err := token.SignExpires(user.Hash, exp)
|
||||
// if err != nil {
|
||||
// c.AbortWithError(http.StatusInternalServerError, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// c.IndentedJSON(http.StatusOK, &tokenPayload{
|
||||
// Access: tokenstr,
|
||||
// Expires: exp - time.Now().Unix(),
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// type tokenPayload struct {
|
||||
// Access string `json:"access_token,omitempty"`
|
||||
// Refresh string `json:"refresh_token,omitempty"`
|
||||
// Expires int64 `json:"expires_in,omitempty"`
|
||||
// }
|
207
web/pages.go
207
web/pages.go
@@ -1,207 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/drone/drone/cache"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/router/middleware/session"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/token"
|
||||
"github.com/drone/drone/store"
|
||||
)
|
||||
|
||||
func ShowIndex(c *gin.Context) {
|
||||
user := session.User(c)
|
||||
if user == nil {
|
||||
c.Redirect(http.StatusSeeOther, "/login")
|
||||
return
|
||||
}
|
||||
|
||||
// get the repository list from the cache
|
||||
repos, err := cache.GetRepos(c, user)
|
||||
if err != nil {
|
||||
c.String(400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// filter to only show the currently active ones
|
||||
activeRepos, err := store.GetRepoListOf(c, repos)
|
||||
if err != nil {
|
||||
c.String(400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.HTML(200, "index.html", gin.H{
|
||||
"User": user,
|
||||
"Repos": activeRepos,
|
||||
})
|
||||
}
|
||||
|
||||
func ShowAllRepos(c *gin.Context) {
|
||||
user := session.User(c)
|
||||
if user == nil {
|
||||
c.Redirect(http.StatusSeeOther, "/login")
|
||||
return
|
||||
}
|
||||
|
||||
// get the repository list from the cache
|
||||
repos, err := cache.GetRepos(c, user)
|
||||
if err != nil {
|
||||
c.String(400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.HTML(200, "repos.html", gin.H{
|
||||
"User": user,
|
||||
"Repos": repos,
|
||||
})
|
||||
}
|
||||
|
||||
func ShowLogin(c *gin.Context) {
|
||||
c.HTML(200, "login.html", gin.H{"Error": c.Query("error")})
|
||||
}
|
||||
|
||||
func ShowLoginForm(c *gin.Context) {
|
||||
c.HTML(200, "login_form.html", gin.H{})
|
||||
}
|
||||
|
||||
func ShowUser(c *gin.Context) {
|
||||
user := session.User(c)
|
||||
token, _ := token.New(
|
||||
token.CsrfToken,
|
||||
user.Login,
|
||||
).Sign(user.Hash)
|
||||
|
||||
c.HTML(200, "user.html", gin.H{
|
||||
"User": user,
|
||||
"Csrf": token,
|
||||
})
|
||||
}
|
||||
|
||||
func ShowRepo(c *gin.Context) {
|
||||
user := session.User(c)
|
||||
repo := session.Repo(c)
|
||||
|
||||
builds, _ := store.GetBuildList(c, repo)
|
||||
groups := []*model.BuildGroup{}
|
||||
|
||||
var curr *model.BuildGroup
|
||||
for _, build := range builds {
|
||||
date := time.Unix(build.Created, 0).Format("Jan 2 2006")
|
||||
if curr == nil || curr.Date != date {
|
||||
curr = &model.BuildGroup{}
|
||||
curr.Date = date
|
||||
groups = append(groups, curr)
|
||||
}
|
||||
curr.Builds = append(curr.Builds, build)
|
||||
}
|
||||
|
||||
httputil.SetCookie(c.Writer, c.Request, "user_last", repo.FullName)
|
||||
|
||||
c.HTML(200, "repo.html", gin.H{
|
||||
"User": user,
|
||||
"Repo": repo,
|
||||
"Builds": builds,
|
||||
"Groups": groups,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func ShowRepoConf(c *gin.Context) {
|
||||
|
||||
user := session.User(c)
|
||||
repo := session.Repo(c)
|
||||
|
||||
token, _ := token.New(
|
||||
token.CsrfToken,
|
||||
user.Login,
|
||||
).Sign(user.Hash)
|
||||
|
||||
c.HTML(200, "repo_config.html", gin.H{
|
||||
"User": user,
|
||||
"Repo": repo,
|
||||
"Csrf": token,
|
||||
"Link": httputil.GetURL(c.Request),
|
||||
})
|
||||
}
|
||||
|
||||
func ShowRepoEncrypt(c *gin.Context) {
|
||||
user := session.User(c)
|
||||
repo := session.Repo(c)
|
||||
|
||||
token, _ := token.New(
|
||||
token.CsrfToken,
|
||||
user.Login,
|
||||
).Sign(user.Hash)
|
||||
|
||||
c.HTML(200, "repo_secret.html", gin.H{
|
||||
"User": user,
|
||||
"Repo": repo,
|
||||
"Csrf": token,
|
||||
})
|
||||
}
|
||||
|
||||
func ShowRepoBadges(c *gin.Context) {
|
||||
user := session.User(c)
|
||||
repo := session.Repo(c)
|
||||
|
||||
c.HTML(200, "repo_badge.html", gin.H{
|
||||
"User": user,
|
||||
"Repo": repo,
|
||||
"Link": httputil.GetURL(c.Request),
|
||||
})
|
||||
}
|
||||
|
||||
func ShowBuild(c *gin.Context) {
|
||||
user := session.User(c)
|
||||
repo := session.Repo(c)
|
||||
num, _ := strconv.Atoi(c.Param("number"))
|
||||
seq, _ := strconv.Atoi(c.Param("job"))
|
||||
if seq == 0 {
|
||||
seq = 1
|
||||
}
|
||||
|
||||
build, err := store.GetBuildNumber(c, repo, num)
|
||||
if err != nil {
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
jobs, err := store.GetJobList(c, build)
|
||||
if err != nil {
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
var job *model.Job
|
||||
for _, j := range jobs {
|
||||
if j.Number == seq {
|
||||
job = j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
httputil.SetCookie(c.Writer, c.Request, "user_last", repo.FullName)
|
||||
|
||||
var csrf string
|
||||
if user != nil {
|
||||
csrf, _ = token.New(
|
||||
token.CsrfToken,
|
||||
user.Login,
|
||||
).Sign(user.Hash)
|
||||
}
|
||||
|
||||
c.HTML(200, "build.html", gin.H{
|
||||
"User": user,
|
||||
"Repo": repo,
|
||||
"Build": build,
|
||||
"Jobs": jobs,
|
||||
"Job": job,
|
||||
"Csrf": csrf,
|
||||
})
|
||||
}
|
113
web/slack.go
113
web/slack.go
@@ -1,113 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/store"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
slashDeploy = "deploy"
|
||||
slashRestart = "restart"
|
||||
slashStatus = "status"
|
||||
)
|
||||
|
||||
// Slack is handler function that handles Slack slash commands.
|
||||
func Slack(c *gin.Context) {
|
||||
command := c.Param("command")
|
||||
text := c.PostForm("text")
|
||||
args := strings.Split(text, " ")
|
||||
|
||||
if command == "" {
|
||||
command = args[0]
|
||||
args = args[1:]
|
||||
}
|
||||
|
||||
switch command {
|
||||
case slashStatus:
|
||||
slackStatus(c, args)
|
||||
|
||||
case slashRestart:
|
||||
slackRestart(c, args)
|
||||
|
||||
case slashDeploy:
|
||||
slackDeploy(c, args)
|
||||
|
||||
default:
|
||||
c.String(200, "sorry, I didn't understand [%s]", text)
|
||||
}
|
||||
}
|
||||
|
||||
func slackDeploy(c *gin.Context, args []string) {
|
||||
if len(args) != 3 {
|
||||
c.String(200, "Invalid command. Please provide [repo] [build number] [environment]")
|
||||
return
|
||||
}
|
||||
var (
|
||||
repo = args[0]
|
||||
num = args[1]
|
||||
env = args[2]
|
||||
)
|
||||
owner, name, _ := parseRepoBranch(repo)
|
||||
|
||||
c.String(200, "deploying build %s/%s#%s to %s", owner, name, num, env)
|
||||
}
|
||||
|
||||
func slackRestart(c *gin.Context, args []string) {
|
||||
var (
|
||||
repo = args[0]
|
||||
num = args[1]
|
||||
)
|
||||
owner, name, _ := parseRepoBranch(repo)
|
||||
|
||||
c.String(200, "restarting build %s/%s#%s", owner, name, num)
|
||||
}
|
||||
|
||||
func slackStatus(c *gin.Context, args []string) {
|
||||
var (
|
||||
owner string
|
||||
name string
|
||||
branch string
|
||||
)
|
||||
if len(args) > 0 {
|
||||
owner, name, branch = parseRepoBranch(args[0])
|
||||
}
|
||||
|
||||
repo, err := store.GetRepoOwnerName(c, owner, name)
|
||||
if err != nil {
|
||||
c.String(200, "cannot find repository %s/%s", owner, name)
|
||||
return
|
||||
}
|
||||
if branch == "" {
|
||||
branch = repo.Branch
|
||||
}
|
||||
build, err := store.GetBuildLast(c, repo, branch)
|
||||
if err != nil {
|
||||
c.String(200, "cannot find status for %s/%s@%s", owner, name, branch)
|
||||
return
|
||||
}
|
||||
c.String(200, "%s@%s build number %d finished with status %s",
|
||||
repo.FullName,
|
||||
build.Branch,
|
||||
build.Number,
|
||||
build.Status,
|
||||
)
|
||||
}
|
||||
|
||||
func parseRepoBranch(repo string) (owner, name, branch string) {
|
||||
|
||||
parts := strings.Split(repo, "@")
|
||||
if len(parts) == 2 {
|
||||
branch = parts[1]
|
||||
repo = parts[0]
|
||||
}
|
||||
|
||||
parts = strings.Split(repo, "/")
|
||||
if len(parts) == 2 {
|
||||
owner = parts[0]
|
||||
name = parts[1]
|
||||
}
|
||||
return owner, name, branch
|
||||
}
|
116
web/stream.go
116
web/stream.go
@@ -1,116 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/drone/drone/bus"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/router/middleware/session"
|
||||
"github.com/drone/drone/store"
|
||||
"github.com/drone/drone/stream"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/manucorporat/sse"
|
||||
)
|
||||
|
||||
// GetRepoEvents will upgrade the connection to a Websocket and will stream
|
||||
// event updates to the browser.
|
||||
func GetRepoEvents(c *gin.Context) {
|
||||
repo := session.Repo(c)
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
|
||||
eventc := make(chan *bus.Event, 1)
|
||||
bus.Subscribe(c, eventc)
|
||||
defer func() {
|
||||
bus.Unsubscribe(c, eventc)
|
||||
close(eventc)
|
||||
log.Infof("closed event stream")
|
||||
}()
|
||||
|
||||
c.Stream(func(w io.Writer) bool {
|
||||
select {
|
||||
case event := <-eventc:
|
||||
if event == nil {
|
||||
log.Infof("nil event received")
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(bradrydzewski) This is a super hacky workaround until we improve
|
||||
// the actual bus. Having a per-call database event is just plain stupid.
|
||||
if event.Repo.FullName == repo.FullName {
|
||||
|
||||
var payload = struct {
|
||||
model.Build
|
||||
Jobs []*model.Job `json:"jobs"`
|
||||
}{}
|
||||
payload.Build = event.Build
|
||||
payload.Jobs, _ = store.GetJobList(c, &event.Build)
|
||||
data, _ := json.Marshal(&payload)
|
||||
|
||||
sse.Encode(w, sse.Event{
|
||||
Event: "message",
|
||||
Data: string(data),
|
||||
})
|
||||
}
|
||||
case <-c.Writer.CloseNotify():
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func GetStream(c *gin.Context) {
|
||||
|
||||
repo := session.Repo(c)
|
||||
buildn, _ := strconv.Atoi(c.Param("build"))
|
||||
jobn, _ := strconv.Atoi(c.Param("number"))
|
||||
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
|
||||
build, err := store.GetBuildNumber(c, repo, buildn)
|
||||
if err != nil {
|
||||
log.Debugln("stream cannot get build number.", err)
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
job, err := store.GetJobNumber(c, build, jobn)
|
||||
if err != nil {
|
||||
log.Debugln("stream cannot get job number.", err)
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := stream.Reader(c, stream.ToKey(job.ID))
|
||||
if err != nil {
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-c.Writer.CloseNotify()
|
||||
rc.Close()
|
||||
}()
|
||||
|
||||
var line int
|
||||
var scanner = bufio.NewScanner(rc)
|
||||
for scanner.Scan() {
|
||||
line++
|
||||
var err = sse.Encode(c.Writer, sse.Event{
|
||||
Id: strconv.Itoa(line),
|
||||
Event: "message",
|
||||
Data: scanner.Text(),
|
||||
})
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
c.Writer.Flush()
|
||||
}
|
||||
|
||||
log.Debugf("Closed stream %s#%d", repo.FullName, build.Number)
|
||||
}
|
Reference in New Issue
Block a user