diff --git a/server/grpc/rpc.go b/server/grpc/rpc.go index 7d6ac6bdd..3a2d77e36 100644 --- a/server/grpc/rpc.go +++ b/server/grpc/rpc.go @@ -321,6 +321,12 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { return err } + log.Trace(). + Str("repo_id", fmt.Sprint(repo.ID)). + Str("build_id", fmt.Sprint(build.ID)). + Str("proc_id", id). + Msgf("gRPC Done with state: %#v", state) + if proc, err = shared.UpdateProcStatusToDone(s.store, *proc, state); err != nil { log.Error().Msgf("error: done: cannot update proc_id %d state: %s", proc.ID, err) } @@ -341,8 +347,8 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { } s.completeChildrenIfParentCompleted(procs, proc) - if !isThereRunningStage(procs) { - if build, err = shared.UpdateStatusToDone(s.store, *build, buildStatus(procs), proc.Stopped); err != nil { + if !model.IsThereRunningStage(procs) { + if build, err = shared.UpdateStatusToDone(s.store, *build, model.BuildStatus(procs), proc.Stopped); err != nil { log.Error().Err(err).Msgf("error: done: cannot update build_id %d final state", build.ID) } } @@ -361,23 +367,13 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { s.buildCount.WithLabelValues(repo.FullName, build.Branch, string(build.Status), "total").Inc() s.buildTime.WithLabelValues(repo.FullName, build.Branch, string(build.Status), "total").Set(float64(build.Finished - build.Started)) } - if isMultiPipeline(procs) { + if model.IsMultiPipeline(procs) { s.buildTime.WithLabelValues(repo.FullName, build.Branch, string(proc.State), proc.Name).Set(float64(proc.Stopped - proc.Started)) } return nil } -func isMultiPipeline(procs []*model.Proc) bool { - countPPIDZero := 0 - for _, proc := range procs { - if proc.PPID == 0 { - countPPIDZero++ - } - } - return countPPIDZero > 1 -} - // Log implements the rpc.Log function func (s *RPC) Log(c context.Context, id string, line *rpc.Line) error { entry := new(logging.Entry) @@ -398,31 +394,6 @@ func (s *RPC) completeChildrenIfParentCompleted(procs []*model.Proc, completedPr } } -func isThereRunningStage(procs []*model.Proc) bool { - for _, p := range procs { - if p.PPID == 0 { - if p.Running() { - return true - } - } - } - return false -} - -func buildStatus(procs []*model.Proc) model.StatusValue { - status := model.StatusSuccess - - for _, p := range procs { - if p.PPID == 0 { - if p.Failing() { - status = p.State - } - } - } - - return status -} - func (s *RPC) updateRemoteStatus(ctx context.Context, repo *model.Repo, build *model.Build, proc *model.Proc) { user, err := s.store.GetUser(repo.UserID) if err != nil { diff --git a/server/model/build.go b/server/model/build.go index 34920642a..399537622 100644 --- a/server/model/build.go +++ b/server/model/build.go @@ -28,6 +28,7 @@ type Build struct { Error string `json:"error" xorm:"build_error"` Enqueued int64 `json:"enqueued_at" xorm:"build_enqueued"` Created int64 `json:"created_at" xorm:"build_created"` + Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"` Started int64 `json:"started_at" xorm:"build_started"` Finished int64 `json:"finished_at" xorm:"build_finished"` Deploy string `json:"deploy_to" xorm:"build_deploy"` diff --git a/server/model/proc.go b/server/model/proc.go index 6ed26d317..89f449417 100644 --- a/server/model/proc.go +++ b/server/model/proc.go @@ -68,6 +68,20 @@ func (p *Proc) IsParent() bool { return p.PPID == 0 } +// IsMultiPipeline checks if proc list contain more than one parent proc +func IsMultiPipeline(procs []*Proc) bool { + c := 0 + for _, proc := range procs { + if proc.IsParent() { + c++ + } + if c > 1 { + return true + } + } + return false +} + // Tree creates a process tree from a flat process list. func Tree(procs []*Proc) ([]*Proc, error) { var nodes []*Proc @@ -93,6 +107,32 @@ func Tree(procs []*Proc) ([]*Proc, error) { return nodes, nil } +// BuildStatus determine build status based on corresponding proc list +func BuildStatus(procs []*Proc) StatusValue { + status := StatusSuccess + + for _, p := range procs { + if p.IsParent() && p.Failing() { + status = p.State + } + } + + return status +} + +// IsThereRunningStage determine if it contains procs running or pending to run +// TODO: return false based on depends_on (https://github.com/woodpecker-ci/woodpecker/pull/730#discussion_r795681697) +func IsThereRunningStage(procs []*Proc) bool { + for _, p := range procs { + if p.IsParent() { + if p.Running() { + return true + } + } + } + return false +} + func findNode(nodes []*Proc, pid int) (*Proc, error) { for _, node := range nodes { if node.PID == pid { diff --git a/web/src/compositions/useBuild.ts b/web/src/compositions/useBuild.ts index da7e56997..7ea5c72be 100644 --- a/web/src/compositions/useBuild.ts +++ b/web/src/compositions/useBuild.ts @@ -40,13 +40,17 @@ export default (build: Ref) => { } const start = build.value.started_at || 0; - const end = build.value.finished_at || 0; + const end = build.value.finished_at || build.value.updated_at || 0; if (start === 0) { return 0; } if (end === 0) { + // only calculate time on running builds + if (build.value.status !== 'running') { + return 0; + } return Date.now() - start * 1000; } diff --git a/web/src/lib/api/types/build.ts b/web/src/lib/api/types/build.ts index 891b1774a..e5f931d38 100644 --- a/web/src/lib/api/types/build.ts +++ b/web/src/lib/api/types/build.ts @@ -18,6 +18,9 @@ export type Build = { // When the build request was received. created_at: number; + // When the build was updated last time in database. + updated_at: number; + // When the build was enqueued. enqueued_at: number;