mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-06-02 21:49:25 +00:00
Improve status updates (#561)
- link to specific proc (only general build before) - set status for all procs (before: only for the whole build on some SCMs) - set status after restart - set status to pending after waiting for approval - make status of gitlab, gitea & github equal - dedupe status update code - dedupe `PostBuild` code close #410, close #297, close #459, close #521
This commit is contained in:
parent
c2b0c1d73e
commit
8e8f8967c3
|
@ -313,98 +313,28 @@ func PostApproval(c *gin.Context) {
|
||||||
yamls = append(yamls, &remote.FileMeta{Data: y.Data, Name: y.Name})
|
yamls = append(yamls, &remote.FileMeta{Data: y.Data, Name: y.Name})
|
||||||
}
|
}
|
||||||
|
|
||||||
build, err = startBuild(c, _store, build, user, repo, yamls)
|
build, buildItems, err := createBuildItems(c, _store, build, user, repo, yamls, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, fmt.Sprintf("startBuild: %v", err))
|
msg := fmt.Sprintf("failure to createBuildItems for %s", repo.FullName)
|
||||||
}
|
|
||||||
c.JSON(200, build)
|
|
||||||
}
|
|
||||||
|
|
||||||
func startBuild(ctx context.Context, store store.Store, build *model.Build, user *model.User, repo *model.Repo, yamls []*remote.FileMeta) (*model.Build, error) {
|
|
||||||
netrc, err := server.Config.Services.Remote.Netrc(user, repo)
|
|
||||||
if err != nil {
|
|
||||||
msg := "Failed to generate netrc file"
|
|
||||||
log.Error().Err(err).Msg(msg)
|
log.Error().Err(err).Msg(msg)
|
||||||
return nil, fmt.Errorf("%s: %v", msg, err)
|
c.String(http.StatusInternalServerError, msg)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the previous build so that we can send status change notifications
|
build, err = startBuild(c, _store, build, user, repo, buildItems)
|
||||||
last, err := store.GetBuildLastBefore(repo, build.Branch, build.ID)
|
|
||||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
||||||
log.Error().Err(err).Str("repo", repo.FullName).Msgf("Error getting last build before build number '%d'", build.Number)
|
|
||||||
}
|
|
||||||
|
|
||||||
secs, err := server.Config.Services.Secrets.SecretListBuild(repo, build)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Error getting secrets for %s#%d", repo.FullName, build.Number)
|
msg := fmt.Sprintf("failure to start build for %s", repo.FullName)
|
||||||
|
log.Error().Err(err).Msg(msg)
|
||||||
|
c.String(http.StatusInternalServerError, msg)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
regs, err := server.Config.Services.Registries.RegistryList(repo)
|
c.JSON(200, build)
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msgf("Error getting registry credentials for %s#%d", repo.FullName, build.Number)
|
|
||||||
}
|
|
||||||
|
|
||||||
envs := map[string]string{}
|
|
||||||
if server.Config.Services.Environ != nil {
|
|
||||||
globals, _ := server.Config.Services.Environ.EnvironList(repo)
|
|
||||||
for _, global := range globals {
|
|
||||||
envs[global.Name] = global.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b := shared.ProcBuilder{
|
|
||||||
Repo: repo,
|
|
||||||
Curr: build,
|
|
||||||
Last: last,
|
|
||||||
Netrc: netrc,
|
|
||||||
Secs: secs,
|
|
||||||
Regs: regs,
|
|
||||||
Envs: envs,
|
|
||||||
Link: server.Config.Server.Host,
|
|
||||||
Yamls: yamls,
|
|
||||||
}
|
|
||||||
buildItems, err := b.Build()
|
|
||||||
if err != nil {
|
|
||||||
if _, err := shared.UpdateToStatusError(store, *build, err); err != nil {
|
|
||||||
log.Error().Err(err).Msgf("Error setting error status of build for %s#%d", repo.FullName, build.Number)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
build = shared.SetBuildStepsOnBuild(b.Curr, buildItems)
|
|
||||||
|
|
||||||
if err := store.ProcCreate(build.Procs); err != nil {
|
|
||||||
log.Error().Err(err).Str("repo", repo.FullName).Msgf("error persisting procs for %s#%d", repo.FullName, build.Number)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
for _, item := range buildItems {
|
|
||||||
uri := fmt.Sprintf("%s/%s/build/%d", server.Config.Server.Host, repo.FullName, build.Number)
|
|
||||||
if len(buildItems) > 1 {
|
|
||||||
err = server.Config.Services.Remote.Status(ctx, user, repo, build, uri, item.Proc)
|
|
||||||
} else {
|
|
||||||
err = server.Config.Services.Remote.Status(ctx, user, repo, build, uri, nil)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msgf("error setting commit status for %s/%d", repo.FullName, build.Number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := publishToTopic(ctx, build, repo, model.Enqueued); err != nil {
|
|
||||||
log.Error().Err(err).Msg("publishToTopic")
|
|
||||||
}
|
|
||||||
if err := queueBuild(build, repo, buildItems); err != nil {
|
|
||||||
log.Error().Err(err).Msg("queueBuild")
|
|
||||||
}
|
|
||||||
|
|
||||||
return build, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostDecline(c *gin.Context) {
|
func PostDecline(c *gin.Context) {
|
||||||
var (
|
var (
|
||||||
_remote = server.Config.Services.Remote
|
_store = store.FromContext(c)
|
||||||
_store = store.FromContext(c)
|
|
||||||
|
|
||||||
repo = session.Repo(c)
|
repo = session.Repo(c)
|
||||||
user = session.User(c)
|
user = session.User(c)
|
||||||
num, _ = strconv.ParseInt(c.Params.ByName("number"), 10, 64)
|
num, _ = strconv.ParseInt(c.Params.ByName("number"), 10, 64)
|
||||||
|
@ -425,10 +355,15 @@ func PostDecline(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number)
|
if build.Procs, err = _store.ProcList(build); err != nil {
|
||||||
err = _remote.Status(c, user, repo, build, uri, nil)
|
log.Error().Err(err).Msg("can not get proc list from store")
|
||||||
if err != nil {
|
}
|
||||||
log.Error().Msgf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err)
|
if build.Procs, err = model.Tree(build.Procs); err != nil {
|
||||||
|
log.Error().Err(err).Msg("can not build tree from proc list")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateBuildStatus(c, build, repo, user); err != nil {
|
||||||
|
log.Error().Err(err).Msg("updateBuildStatus")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(200, build)
|
c.JSON(200, build)
|
||||||
|
@ -497,12 +432,9 @@ func PostBuild(c *gin.Context) {
|
||||||
_ = c.AbortWithError(404, err)
|
_ = c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var yamls []*remote.FileMeta
|
||||||
netrc, err := _remote.Netrc(user, repo)
|
for _, y := range configs {
|
||||||
if err != nil {
|
yamls = append(yamls, &remote.FileMeta{Data: y.Data, Name: y.Name})
|
||||||
log.Error().Msgf("failure to generate netrc for %s. %s", repo.FullName, err)
|
|
||||||
_ = c.AbortWithError(500, err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
build.ID = 0
|
build.ID = 0
|
||||||
|
@ -516,99 +448,63 @@ func PostBuild(c *gin.Context) {
|
||||||
build.Deploy = c.DefaultQuery("deploy_to", build.Deploy)
|
build.Deploy = c.DefaultQuery("deploy_to", build.Deploy)
|
||||||
|
|
||||||
if event, ok := c.GetQuery("event"); ok {
|
if event, ok := c.GetQuery("event"); ok {
|
||||||
if event := model.WebhookEvent(event); model.ValidateWebhookEvent(event) {
|
build.Event = model.WebhookEvent(event)
|
||||||
build.Event = event
|
|
||||||
|
if !model.ValidateWebhookEvent(build.Event) {
|
||||||
|
msg := fmt.Sprintf("build event '%s' is invalid", event)
|
||||||
|
c.String(http.StatusBadRequest, msg)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = _store.CreateBuild(build)
|
err = _store.CreateBuild(build)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(500, err.Error())
|
msg := fmt.Sprintf("failure to save build for %s", repo.FullName)
|
||||||
|
log.Error().Err(err).Msg(msg)
|
||||||
|
c.String(http.StatusInternalServerError, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = persistBuildConfigs(configs, build.ID)
|
err = persistBuildConfigs(configs, build.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Msgf("failure to persist build config for %s. %s", repo.FullName, err)
|
msg := fmt.Sprintf("failure to persist build config for %s.", repo.FullName)
|
||||||
_ = c.AbortWithError(500, err)
|
log.Error().Err(err).Msg(msg)
|
||||||
|
c.String(http.StatusInternalServerError, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read query string parameters into buildParams, exclude reserved params
|
// Read query string parameters into buildParams, exclude reserved params
|
||||||
var buildParams = map[string]string{}
|
var envs = map[string]string{}
|
||||||
for key, val := range c.Request.URL.Query() {
|
for key, val := range c.Request.URL.Query() {
|
||||||
switch key {
|
switch key {
|
||||||
|
// Skip some options of the endpoint
|
||||||
case "fork", "event", "deploy_to":
|
case "fork", "event", "deploy_to":
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
// We only accept string literals, because build parameters will be
|
// We only accept string literals, because build parameters will be
|
||||||
// injected as environment variables
|
// injected as environment variables
|
||||||
buildParams[key] = val[0]
|
// TODO: sanitize the value
|
||||||
|
envs[key] = val[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the previous build so that we can send
|
build, buildItems, err := createBuildItems(c, _store, build, user, repo, yamls, envs)
|
||||||
// on status change notifications
|
|
||||||
last, _ := _store.GetBuildLastBefore(repo, build.Branch, build.ID)
|
|
||||||
secs, err := server.Config.Services.Secrets.SecretListBuild(repo, build)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Msgf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
msg := fmt.Sprintf("failure to createBuildItems for %s", repo.FullName)
|
||||||
}
|
log.Error().Err(err).Msg(msg)
|
||||||
regs, err := server.Config.Services.Registries.RegistryList(repo)
|
c.String(http.StatusInternalServerError, msg)
|
||||||
if err != nil {
|
|
||||||
log.Debug().Msgf("Error getting registry credentials for %s#%d. %s", repo.FullName, build.Number, err)
|
|
||||||
}
|
|
||||||
if server.Config.Services.Environ != nil {
|
|
||||||
globals, _ := server.Config.Services.Environ.EnvironList(repo)
|
|
||||||
for _, global := range globals {
|
|
||||||
buildParams[global.Name] = global.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var yamls []*remote.FileMeta
|
|
||||||
for _, y := range configs {
|
|
||||||
yamls = append(yamls, &remote.FileMeta{Data: y.Data, Name: y.Name})
|
|
||||||
}
|
|
||||||
|
|
||||||
b := shared.ProcBuilder{
|
|
||||||
Repo: repo,
|
|
||||||
Curr: build,
|
|
||||||
Last: last,
|
|
||||||
Netrc: netrc,
|
|
||||||
Secs: secs,
|
|
||||||
Regs: regs,
|
|
||||||
Link: server.Config.Server.Host,
|
|
||||||
Yamls: yamls,
|
|
||||||
Envs: buildParams,
|
|
||||||
}
|
|
||||||
buildItems, err := b.Build()
|
|
||||||
if err != nil {
|
|
||||||
build.Status = model.StatusError
|
|
||||||
build.Started = time.Now().Unix()
|
|
||||||
build.Finished = build.Started
|
|
||||||
build.Error = err.Error()
|
|
||||||
c.JSON(500, build)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
build = shared.SetBuildStepsOnBuild(b.Curr, buildItems)
|
|
||||||
|
|
||||||
err = _store.ProcCreate(build.Procs)
|
build, err = startBuild(c, _store, build, user, repo, buildItems)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Msgf("cannot restart %s#%d: %s", repo.FullName, build.Number, err)
|
msg := fmt.Sprintf("failure to start build for %s", repo.FullName)
|
||||||
build.Status = model.StatusError
|
log.Error().Err(err).Msg(msg)
|
||||||
build.Started = time.Now().Unix()
|
c.String(http.StatusInternalServerError, msg)
|
||||||
build.Finished = build.Started
|
|
||||||
build.Error = err.Error()
|
|
||||||
c.JSON(500, build)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(202, build)
|
|
||||||
|
|
||||||
if err := publishToTopic(c, build, repo, model.Enqueued); err != nil {
|
c.JSON(200, build)
|
||||||
log.Error().Err(err).Msg("publishToTopic")
|
|
||||||
}
|
|
||||||
if err := queueBuild(build, repo, buildItems); err != nil {
|
|
||||||
log.Error().Err(err).Msg("queueBuild")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteBuildLogs(c *gin.Context) {
|
func DeleteBuildLogs(c *gin.Context) {
|
||||||
|
@ -652,6 +548,101 @@ func DeleteBuildLogs(c *gin.Context) {
|
||||||
c.String(204, "")
|
c.String(204, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createBuildItems(ctx context.Context, store store.Store, build *model.Build, user *model.User, repo *model.Repo, yamls []*remote.FileMeta, envs map[string]string) (*model.Build, []*shared.BuildItem, error) {
|
||||||
|
netrc, err := server.Config.Services.Remote.Netrc(user, repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to generate netrc file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the previous build so that we can send status change notifications
|
||||||
|
last, err := store.GetBuildLastBefore(repo, build.Branch, build.ID)
|
||||||
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
log.Error().Err(err).Str("repo", repo.FullName).Msgf("Error getting last build before build number '%d'", build.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
secs, err := server.Config.Services.Secrets.SecretListBuild(repo, build)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Error getting secrets for %s#%d", repo.FullName, build.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
regs, err := server.Config.Services.Registries.RegistryList(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Error getting registry credentials for %s#%d", repo.FullName, build.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
if envs == nil {
|
||||||
|
envs = map[string]string{}
|
||||||
|
}
|
||||||
|
if server.Config.Services.Environ != nil {
|
||||||
|
globals, _ := server.Config.Services.Environ.EnvironList(repo)
|
||||||
|
for _, global := range globals {
|
||||||
|
envs[global.Name] = global.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b := shared.ProcBuilder{
|
||||||
|
Repo: repo,
|
||||||
|
Curr: build,
|
||||||
|
Last: last,
|
||||||
|
Netrc: netrc,
|
||||||
|
Secs: secs,
|
||||||
|
Regs: regs,
|
||||||
|
Envs: envs,
|
||||||
|
Link: server.Config.Server.Host,
|
||||||
|
Yamls: yamls,
|
||||||
|
}
|
||||||
|
buildItems, err := b.Build()
|
||||||
|
if err != nil {
|
||||||
|
if _, err := shared.UpdateToStatusError(store, *build, err); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Error setting error status of build for %s#%d", repo.FullName, build.Number)
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
build = shared.SetBuildStepsOnBuild(b.Curr, buildItems)
|
||||||
|
|
||||||
|
return build, buildItems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startBuild(ctx context.Context, store store.Store, build *model.Build, user *model.User, repo *model.Repo, buildItems []*shared.BuildItem) (*model.Build, error) {
|
||||||
|
if err := store.ProcCreate(build.Procs); err != nil {
|
||||||
|
log.Error().Err(err).Str("repo", repo.FullName).Msgf("error persisting procs for %s#%d", repo.FullName, build.Number)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := publishToTopic(ctx, build, repo, model.Enqueued); err != nil {
|
||||||
|
log.Error().Err(err).Msg("publishToTopic")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := queueBuild(build, repo, buildItems); err != nil {
|
||||||
|
log.Error().Err(err).Msg("queueBuild")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateBuildStatus(ctx, build, repo, user); err != nil {
|
||||||
|
log.Error().Err(err).Msg("updateBuildStatus")
|
||||||
|
}
|
||||||
|
|
||||||
|
return build, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateBuildStatus(ctx context.Context, build *model.Build, repo *model.Repo, user *model.User) error {
|
||||||
|
for _, proc := range build.Procs {
|
||||||
|
// skip child procs
|
||||||
|
if !proc.IsParent() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := server.Config.Services.Remote.Status(ctx, user, repo, build, proc)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("error setting commit status for %s/%d", repo.FullName, build.Number)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func persistBuildConfigs(configs []*model.Config, buildID int64) error {
|
func persistBuildConfigs(configs []*model.Config, buildID int64) error {
|
||||||
for _, conf := range configs {
|
for _, conf := range configs {
|
||||||
buildConfig := &model.BuildConfig{
|
buildConfig := &model.BuildConfig{
|
||||||
|
|
|
@ -219,7 +219,7 @@ func PostHook(c *gin.Context) {
|
||||||
|
|
||||||
err = _store.CreateBuild(build, build.Procs...)
|
err = _store.CreateBuild(build, build.Procs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("failure to save commit for %s", repo.FullName)
|
msg := fmt.Sprintf("failure to save build for %s", repo.FullName)
|
||||||
log.Error().Err(err).Msg(msg)
|
log.Error().Err(err).Msg(msg)
|
||||||
c.String(http.StatusInternalServerError, msg)
|
c.String(http.StatusInternalServerError, msg)
|
||||||
return
|
return
|
||||||
|
@ -236,16 +236,28 @@ func PostHook(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
build, buildItems, err := createBuildItems(c, _store, build, repoUser, repo, remoteYamlConfigs, nil)
|
||||||
|
if err != nil {
|
||||||
|
msg := fmt.Sprintf("failure to createBuildItems for %s", repo.FullName)
|
||||||
|
log.Error().Err(err).Msg(msg)
|
||||||
|
c.String(http.StatusInternalServerError, msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if build.Status == model.StatusBlocked {
|
if build.Status == model.StatusBlocked {
|
||||||
if err := publishToTopic(c, build, repo, model.Enqueued); err != nil {
|
if err := publishToTopic(c, build, repo, model.Enqueued); err != nil {
|
||||||
log.Error().Err(err).Msg("publishToTopic")
|
log.Error().Err(err).Msg("publishToTopic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := updateBuildStatus(c, build, repo, repoUser); err != nil {
|
||||||
|
log.Error().Err(err).Msg("updateBuildStatus")
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, build)
|
c.JSON(http.StatusOK, build)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
build, err = startBuild(c, _store, build, repoUser, repo, remoteYamlConfigs)
|
build, err = startBuild(c, _store, build, repoUser, repo, buildItems)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("failure to start build for %s", repo.FullName)
|
msg := fmt.Sprintf("failure to start build for %s", repo.FullName)
|
||||||
log.Error().Err(err).Msg(msg)
|
log.Error().Err(err).Msg(msg)
|
||||||
|
|
|
@ -345,15 +345,9 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error {
|
||||||
if build, err = shared.UpdateStatusToDone(s.store, *build, buildStatus(procs), proc.Stopped); err != nil {
|
if build, err = shared.UpdateStatusToDone(s.store, *build, buildStatus(procs), proc.Stopped); err != nil {
|
||||||
log.Error().Err(err).Msgf("error: done: cannot update build_id %d final state", build.ID)
|
log.Error().Err(err).Msgf("error: done: cannot update build_id %d final state", build.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isMultiPipeline(procs) {
|
|
||||||
s.updateRemoteStatus(c, repo, build, nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if isMultiPipeline(procs) {
|
s.updateRemoteStatus(c, repo, build, proc)
|
||||||
s.updateRemoteStatus(c, repo, build, proc)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.logger.Close(c, id); err != nil {
|
if err := s.logger.Close(c, id); err != nil {
|
||||||
log.Error().Err(err).Msgf("done: cannot close build_id %d logger", proc.ID)
|
log.Error().Err(err).Msgf("done: cannot close build_id %d logger", proc.ID)
|
||||||
|
@ -431,21 +425,27 @@ func buildStatus(procs []*model.Proc) model.StatusValue {
|
||||||
|
|
||||||
func (s *RPC) updateRemoteStatus(ctx context.Context, repo *model.Repo, build *model.Build, proc *model.Proc) {
|
func (s *RPC) updateRemoteStatus(ctx context.Context, repo *model.Repo, build *model.Build, proc *model.Proc) {
|
||||||
user, err := s.store.GetUser(repo.UserID)
|
user, err := s.store.GetUser(repo.UserID)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
if refresher, ok := s.remote.(remote.Refresher); ok {
|
log.Error().Err(err).Msgf("can not get user with id '%d'", repo.UserID)
|
||||||
ok, err := refresher.Refresh(ctx, user)
|
return
|
||||||
if err != nil {
|
}
|
||||||
log.Error().Err(err).Msgf("grpc: refresh oauth token of user '%s' failed", user.Login)
|
|
||||||
} else if ok {
|
if refresher, ok := s.remote.(remote.Refresher); ok {
|
||||||
if err := s.store.UpdateUser(user); err != nil {
|
ok, err := refresher.Refresh(ctx, user)
|
||||||
log.Error().Err(err).Msg("fail to save user to store after refresh oauth token")
|
if err != nil {
|
||||||
}
|
log.Error().Err(err).Msgf("grpc: refresh oauth token of user '%s' failed", user.Login)
|
||||||
|
} else if ok {
|
||||||
|
if err := s.store.UpdateUser(user); err != nil {
|
||||||
|
log.Error().Err(err).Msg("fail to save user to store after refresh oauth token")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number)
|
}
|
||||||
err = s.remote.Status(ctx, user, repo, build, uri, proc)
|
|
||||||
|
// only do status updates for parent procs
|
||||||
|
if proc != nil && proc.IsParent() {
|
||||||
|
err = s.remote.Status(ctx, user, repo, build, proc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Msgf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err)
|
log.Error().Err(err).Msgf("error setting commit status for %s/%d", repo.FullName, build.Number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,11 @@ func (p *Proc) Failing() bool {
|
||||||
return p.State == StatusError || p.State == StatusKilled || p.State == StatusFailure
|
return p.State == StatusError || p.State == StatusKilled || p.State == StatusFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsParent returns true if the process is a parent process.
|
||||||
|
func (p *Proc) IsParent() bool {
|
||||||
|
return p.PPID == 0
|
||||||
|
}
|
||||||
|
|
||||||
// Tree creates a process tree from a flat process list.
|
// Tree creates a process tree from a flat process list.
|
||||||
func Tree(procs []*Proc) ([]*Proc, error) {
|
func Tree(procs []*Proc) ([]*Proc, error) {
|
||||||
var nodes []*Proc
|
var nodes []*Proc
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/remote"
|
"github.com/woodpecker-ci/woodpecker/server/remote"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/remote/bitbucket/internal"
|
"github.com/woodpecker-ci/woodpecker/server/remote/bitbucket/internal"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/remote/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bitbucket cloud endpoints.
|
// Bitbucket cloud endpoints.
|
||||||
|
@ -221,14 +222,14 @@ func (c *config) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status creates a build status for the Bitbucket commit.
|
// Status creates a build status for the Bitbucket commit.
|
||||||
func (c *config) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
func (c *config) Status(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, proc *model.Proc) error {
|
||||||
status := internal.BuildStatus{
|
status := internal.BuildStatus{
|
||||||
State: convertStatus(b.Status),
|
State: convertStatus(build.Status),
|
||||||
Desc: convertDesc(b.Status),
|
Desc: common.GetBuildStatusDescription(build.Status),
|
||||||
Key: "Woodpecker",
|
Key: "Woodpecker",
|
||||||
URL: link,
|
URL: common.GetBuildStatusLink(repo, build, nil),
|
||||||
}
|
}
|
||||||
return c.newClient(ctx, u).CreateStatus(r.Owner, r.Name, b.Commit, &status)
|
return c.newClient(ctx, user).CreateStatus(repo.Owner, repo.Name, build.Commit, &status)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate activates the repository by registering repository push hooks with
|
// Activate activates the repository by registering repository push hooks with
|
||||||
|
|
|
@ -254,7 +254,7 @@ func Test_bitbucket(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
g.It("Should update the status", func() {
|
g.It("Should update the status", func() {
|
||||||
err := c.Status(ctx, fakeUser, fakeRepo, fakeBuild, "http://127.0.0.1", nil)
|
err := c.Status(ctx, fakeUser, fakeRepo, fakeBuild, fakeProc)
|
||||||
g.Assert(err).IsNil()
|
g.Assert(err).IsNil()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -352,4 +352,9 @@ var (
|
||||||
fakeBuild = &model.Build{
|
fakeBuild = &model.Build{
|
||||||
Commit: "9ecad50",
|
Commit: "9ecad50",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fakeProc = &model.Proc{
|
||||||
|
Name: "test",
|
||||||
|
State: model.StatusSuccess,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,15 +32,6 @@ const (
|
||||||
statusFailure = "FAILED"
|
statusFailure = "FAILED"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
descPending = "this build is pending"
|
|
||||||
descSuccess = "the build was successful"
|
|
||||||
descFailure = "the build failed"
|
|
||||||
descBlocked = "the build requires approval"
|
|
||||||
descDeclined = "the build was rejected"
|
|
||||||
descError = "oops, something went wrong"
|
|
||||||
)
|
|
||||||
|
|
||||||
// convertStatus is a helper function used to convert a Woodpecker status to a
|
// convertStatus is a helper function used to convert a Woodpecker status to a
|
||||||
// Bitbucket commit status.
|
// Bitbucket commit status.
|
||||||
func convertStatus(status model.StatusValue) string {
|
func convertStatus(status model.StatusValue) string {
|
||||||
|
@ -54,25 +45,6 @@ func convertStatus(status model.StatusValue) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertDesc is a helper function used to convert a Woodpecker status to a
|
|
||||||
// Bitbucket status description.
|
|
||||||
func convertDesc(status model.StatusValue) string {
|
|
||||||
switch status {
|
|
||||||
case model.StatusPending, model.StatusRunning:
|
|
||||||
return descPending
|
|
||||||
case model.StatusSuccess:
|
|
||||||
return descSuccess
|
|
||||||
case model.StatusFailure:
|
|
||||||
return descFailure
|
|
||||||
case model.StatusBlocked:
|
|
||||||
return descBlocked
|
|
||||||
case model.StatusDeclined:
|
|
||||||
return descDeclined
|
|
||||||
default:
|
|
||||||
return descError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertRepo is a helper function used to convert a Bitbucket repository
|
// convertRepo is a helper function used to convert a Bitbucket repository
|
||||||
// structure to the common Woodpecker repository structure.
|
// structure to the common Woodpecker repository structure.
|
||||||
func convertRepo(from *internal.Repo) *model.Repo {
|
func convertRepo(from *internal.Repo) *model.Repo {
|
||||||
|
|
|
@ -43,24 +43,6 @@ func Test_helper(t *testing.T) {
|
||||||
g.Assert(convertStatus(model.StatusError)).Equal(statusFailure)
|
g.Assert(convertStatus(model.StatusError)).Equal(statusFailure)
|
||||||
})
|
})
|
||||||
|
|
||||||
g.It("should convert passing desc", func() {
|
|
||||||
g.Assert(convertDesc(model.StatusSuccess)).Equal(descSuccess)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.It("should convert pending desc", func() {
|
|
||||||
g.Assert(convertDesc(model.StatusPending)).Equal(descPending)
|
|
||||||
g.Assert(convertDesc(model.StatusRunning)).Equal(descPending)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.It("should convert failing desc", func() {
|
|
||||||
g.Assert(convertDesc(model.StatusFailure)).Equal(descFailure)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.It("should convert error desc", func() {
|
|
||||||
g.Assert(convertDesc(model.StatusKilled)).Equal(descError)
|
|
||||||
g.Assert(convertDesc(model.StatusError)).Equal(descError)
|
|
||||||
})
|
|
||||||
|
|
||||||
g.It("should convert repository", func() {
|
g.It("should convert repository", func() {
|
||||||
from := &internal.Repo{
|
from := &internal.Repo{
|
||||||
FullName: "octocat/hello-world",
|
FullName: "octocat/hello-world",
|
||||||
|
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/remote"
|
"github.com/woodpecker-ci/woodpecker/server/remote"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/remote/bitbucketserver/internal"
|
"github.com/woodpecker-ci/woodpecker/server/remote/bitbucketserver/internal"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/remote/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -185,18 +186,18 @@ func (c *Config) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status is not supported by the bitbucketserver driver.
|
// Status is not supported by the bitbucketserver driver.
|
||||||
func (c *Config) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
func (c *Config) Status(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, proc *model.Proc) error {
|
||||||
status := internal.BuildStatus{
|
status := internal.BuildStatus{
|
||||||
State: convertStatus(b.Status),
|
State: convertStatus(build.Status),
|
||||||
Desc: convertDesc(b.Status),
|
Desc: common.GetBuildStatusDescription(build.Status),
|
||||||
Name: fmt.Sprintf("Woodpecker #%d - %s", b.Number, b.Branch),
|
Name: fmt.Sprintf("Woodpecker #%d - %s", build.Number, build.Branch),
|
||||||
Key: "Woodpecker",
|
Key: "Woodpecker",
|
||||||
URL: link,
|
URL: common.GetBuildStatusLink(repo, build, nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token)
|
client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, user.Token)
|
||||||
|
|
||||||
return client.CreateStatus(b.Commit, &status)
|
return client.CreateStatus(build.Commit, &status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) {
|
func (c *Config) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||||
|
|
|
@ -34,13 +34,6 @@ const (
|
||||||
statusFailure = "FAILED"
|
statusFailure = "FAILED"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
descPending = "this build is pending"
|
|
||||||
descSuccess = "the build was successful"
|
|
||||||
descFailure = "the build failed"
|
|
||||||
descError = "oops, something went wrong"
|
|
||||||
)
|
|
||||||
|
|
||||||
// convertStatus is a helper function used to convert a Woodpecker status to a
|
// convertStatus is a helper function used to convert a Woodpecker status to a
|
||||||
// Bitbucket commit status.
|
// Bitbucket commit status.
|
||||||
func convertStatus(status model.StatusValue) string {
|
func convertStatus(status model.StatusValue) string {
|
||||||
|
@ -54,21 +47,6 @@ func convertStatus(status model.StatusValue) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertDesc is a helper function used to convert a Woodpecker status to a
|
|
||||||
// Bitbucket status description.
|
|
||||||
func convertDesc(status model.StatusValue) string {
|
|
||||||
switch status {
|
|
||||||
case model.StatusPending, model.StatusRunning:
|
|
||||||
return descPending
|
|
||||||
case model.StatusSuccess:
|
|
||||||
return descSuccess
|
|
||||||
case model.StatusFailure:
|
|
||||||
return descFailure
|
|
||||||
default:
|
|
||||||
return descError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertRepo is a helper function used to convert a Bitbucket server repository
|
// convertRepo is a helper function used to convert a Bitbucket server repository
|
||||||
// structure to the common Woodpecker repository structure.
|
// structure to the common Woodpecker repository structure.
|
||||||
func convertRepo(from *internal.Repo) *model.Repo {
|
func convertRepo(from *internal.Repo) *model.Repo {
|
||||||
|
|
|
@ -243,7 +243,7 @@ func (c *Coding) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status sends the commit status to the remote system.
|
// Status sends the commit status to the remote system.
|
||||||
func (c *Coding) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
func (c *Coding) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, proc *model.Proc) error {
|
||||||
// EMPTY: not implemented in Coding OAuth API
|
// EMPTY: not implemented in Coding OAuth API
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
60
server/remote/common/status.go
Normal file
60
server/remote/common/status.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
const base = "ci/woodpecker"
|
||||||
|
|
||||||
|
func GetBuildStatusContext(repo *model.Repo, build *model.Build, proc *model.Proc) string {
|
||||||
|
name := base
|
||||||
|
|
||||||
|
switch build.Event {
|
||||||
|
case model.EventPull:
|
||||||
|
name += "/pr"
|
||||||
|
default:
|
||||||
|
if len(build.Event) > 0 {
|
||||||
|
name += "/" + string(build.Event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if proc != nil {
|
||||||
|
name += "/" + proc.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBuildStatusDescription is a helper function that generates a description
|
||||||
|
// message for the current build status.
|
||||||
|
func GetBuildStatusDescription(status model.StatusValue) string {
|
||||||
|
switch status {
|
||||||
|
case model.StatusPending:
|
||||||
|
return "Pipeline is pending"
|
||||||
|
case model.StatusRunning:
|
||||||
|
return "Pipeline is running"
|
||||||
|
case model.StatusSuccess:
|
||||||
|
return "Pipeline was successful"
|
||||||
|
case model.StatusFailure, model.StatusError:
|
||||||
|
return "Pipeline failed"
|
||||||
|
case model.StatusKilled:
|
||||||
|
return "Pipeline was canceled"
|
||||||
|
case model.StatusBlocked:
|
||||||
|
return "Pipeline is pending approval"
|
||||||
|
case model.StatusDeclined:
|
||||||
|
return "Pipeline was rejected"
|
||||||
|
default:
|
||||||
|
return "unknown status"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBuildStatusLink(repo *model.Repo, build *model.Build, proc *model.Proc) string {
|
||||||
|
if proc == nil {
|
||||||
|
return fmt.Sprintf("%s/%s/build/%d", server.Config.Server.Host, repo.FullName, build.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/%s/build/%d/%d", server.Config.Server.Host, repo.FullName, build.Number, proc.PID)
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/woodpecker-ci/woodpecker/server"
|
"github.com/woodpecker-ci/woodpecker/server"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/remote"
|
"github.com/woodpecker-ci/woodpecker/server/remote"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/remote/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -337,27 +338,23 @@ func (c *Gitea) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status is supported by the Gitea driver.
|
// Status is supported by the Gitea driver.
|
||||||
func (c *Gitea) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
func (c *Gitea) Status(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, proc *model.Proc) error {
|
||||||
client, err := c.newClientToken(ctx, u.Token)
|
client, err := c.newClientToken(ctx, user.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
status := getStatus(b.Status)
|
|
||||||
desc := getDesc(b.Status)
|
|
||||||
|
|
||||||
_, _, err = client.CreateStatus(
|
_, _, err = client.CreateStatus(
|
||||||
r.Owner,
|
repo.Owner,
|
||||||
r.Name,
|
repo.Name,
|
||||||
b.Commit,
|
build.Commit,
|
||||||
gitea.CreateStatusOption{
|
gitea.CreateStatusOption{
|
||||||
State: status,
|
State: getStatus(proc.State),
|
||||||
TargetURL: link,
|
TargetURL: common.GetBuildStatusLink(repo, build, proc),
|
||||||
Description: desc,
|
Description: common.GetBuildStatusDescription(proc.State),
|
||||||
Context: c.Context,
|
Context: common.GetBuildStatusContext(repo, build, proc),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,16 +457,6 @@ func (c *Gitea) newClientToken(ctx context.Context, token string) (*gitea.Client
|
||||||
return gitea.NewClient(c.URL, gitea.SetToken(token), gitea.SetHTTPClient(httpClient), gitea.SetContext(ctx))
|
return gitea.NewClient(c.URL, gitea.SetToken(token), gitea.SetHTTPClient(httpClient), gitea.SetContext(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
DescPending = "the build is pending"
|
|
||||||
DescRunning = "the build is running"
|
|
||||||
DescSuccess = "the build was successful"
|
|
||||||
DescFailure = "the build failed"
|
|
||||||
DescCanceled = "the build canceled"
|
|
||||||
DescBlocked = "the build is pending approval"
|
|
||||||
DescDeclined = "the build was rejected"
|
|
||||||
)
|
|
||||||
|
|
||||||
// getStatus is a helper function that converts a Woodpecker
|
// getStatus is a helper function that converts a Woodpecker
|
||||||
// status to a Gitea status.
|
// status to a Gitea status.
|
||||||
func getStatus(status model.StatusValue) gitea.StatusState {
|
func getStatus(status model.StatusValue) gitea.StatusState {
|
||||||
|
@ -490,26 +477,3 @@ func getStatus(status model.StatusValue) gitea.StatusState {
|
||||||
return gitea.StatusFailure
|
return gitea.StatusFailure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDesc is a helper function that generates a description
|
|
||||||
// message for the build based on the status.
|
|
||||||
func getDesc(status model.StatusValue) string {
|
|
||||||
switch status {
|
|
||||||
case model.StatusPending:
|
|
||||||
return DescPending
|
|
||||||
case model.StatusRunning:
|
|
||||||
return DescRunning
|
|
||||||
case model.StatusSuccess:
|
|
||||||
return DescSuccess
|
|
||||||
case model.StatusFailure, model.StatusError:
|
|
||||||
return DescFailure
|
|
||||||
case model.StatusKilled:
|
|
||||||
return DescCanceled
|
|
||||||
case model.StatusBlocked:
|
|
||||||
return DescBlocked
|
|
||||||
case model.StatusDeclined:
|
|
||||||
return DescDeclined
|
|
||||||
default:
|
|
||||||
return DescFailure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ func Test_gitea(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
g.It("Should return nil from send build status", func() {
|
g.It("Should return nil from send build status", func() {
|
||||||
err := c.Status(ctx, fakeUser, fakeRepo, fakeBuild, "http://gitea.io", nil)
|
err := c.Status(ctx, fakeUser, fakeRepo, fakeBuild, fakeProc)
|
||||||
g.Assert(err).IsNil()
|
g.Assert(err).IsNil()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -196,4 +196,9 @@ var (
|
||||||
fakeBuild = &model.Build{
|
fakeBuild = &model.Build{
|
||||||
Commit: "9ecad50",
|
Commit: "9ecad50",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fakeProc = &model.Proc{
|
||||||
|
Name: "test",
|
||||||
|
State: model.StatusSuccess,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -64,7 +64,7 @@ func parsePushHook(payload io.Reader) (repo *model.Repo, build *model.Build, err
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// is this even needed?
|
// TODO is this even needed?
|
||||||
if push.RefType == refBranch {
|
if push.RefType == refBranch {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/woodpecker-ci/woodpecker/server"
|
"github.com/woodpecker-ci/woodpecker/server"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/remote"
|
"github.com/woodpecker-ci/woodpecker/server/remote"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/remote/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -418,66 +419,34 @@ func matchingHooks(hooks []*github.Hook, rawurl string) *github.Hook {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
var reDeploy = regexp.MustCompile(`.+/deployments/(\d+)`)
|
||||||
// TODO(bradrydzewski) refactor below functions
|
|
||||||
//
|
|
||||||
|
|
||||||
// Status sends the commit status to the remote system.
|
// Status sends the commit status to the remote system.
|
||||||
// An example would be the GitHub pull request status.
|
// An example would be the GitHub pull request status.
|
||||||
func (c *client) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
func (c *client) Status(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, proc *model.Proc) error {
|
||||||
client := c.newClientToken(ctx, u.Token)
|
client := c.newClientToken(ctx, user.Token)
|
||||||
switch b.Event {
|
|
||||||
case "deployment":
|
|
||||||
return deploymentStatus(ctx, client, r, b, link)
|
|
||||||
default:
|
|
||||||
return repoStatus(ctx, client, r, b, link, c.Context, proc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func repoStatus(c context.Context, client *github.Client, r *model.Repo, b *model.Build, link, ctx string, proc *model.Proc) error {
|
if build.Event == model.EventDeploy {
|
||||||
switch b.Event {
|
matches := reDeploy.FindStringSubmatch(build.Link)
|
||||||
case model.EventPull:
|
if len(matches) != 2 {
|
||||||
ctx += "/pr"
|
return nil
|
||||||
default:
|
|
||||||
if len(b.Event) > 0 {
|
|
||||||
ctx += "/" + string(b.Event)
|
|
||||||
}
|
}
|
||||||
|
id, _ := strconv.Atoi(matches[1])
|
||||||
|
|
||||||
|
_, _, err := client.Repositories.CreateDeploymentStatus(ctx, repo.Owner, repo.Name, int64(id), &github.DeploymentStatusRequest{
|
||||||
|
State: github.String(convertStatus(build.Status)),
|
||||||
|
Description: github.String(common.GetBuildStatusDescription(build.Status)),
|
||||||
|
LogURL: github.String(common.GetBuildStatusLink(repo, build, nil)),
|
||||||
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
status := github.String(convertStatus(b.Status))
|
_, _, err := client.Repositories.CreateStatus(ctx, repo.Owner, repo.Name, build.Commit, &github.RepoStatus{
|
||||||
desc := github.String(convertDesc(b.Status))
|
Context: github.String(common.GetBuildStatusContext(repo, build, proc)),
|
||||||
|
State: github.String(convertStatus(proc.State)),
|
||||||
if proc != nil {
|
Description: github.String(common.GetBuildStatusDescription(proc.State)),
|
||||||
ctx += "/" + proc.Name
|
TargetURL: github.String(common.GetBuildStatusLink(repo, build, proc)),
|
||||||
status = github.String(convertStatus(proc.State))
|
})
|
||||||
desc = github.String(convertDesc(proc.State))
|
|
||||||
}
|
|
||||||
|
|
||||||
data := github.RepoStatus{
|
|
||||||
Context: github.String(ctx),
|
|
||||||
State: status,
|
|
||||||
Description: desc,
|
|
||||||
TargetURL: github.String(link),
|
|
||||||
}
|
|
||||||
_, _, err := client.Repositories.CreateStatus(c, r.Owner, r.Name, b.Commit, &data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var reDeploy = regexp.MustCompile(`.+/deployments/(\d+)`)
|
|
||||||
|
|
||||||
func deploymentStatus(ctx context.Context, client *github.Client, r *model.Repo, b *model.Build, link string) error {
|
|
||||||
matches := reDeploy.FindStringSubmatch(b.Link)
|
|
||||||
if len(matches) != 2 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
id, _ := strconv.Atoi(matches[1])
|
|
||||||
|
|
||||||
data := github.DeploymentStatusRequest{
|
|
||||||
State: github.String(convertStatus(b.Status)),
|
|
||||||
Description: github.String(convertDesc(b.Status)),
|
|
||||||
LogURL: github.String(link),
|
|
||||||
}
|
|
||||||
_, _, err := client.Repositories.CreateDeploymentStatus(ctx, r.Owner, r.Name, int64(id), &data)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,13 +29,13 @@ import (
|
||||||
"github.com/woodpecker-ci/woodpecker/server"
|
"github.com/woodpecker-ci/woodpecker/server"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/remote"
|
"github.com/woodpecker-ci/woodpecker/server/remote"
|
||||||
|
"github.com/woodpecker-ci/woodpecker/server/remote/common"
|
||||||
"github.com/woodpecker-ci/woodpecker/shared/oauth2"
|
"github.com/woodpecker-ci/woodpecker/shared/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultScope = "api"
|
defaultScope = "api"
|
||||||
perPage = 100
|
perPage = 100
|
||||||
statusContext = "ci/drone"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Opts defines configuration options.
|
// Opts defines configuration options.
|
||||||
|
@ -347,7 +347,7 @@ func (g *Gitlab) Dir(ctx context.Context, user *model.User, repo *model.Repo, bu
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status sends the commit status back to gitlab.
|
// Status sends the commit status back to gitlab.
|
||||||
func (g *Gitlab) Status(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, link string, proc *model.Proc) error {
|
func (g *Gitlab) Status(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, proc *model.Proc) error {
|
||||||
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
client, err := newClient(g.URL, user.Token, g.SkipVerify)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -359,12 +359,11 @@ func (g *Gitlab) Status(ctx context.Context, user *model.User, repo *model.Repo,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err = client.Commits.SetCommitStatus(_repo.ID, build.Commit, &gitlab.SetCommitStatusOptions{
|
_, _, err = client.Commits.SetCommitStatus(_repo.ID, build.Commit, &gitlab.SetCommitStatusOptions{
|
||||||
Ref: gitlab.String(strings.ReplaceAll(build.Ref, "refs/heads/", "")),
|
State: getStatus(proc.State),
|
||||||
State: getStatus(build.Status),
|
Description: gitlab.String(common.GetBuildStatusDescription(proc.State)),
|
||||||
Description: gitlab.String(getDesc(build.Status)),
|
TargetURL: gitlab.String(common.GetBuildStatusLink(repo, build, proc)),
|
||||||
TargetURL: &link,
|
Context: gitlab.String(common.GetBuildStatusContext(repo, build, proc)),
|
||||||
Name: nil,
|
PipelineID: gitlab.Int(int(build.Number)),
|
||||||
Context: gitlab.String(statusContext),
|
|
||||||
}, gitlab.WithContext(ctx))
|
}, gitlab.WithContext(ctx))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -20,16 +20,6 @@ import (
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
DescPending = "the build is pending"
|
|
||||||
DescRunning = "the buils is running"
|
|
||||||
DescSuccess = "the build was successful"
|
|
||||||
DescFailure = "the build failed"
|
|
||||||
DescCanceled = "the build canceled"
|
|
||||||
DescBlocked = "the build is pending approval"
|
|
||||||
DescDeclined = "the build was rejected"
|
|
||||||
)
|
|
||||||
|
|
||||||
// getStatus is a helper that converts a Woodpecker status to a Gitlab status.
|
// getStatus is a helper that converts a Woodpecker status to a Gitlab status.
|
||||||
func getStatus(status model.StatusValue) gitlab.BuildStateValue {
|
func getStatus(status model.StatusValue) gitlab.BuildStateValue {
|
||||||
switch status {
|
switch status {
|
||||||
|
@ -47,26 +37,3 @@ func getStatus(status model.StatusValue) gitlab.BuildStateValue {
|
||||||
return gitlab.Failed
|
return gitlab.Failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDesc is a helper function that generates a description
|
|
||||||
// message for the build based on the status.
|
|
||||||
func getDesc(status model.StatusValue) string {
|
|
||||||
switch status {
|
|
||||||
case model.StatusPending:
|
|
||||||
return DescPending
|
|
||||||
case model.StatusRunning:
|
|
||||||
return DescRunning
|
|
||||||
case model.StatusSuccess:
|
|
||||||
return DescSuccess
|
|
||||||
case model.StatusFailure, model.StatusError:
|
|
||||||
return DescFailure
|
|
||||||
case model.StatusKilled:
|
|
||||||
return DescCanceled
|
|
||||||
case model.StatusBlocked:
|
|
||||||
return DescBlocked
|
|
||||||
case model.StatusDeclined:
|
|
||||||
return DescDeclined
|
|
||||||
default:
|
|
||||||
return DescFailure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -209,7 +209,7 @@ func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status is not supported by the Gogs driver.
|
// Status is not supported by the Gogs driver.
|
||||||
func (c *client) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
func (c *client) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, proc *model.Proc) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ func Test_gogs(t *testing.T) {
|
||||||
|
|
||||||
g.It("Should return no-op for usupporeted features", func() {
|
g.It("Should return no-op for usupporeted features", func() {
|
||||||
_, err1 := c.Auth(ctx, "octocat", "4vyW6b49Z")
|
_, err1 := c.Auth(ctx, "octocat", "4vyW6b49Z")
|
||||||
err2 := c.Status(ctx, nil, nil, nil, "", nil)
|
err2 := c.Status(ctx, nil, nil, nil, nil)
|
||||||
err3 := c.Deactivate(ctx, nil, nil, "")
|
err3 := c.Deactivate(ctx, nil, nil, "")
|
||||||
g.Assert(err1).IsNotNil()
|
g.Assert(err1).IsNotNil()
|
||||||
g.Assert(err2).IsNil()
|
g.Assert(err2).IsNil()
|
||||||
|
|
|
@ -283,12 +283,12 @@ func (_m *Remote) Repos(ctx context.Context, u *model.User) ([]*model.Repo, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status provides a mock function with given fields: ctx, u, r, b, link, proc
|
// Status provides a mock function with given fields: ctx, u, r, b, link, proc
|
||||||
func (_m *Remote) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
|
func (_m *Remote) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, proc *model.Proc) error {
|
||||||
ret := _m.Called(ctx, u, r, b, link, proc)
|
ret := _m.Called(ctx, u, r, b, proc)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, *model.Build, string, *model.Proc) error); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, *model.Build, *model.Proc) error); ok {
|
||||||
r0 = rf(ctx, u, r, b, link, proc)
|
r0 = rf(ctx, u, r, b, proc)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
r0 = ret.Error(0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ type Remote interface {
|
||||||
|
|
||||||
// Status sends the commit status to the remote system.
|
// Status sends the commit status to the remote system.
|
||||||
// An example would be the GitHub pull request status.
|
// An example would be the GitHub pull request status.
|
||||||
Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error
|
Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, p *model.Proc) error
|
||||||
|
|
||||||
// Netrc returns a .netrc file that can be used to clone
|
// Netrc returns a .netrc file that can be used to clone
|
||||||
// private repositories from a remote system.
|
// private repositories from a remote system.
|
||||||
|
|
Loading…
Reference in a new issue