diff --git a/.drone.yml b/.drone.yml index e32df4159..46b4c36c8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -15,7 +15,7 @@ pipeline: - go get -u golang.org/x/net/context/ctxhttp - go get -u github.com/golang/protobuf/proto - go get -u github.com/golang/protobuf/protoc-gen-go - - go test -cover $(go list ./... | grep -v /vendor/) + - go test -cover -timeout 30s $(go list ./... | grep -v /vendor/) test_postgres: image: golang:1.12.4 @@ -23,7 +23,7 @@ pipeline: - DATABASE_DRIVER=postgres - DATABASE_CONFIG=host=postgres user=postgres dbname=postgres sslmode=disable commands: - - go test github.com/laszlocph/woodpecker/store/datastore + - go test -timeout 30s github.com/laszlocph/woodpecker/store/datastore test_mysql: image: golang:1.12.4 @@ -31,7 +31,7 @@ pipeline: - DATABASE_DRIVER=mysql - DATABASE_CONFIG=root@tcp(mysql:3306)/test?parseTime=true commands: - - go test github.com/laszlocph/woodpecker/store/datastore + - go test -timeout 30s github.com/laszlocph/woodpecker/store/datastore build: image: golang:1.12.4 diff --git a/BUILDING b/BUILDING index 3554fd219..a6077b4ab 100644 --- a/BUILDING +++ b/BUILDING @@ -1,15 +1,6 @@ 1. Install go 1.9 or later -2. Install dependencies - - go get -u golang.org/x/net/context - go get -u golang.org/x/net/context/ctxhttp - go get -u github.com/golang/protobuf/proto - go get -u github.com/golang/protobuf/protoc-gen-go - -3. Install binaries to $GOPATH/bin - - go install github.com/laszlocph/woodpecker/cmd/drone-agent - go install github.com/laszlocph/woodpecker/cmd/drone-server +2. Execute `make deps` to download dependencies +3. Execute `make install` to compile project and install binaries to `GOPATH` --- diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..571eff573 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +export GO111MODULE=off + +GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./.git/*") + +all: deps build + +deps: + go get -u golang.org/x/net/context + go get -u golang.org/x/net/context/ctxhttp + go get -u github.com/golang/protobuf/proto + go get -u github.com/golang/protobuf/protoc-gen-go + +formatcheck: + ([ -z "$(shell gofmt -d $(GOFILES_NOVENDOR))" ]) || (echo "Source is unformatted"; exit 1) + +format: + @gofmt -w ${GOFILES_NOVENDOR} + +test-agent: + go test -timeout 30s github.com/laszlocph/woodpecker/cmd/drone-agent $(go list ./... | grep -v /vendor/) + +test-server: +ifneq ($(shell uname), "Linux") + $(error Target OS is not Linux drone-server build skipped) +endif + go test -timeout 30s github.com/laszlocph/woodpecker/cmd/drone-server + +test-lib: + go test -timeout 30s $(shell go list ./... | grep -v '/cmd/') + +test: test-lib test-agent test-server + +build-agent: + go build -o build/drone-agent github.com/laszlocph/woodpecker/cmd/drone-agent + +build-server: +ifneq ($(shell uname), "Linux") + $(error Target OS is not Linux drone-server build skipped) +endif + go build -o build/drone-server github.com/laszlocph/woodpecker/cmd/drone-server + +build: build-agent build-server + +install: + go install github.com/laszlocph/woodpecker/cmd/drone-agent + go install github.com/laszlocph/woodpecker/cmd/drone-server \ No newline at end of file diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 000000000..753b47468 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,2 @@ +drone-agent +drone-server \ No newline at end of file diff --git a/cli/drone/exec/exec.go b/cli/drone/exec/exec.go index 1d7388882..d791faa94 100644 --- a/cli/drone/exec/exec.go +++ b/cli/drone/exec/exec.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/drone/envsubst" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/backend" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/backend/docker" @@ -20,7 +21,6 @@ import ( "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/frontend/yaml/linter" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/interrupt" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/multipart" - "github.com/drone/envsubst" "github.com/urfave/cli" ) @@ -275,7 +275,7 @@ var Command = cli.Command{ EnvVar: "DRONE_JOB_NUMBER", }, cli.StringSliceFlag{ - Name: "env, e", + Name: "env, e", EnvVar: "DRONE_ENV", }, }, diff --git a/cncd/pipeline/.drone.yml b/cncd/pipeline/.drone.yml index 81405c049..4097aa625 100644 --- a/cncd/pipeline/.drone.yml +++ b/cncd/pipeline/.drone.yml @@ -12,4 +12,4 @@ pipeline: test: image: golang:1.8 commands: - - go test -cover github.com/laszlocph/woodpecker/cncd/pipeline/... + - go test -cover -timeout 30s github.com/laszlocph/woodpecker/cncd/pipeline/... diff --git a/cncd/pipeline/pipeline/frontend/yaml/constraint.go b/cncd/pipeline/pipeline/frontend/yaml/constraint.go index 01dd1f1d8..cee0ca891 100644 --- a/cncd/pipeline/pipeline/frontend/yaml/constraint.go +++ b/cncd/pipeline/pipeline/frontend/yaml/constraint.go @@ -3,9 +3,9 @@ package yaml import ( "path/filepath" + libcompose "github.com/docker/libcompose/yaml" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/frontend" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/frontend/yaml/types" - libcompose "github.com/docker/libcompose/yaml" ) type ( diff --git a/cncd/pipeline/pipeline/frontend/yaml/types/bool.go b/cncd/pipeline/pipeline/frontend/yaml/types/bool.go index 868ff3b24..17a7aa9af 100644 --- a/cncd/pipeline/pipeline/frontend/yaml/types/bool.go +++ b/cncd/pipeline/pipeline/frontend/yaml/types/bool.go @@ -25,4 +25,4 @@ func (b *BoolTrue) UnmarshalYAML(unmarshal func(interface{}) error) error { // Bool returns the bool value. func (b BoolTrue) Bool() bool { return !b.value -} \ No newline at end of file +} diff --git a/cncd/pipeline/pipeline/rpc/client_grpc.go b/cncd/pipeline/pipeline/rpc/client_grpc.go index 0baca63ae..9e5f78c8d 100644 --- a/cncd/pipeline/pipeline/rpc/client_grpc.go +++ b/cncd/pipeline/pipeline/rpc/client_grpc.go @@ -3,8 +3,8 @@ package rpc import ( "context" "encoding/json" - "time" "log" + "time" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/backend" "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/rpc/proto" diff --git a/cncd/pipeline/pipeline/rpc/health.go b/cncd/pipeline/pipeline/rpc/health.go index 1adc02c1b..c074d7546 100644 --- a/cncd/pipeline/pipeline/rpc/health.go +++ b/cncd/pipeline/pipeline/rpc/health.go @@ -6,6 +6,6 @@ import ( // Health defines a health-check connection. type Health interface { - // Check returns if server is healthy or not + // Check returns if server is healthy or not Check(c context.Context) (bool, error) } diff --git a/remote/bitbucketserver/convert_test.go b/remote/bitbucketserver/convert_test.go index f861ac689..13d6659b1 100644 --- a/remote/bitbucketserver/convert_test.go +++ b/remote/bitbucketserver/convert_test.go @@ -17,9 +17,9 @@ package bitbucketserver import ( "testing" + "github.com/franela/goblin" "github.com/laszlocph/woodpecker/model" "github.com/laszlocph/woodpecker/remote/bitbucketserver/internal" - "github.com/franela/goblin" "github.com/mrjones/oauth" ) diff --git a/remote/gitea/helper_test.go b/remote/gitea/helper_test.go index 7812625de..3ff90ad58 100644 --- a/remote/gitea/helper_test.go +++ b/remote/gitea/helper_test.go @@ -19,9 +19,9 @@ import ( "testing" "code.gitea.io/sdk/gitea" + "github.com/franela/goblin" "github.com/laszlocph/woodpecker/model" "github.com/laszlocph/woodpecker/remote/gitea/fixtures" - "github.com/franela/goblin" ) func Test_parse(t *testing.T) { diff --git a/remote/gitea/types.go b/remote/gitea/types.go index 4f0de583a..f78a679ed 100644 --- a/remote/gitea/types.go +++ b/remote/gitea/types.go @@ -69,13 +69,13 @@ type pullRequestHook struct { Email string `json:"email"` Avatar string `json:"avatar_url"` } `json:"user"` - Title string `json:"title"` - Body string `json:"body"` - State string `json:"state"` - URL string `json:"html_url"` - Mergeable bool `json:"mergeable"` - Merged bool `json:"merged"` - MergeBase string `json:"merge_base"` + Title string `json:"title"` + Body string `json:"body"` + State string `json:"state"` + URL string `json:"html_url"` + Mergeable bool `json:"mergeable"` + Merged bool `json:"merged"` + MergeBase string `json:"merge_base"` Base struct { Label string `json:"label"` Ref string `json:"ref"` diff --git a/remote/github/convert_test.go b/remote/github/convert_test.go index 67b5e3d31..12af5a084 100644 --- a/remote/github/convert_test.go +++ b/remote/github/convert_test.go @@ -17,8 +17,8 @@ package github import ( "testing" - "github.com/laszlocph/woodpecker/model" "github.com/google/go-github/github" + "github.com/laszlocph/woodpecker/model" "github.com/franela/goblin" ) diff --git a/remote/github/parse_test.go b/remote/github/parse_test.go index cc8cd4530..a2c957363 100644 --- a/remote/github/parse_test.go +++ b/remote/github/parse_test.go @@ -19,9 +19,9 @@ import ( "net/http" "testing" + "github.com/franela/goblin" "github.com/laszlocph/woodpecker/model" "github.com/laszlocph/woodpecker/remote/github/fixtures" - "github.com/franela/goblin" ) func Test_parser(t *testing.T) { diff --git a/remote/gitlab/gitlab_test.go b/remote/gitlab/gitlab_test.go index a61687179..150a9c30e 100644 --- a/remote/gitlab/gitlab_test.go +++ b/remote/gitlab/gitlab_test.go @@ -19,9 +19,9 @@ import ( "net/http" "testing" + "github.com/franela/goblin" "github.com/laszlocph/woodpecker/model" "github.com/laszlocph/woodpecker/remote/gitlab/testdata" - "github.com/franela/goblin" ) func Test_Gitlab(t *testing.T) { diff --git a/remote/gitlab/testdata/hooks.go b/remote/gitlab/testdata/hooks.go index ff5ad57bf..ef07684e9 100644 --- a/remote/gitlab/testdata/hooks.go +++ b/remote/gitlab/testdata/hooks.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab/testdata/oauth.go b/remote/gitlab/testdata/oauth.go index 6b975e577..4ae0cb5b4 100644 --- a/remote/gitlab/testdata/oauth.go +++ b/remote/gitlab/testdata/oauth.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab/testdata/projects.go b/remote/gitlab/testdata/projects.go index eb3cfc397..f51937d28 100644 --- a/remote/gitlab/testdata/projects.go +++ b/remote/gitlab/testdata/projects.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab/testdata/testdata.go b/remote/gitlab/testdata/testdata.go index 63db0c4f2..84e4f4542 100644 --- a/remote/gitlab/testdata/testdata.go +++ b/remote/gitlab/testdata/testdata.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab/testdata/users.go b/remote/gitlab/testdata/users.go index 247adc9aa..0198fc069 100644 --- a/remote/gitlab/testdata/users.go +++ b/remote/gitlab/testdata/users.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab3/gitlab_test.go b/remote/gitlab3/gitlab_test.go index 11a761496..99a707f7c 100644 --- a/remote/gitlab3/gitlab_test.go +++ b/remote/gitlab3/gitlab_test.go @@ -19,9 +19,9 @@ import ( "net/http" "testing" + "github.com/franela/goblin" "github.com/laszlocph/woodpecker/model" "github.com/laszlocph/woodpecker/remote/gitlab3/testdata" - "github.com/franela/goblin" ) func Test_Gitlab(t *testing.T) { diff --git a/remote/gitlab3/testdata/hooks.go b/remote/gitlab3/testdata/hooks.go index ff5ad57bf..ef07684e9 100644 --- a/remote/gitlab3/testdata/hooks.go +++ b/remote/gitlab3/testdata/hooks.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab3/testdata/oauth.go b/remote/gitlab3/testdata/oauth.go index 6b975e577..4ae0cb5b4 100644 --- a/remote/gitlab3/testdata/oauth.go +++ b/remote/gitlab3/testdata/oauth.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab3/testdata/projects.go b/remote/gitlab3/testdata/projects.go index eb3cfc397..f51937d28 100644 --- a/remote/gitlab3/testdata/projects.go +++ b/remote/gitlab3/testdata/projects.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab3/testdata/testdata.go b/remote/gitlab3/testdata/testdata.go index d9ea8995a..f197e7197 100644 --- a/remote/gitlab3/testdata/testdata.go +++ b/remote/gitlab3/testdata/testdata.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gitlab3/testdata/users.go b/remote/gitlab3/testdata/users.go index 247adc9aa..0198fc069 100644 --- a/remote/gitlab3/testdata/users.go +++ b/remote/gitlab3/testdata/users.go @@ -1,11 +1,11 @@ // Copyright 2018 Drone.IO Inc. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/remote/gogs/helper.go b/remote/gogs/helper.go index f8d6d1e61..8fe2547fc 100644 --- a/remote/gogs/helper.go +++ b/remote/gogs/helper.go @@ -22,8 +22,8 @@ import ( "strings" "time" - "github.com/laszlocph/woodpecker/model" "github.com/gogits/go-gogs-client" + "github.com/laszlocph/woodpecker/model" ) // helper function that converts a Gogs repository to a Drone repository. diff --git a/remote/gogs/types.go b/remote/gogs/types.go index 9b62c3e28..cd46942dd 100644 --- a/remote/gogs/types.go +++ b/remote/gogs/types.go @@ -68,14 +68,14 @@ type pullRequestHook struct { Email string `json:"email"` Avatar string `json:"avatar_url"` } `json:"user"` - Title string `json:"title"` - Body string `json:"body"` - State string `json:"state"` - URL string `json:"html_url"` - Mergeable bool `json:"mergeable"` - Merged bool `json:"merged"` - MergeBase string `json:"merge_base"` - BaseBranch string `json:"base_branch"` + Title string `json:"title"` + Body string `json:"body"` + State string `json:"state"` + URL string `json:"html_url"` + Mergeable bool `json:"mergeable"` + Merged bool `json:"merged"` + MergeBase string `json:"merge_base"` + BaseBranch string `json:"base_branch"` Base struct { Label string `json:"label"` Ref string `json:"ref"` diff --git a/router/middleware/header/header.go b/router/middleware/header/header.go index 81ed7332f..cfa29e2e7 100644 --- a/router/middleware/header/header.go +++ b/router/middleware/header/header.go @@ -18,8 +18,8 @@ import ( "net/http" "time" - "github.com/laszlocph/woodpecker/version" "github.com/gin-gonic/gin" + "github.com/laszlocph/woodpecker/version" ) // NoCache is a middleware function that appends headers diff --git a/router/middleware/remote.go b/router/middleware/remote.go index ef0233e6a..a7b1f04c4 100644 --- a/router/middleware/remote.go +++ b/router/middleware/remote.go @@ -15,8 +15,8 @@ package middleware import ( - "github.com/laszlocph/woodpecker/remote" "github.com/gin-gonic/gin" + "github.com/laszlocph/woodpecker/remote" ) // Remote is a middleware function that initializes the Remote and attaches to diff --git a/router/middleware/session/agent.go b/router/middleware/session/agent.go index 0fd8f8f96..dba23645f 100644 --- a/router/middleware/session/agent.go +++ b/router/middleware/session/agent.go @@ -15,8 +15,8 @@ package session import ( - "github.com/laszlocph/woodpecker/shared/token" "github.com/gin-gonic/gin" + "github.com/laszlocph/woodpecker/shared/token" ) // AuthorizeAgent authorizes requsts from build agents to access the queue. diff --git a/router/middleware/version.go b/router/middleware/version.go index 02c0c6217..a7b7c6bc6 100644 --- a/router/middleware/version.go +++ b/router/middleware/version.go @@ -15,8 +15,8 @@ package middleware import ( - "github.com/laszlocph/woodpecker/version" "github.com/gin-gonic/gin" + "github.com/laszlocph/woodpecker/version" ) // Version is a middleware function that appends the Drone version information diff --git a/server/build.go b/server/build.go index 74edae3e6..a1501484d 100644 --- a/server/build.go +++ b/server/build.go @@ -19,6 +19,7 @@ import ( "context" "fmt" "io" + "log" "net/http" "strconv" "time" @@ -185,14 +186,10 @@ func DeleteBuild(c *gin.Context) { continue } - proc.State = model.StatusKilled - proc.Stopped = time.Now().Unix() - if proc.Started == 0 { - proc.Started = proc.Stopped - } - proc.ExitCode = 137 // TODO cancel child procs - store.FromContext(c).ProcUpdate(proc) + if _, err = UpdateProcToStatusKilled(store.FromContext(c), *proc); err != nil { + log.Printf("error: done: cannot update proc_id %d state: %s", proc.ID, err) + } Config.Services.Queue.Error(context.Background(), fmt.Sprint(proc.ID), queue.ErrCancel) cancelled = true @@ -235,23 +232,19 @@ func ZombieKill(c *gin.Context) { for _, proc := range procs { if proc.Running() { - proc.State = model.StatusKilled - proc.ExitCode = 137 - proc.Stopped = time.Now().Unix() - if proc.Started == 0 { - proc.Started = proc.Stopped + if _, err := UpdateProcToStatusKilled(store.FromContext(c), *proc); err != nil { + log.Printf("error: done: cannot update proc_id %d state: %s", proc.ID, err) } + } else { + store.FromContext(c).ProcUpdate(proc) } - } - - for _, proc := range procs { - store.FromContext(c).ProcUpdate(proc) Config.Services.Queue.Error(context.Background(), fmt.Sprint(proc.ID), queue.ErrCancel) } - build.Status = model.StatusKilled - build.Finished = time.Now().Unix() - store.FromContext(c).UpdateBuild(build) + if _, err := UpdateToStatusKilled(store.FromContext(c), *build); err != nil { + c.AbortWithError(500, err) + return + } c.String(204, "") } @@ -275,9 +268,6 @@ func PostApproval(c *gin.Context) { c.String(500, "cannot decline a build with status %s", build.Status) return } - build.Status = model.StatusPending - build.Reviewed = time.Now().Unix() - build.Reviewer = user.Login // fetch the build file from the database configs, err := Config.Storage.Config.ConfigsForBuild(build.ID) @@ -289,12 +279,12 @@ func PostApproval(c *gin.Context) { netrc, err := remote_.Netrc(user, repo) if err != nil { - c.String(500, "Failed to generate netrc file. %s", err) + c.String(500, "failed to generate netrc file. %s", err) return } - if uerr := store.UpdateBuild(c, build); err != nil { - c.String(500, "error updating build. %s", uerr) + if build, err = UpdateToStatusPending(store.FromContext(c), *build, user.Login); err != nil { + c.String(500, "error updating build. %s", err) return } @@ -337,11 +327,9 @@ func PostApproval(c *gin.Context) { } buildItems, err := b.Build() if err != nil { - build.Status = model.StatusError - build.Started = time.Now().Unix() - build.Finished = build.Started - build.Error = err.Error() - store.UpdateBuild(c, build) + if _, err = UpdateToStatusError(store.FromContext(c), *build, err); err != nil { + logrus.Errorf("Error setting error status of build for %s#%d. %s", repo.FullName, build.Number, err) + } return } build = setBuildStepsOnBuild(b.Curr, buildItems) @@ -388,12 +376,8 @@ func PostDecline(c *gin.Context) { c.String(500, "cannot decline a build with status %s", build.Status) return } - build.Status = model.StatusDeclined - build.Reviewed = time.Now().Unix() - build.Reviewer = user.Login - err = store.UpdateBuild(c, build) - if err != nil { + if _, err = UpdateToStatusDeclined(store.FromContext(c), *build, user.Login); err != nil { c.String(500, "error updating build. %s", err) return } diff --git a/server/buildStatus_test.go b/server/buildStatus_test.go new file mode 100644 index 000000000..59bd40d63 --- /dev/null +++ b/server/buildStatus_test.go @@ -0,0 +1,120 @@ +// Copyright 2019 mhmxs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "errors" + "testing" + "time" + + "github.com/laszlocph/woodpecker/model" +) + +const TIMEOUT = 3 * time.Second + +type mockUpdateBuildStore struct { +} + +func (m *mockUpdateBuildStore) UpdateBuild(build *model.Build) error { + return nil +} + +func TestUpdateToStatusRunning(t *testing.T) { + t.Parallel() + + build, _ := UpdateToStatusRunning(&mockUpdateBuildStore{}, model.Build{}, int64(1)) + + if model.StatusRunning != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusRunning, build.Status) + } else if int64(1) != build.Started { + t.Errorf("Build started not equals 1 != %d", build.Started) + } +} + +func TestUpdateToStatusPending(t *testing.T) { + t.Parallel() + + now := time.Now().Unix() + + build, _ := UpdateToStatusPending(&mockUpdateBuildStore{}, model.Build{}, "Reviewer") + + if model.StatusPending != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusPending, build.Status) + } else if "Reviewer" != build.Reviewer { + t.Errorf("Reviewer not equals 'Reviewer' != '%s'", build.Reviewer) + } else if now > build.Reviewed { + t.Errorf("Reviewed not updated %d !< %d", now, build.Reviewed) + } +} + +func TestUpdateToStatusDeclined(t *testing.T) { + t.Parallel() + + now := time.Now().Unix() + + build, _ := UpdateToStatusDeclined(&mockUpdateBuildStore{}, model.Build{}, "Reviewer") + + if model.StatusDeclined != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusDeclined, build.Status) + } else if "Reviewer" != build.Reviewer { + t.Errorf("Reviewer not equals 'Reviewer' != '%s'", build.Reviewer) + } else if now > build.Reviewed { + t.Errorf("Reviewed not updated %d !< %d", now, build.Reviewed) + } +} + +func TestUpdateToStatusToDone(t *testing.T) { + t.Parallel() + + build, _ := UpdateStatusToDone(&mockUpdateBuildStore{}, model.Build{}, "status", int64(1)) + + if "status" != build.Status { + t.Errorf("Build status not equals 'status' != '%s'", build.Status) + } else if int64(1) != build.Finished { + t.Errorf("Build finished not equals 1 != %d", build.Finished) + } +} + +func TestUpdateToStatusError(t *testing.T) { + t.Parallel() + + now := time.Now().Unix() + + build, _ := UpdateToStatusError(&mockUpdateBuildStore{}, model.Build{}, errors.New("error")) + + if "error" != build.Error { + t.Errorf("Build error not equals 'error' != '%s'", build.Error) + } else if model.StatusError != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusError, build.Status) + } else if now > build.Started { + t.Errorf("Started not updated %d !< %d", now, build.Started) + } else if build.Started != build.Finished { + t.Errorf("Build started and finished not equals %d != %d", build.Started, build.Finished) + } +} + +func TestUpdateToStatusKilled(t *testing.T) { + t.Parallel() + + now := time.Now().Unix() + + build, _ := UpdateToStatusKilled(&mockUpdateBuildStore{}, model.Build{}) + + if model.StatusKilled != build.Status { + t.Errorf("Build status not equals '%s' != '%s'", model.StatusKilled, build.Status) + } else if now > build.Finished { + t.Errorf("Finished not updated %d !< %d", now, build.Finished) + } +} diff --git a/server/builsStatus.go b/server/builsStatus.go new file mode 100644 index 000000000..b03b91736 --- /dev/null +++ b/server/builsStatus.go @@ -0,0 +1,65 @@ +// Copyright 2019 mhmxs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "time" + + "github.com/laszlocph/woodpecker/model" +) + +type UpdateBuildStore interface { + UpdateBuild(*model.Build) error +} + +func UpdateToStatusRunning(store UpdateBuildStore, build model.Build, started int64) (*model.Build, error) { + build.Status = model.StatusRunning + build.Started = started + return &build, store.UpdateBuild(&build) +} + +func UpdateToStatusPending(store UpdateBuildStore, build model.Build, reviewer string) (*model.Build, error) { + build.Reviewer = reviewer + build.Status = model.StatusPending + build.Reviewed = time.Now().Unix() + return &build, store.UpdateBuild(&build) +} + +func UpdateToStatusDeclined(store UpdateBuildStore, build model.Build, reviewer string) (*model.Build, error) { + build.Reviewer = reviewer + build.Status = model.StatusDeclined + build.Reviewed = time.Now().Unix() + return &build, store.UpdateBuild(&build) +} + +func UpdateStatusToDone(store UpdateBuildStore, build model.Build, status string, stopped int64) (*model.Build, error) { + build.Status = status + build.Finished = stopped + return &build, store.UpdateBuild(&build) +} + +func UpdateToStatusError(store UpdateBuildStore, build model.Build, err error) (*model.Build, error) { + build.Error = err.Error() + build.Status = model.StatusError + build.Started = time.Now().Unix() + build.Finished = build.Started + return &build, store.UpdateBuild(&build) +} + +func UpdateToStatusKilled(store UpdateBuildStore, build model.Build) (*model.Build, error) { + build.Status = model.StatusKilled + build.Finished = time.Now().Unix() + return &build, store.UpdateBuild(&build) +} diff --git a/server/file.go b/server/file.go index b7cbfe60c..efa497761 100644 --- a/server/file.go +++ b/server/file.go @@ -20,9 +20,9 @@ import ( "strconv" "strings" + "github.com/gin-gonic/gin" "github.com/laszlocph/woodpecker/router/middleware/session" "github.com/laszlocph/woodpecker/store" - "github.com/gin-gonic/gin" ) // FileList gets a list file by build. diff --git a/server/hook.go b/server/hook.go index da3df0dbc..fdbe8f8dd 100644 --- a/server/hook.go +++ b/server/hook.go @@ -259,11 +259,9 @@ func PostHook(c *gin.Context) { } buildItems, err := b.Build() if err != nil { - build.Status = model.StatusError - build.Started = time.Now().Unix() - build.Finished = build.Started - build.Error = err.Error() - store.UpdateBuild(c, build) + if _, err = UpdateToStatusError(store.FromContext(c), *build, err); err != nil { + logrus.Errorf("Error setting error status of build for %s#%d. %s", repo.FullName, build.Number, err) + } return } build = setBuildStepsOnBuild(b.Curr, buildItems) diff --git a/server/metrics/prometheus.go b/server/metrics/prometheus.go index c2ecaecb1..9f6dbb01c 100644 --- a/server/metrics/prometheus.go +++ b/server/metrics/prometheus.go @@ -18,8 +18,8 @@ import ( "errors" "fmt" - "github.com/laszlocph/woodpecker/server" "github.com/gin-gonic/gin" + "github.com/laszlocph/woodpecker/server" "github.com/prometheus/client_golang/prometheus/promhttp" ) diff --git a/server/procStatus.go b/server/procStatus.go new file mode 100644 index 000000000..6808a070f --- /dev/null +++ b/server/procStatus.go @@ -0,0 +1,89 @@ +// Copyright 2019 mhmxs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "time" + + "github.com/laszlocph/woodpecker/cncd/pipeline/pipeline/rpc" + "github.com/laszlocph/woodpecker/model" +) + +type UpdateProcStore interface { + ProcUpdate(*model.Proc) error +} + +func UpdateProcStatus(store UpdateProcStore, proc model.Proc, state rpc.State, started int64) (*model.Proc, error) { + if state.Exited { + proc.Stopped = state.Finished + proc.ExitCode = state.ExitCode + proc.Error = state.Error + proc.State = model.StatusSuccess + if state.ExitCode != 0 || state.Error != "" { + proc.State = model.StatusFailure + } + if state.ExitCode == 137 { + proc.State = model.StatusKilled + } + } else { + proc.Started = state.Started + proc.State = model.StatusRunning + } + + if proc.Started == 0 && proc.Stopped != 0 { + proc.Started = started + } + return &proc, store.ProcUpdate(&proc) +} + +func UpdateProcToStatusStarted(store UpdateProcStore, proc model.Proc, state rpc.State) (*model.Proc, error) { + proc.Started = state.Started + proc.State = model.StatusRunning + return &proc, store.ProcUpdate(&proc) +} + +func UpdateProcToStatusSkipped(store UpdateProcStore, proc model.Proc, stopped int64) (*model.Proc, error) { + proc.State = model.StatusSkipped + if proc.Started != 0 { + proc.State = model.StatusSuccess // for deamons that are killed + proc.Stopped = stopped + } + return &proc, store.ProcUpdate(&proc) +} + +func UpdateProcStatusToDone(store UpdateProcStore, proc model.Proc, state rpc.State) (*model.Proc, error) { + proc.Stopped = state.Finished + proc.Error = state.Error + proc.ExitCode = state.ExitCode + if state.Started == 0 { + proc.State = model.StatusSkipped + } else { + proc.State = model.StatusSuccess + } + if proc.ExitCode != 0 || proc.Error != "" { + proc.State = model.StatusFailure + } + return &proc, store.ProcUpdate(&proc) +} + +func UpdateProcToStatusKilled(store UpdateProcStore, proc model.Proc) (*model.Proc, error) { + proc.State = model.StatusKilled + proc.Stopped = time.Now().Unix() + if proc.Started == 0 { + proc.Started = proc.Stopped + } + proc.ExitCode = 137 + return &proc, store.ProcUpdate(&proc) +} diff --git a/server/rpc.go b/server/rpc.go index 34b301d2b..48808a385 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -179,27 +179,7 @@ func (s *RPC) Update(c context.Context, id string, state rpc.State) error { return err } - if state.Exited { - proc.Stopped = state.Finished - proc.ExitCode = state.ExitCode - proc.Error = state.Error - proc.State = model.StatusSuccess - if state.ExitCode != 0 || state.Error != "" { - proc.State = model.StatusFailure - } - if state.ExitCode == 137 { - proc.State = model.StatusKilled - } - } else { - proc.Started = state.Started - proc.State = model.StatusRunning - } - - if proc.Started == 0 && proc.Stopped != 0 { - proc.Started = build.Started - } - - if err := s.store.ProcUpdate(proc); err != nil { + if proc, err = UpdateProcStatus(s.store, *proc, state, build.Started); err != nil { log.Printf("error: rpc.update: cannot update proc: %s", err) } @@ -326,9 +306,7 @@ func (s *RPC) Init(c context.Context, id string, state rpc.State) error { } if build.Status == model.StatusPending { - build.Status = model.StatusRunning - build.Started = state.Started - if err := s.store.UpdateBuild(build); err != nil { + if build, err = UpdateToStatusRunning(s.store, *build, state.Started); err != nil { log.Printf("error: init: cannot update build_id %d state: %s", build.ID, err) } } @@ -348,9 +326,8 @@ func (s *RPC) Init(c context.Context, id string, state rpc.State) error { s.pubsub.Publish(c, "topic/events", message) }() - proc.Started = state.Started - proc.State = model.StatusRunning - return s.store.ProcUpdate(proc) + _, err = UpdateProcToStatusStarted(s.store, *proc, state) + return err } // Done implements the rpc.Done function @@ -378,7 +355,9 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { return err } - s.updateProcState(proc, state) + if proc, err = UpdateProcStatusToDone(s.store, *proc, state); err != nil { + log.Printf("error: done: cannot update proc_id %d state: %s", proc.ID, err) + } var queueErr error if proc.Failing() { @@ -394,9 +373,7 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { s.completeChildrenIfParentCompleted(procs, proc) if !isThereRunningStage(procs) { - build.Status = buildStatus(procs) - build.Finished = proc.Stopped - if err := s.store.UpdateBuild(build); err != nil { + if build, err = UpdateStatusToDone(s.store, *build, buildStatus(procs), proc.Stopped); err != nil { log.Printf("error: done: cannot update build_id %d final state: %s", build.ID, err) } @@ -444,32 +421,10 @@ func (s *RPC) Log(c context.Context, id string, line *rpc.Line) error { return nil } -func (s *RPC) updateProcState(proc *model.Proc, state rpc.State) { - proc.Stopped = state.Finished - proc.Error = state.Error - proc.ExitCode = state.ExitCode - if state.Started == 0 { - proc.State = model.StatusSkipped - } else { - proc.State = model.StatusSuccess - } - if proc.ExitCode != 0 || proc.Error != "" { - proc.State = model.StatusFailure - } - if err := s.store.ProcUpdate(proc); err != nil { - log.Printf("error: done: cannot update proc_id %d state: %s", proc.ID, err) - } -} - func (s *RPC) completeChildrenIfParentCompleted(procs []*model.Proc, completedProc *model.Proc) { for _, p := range procs { if p.Running() && p.PPID == completedProc.PID { - p.State = model.StatusSkipped - if p.Started != 0 { - p.State = model.StatusSuccess // for deamons that are killed - p.Stopped = completedProc.Stopped - } - if err := s.store.ProcUpdate(p); err != nil { + if _, err := UpdateProcToStatusSkipped(s.store, *p, completedProc.Stopped); err != nil { log.Printf("error: done: cannot update proc_id %d child state: %s", p.ID, err) } } diff --git a/server/web/web.go b/server/web/web.go index db42511a1..43e322b66 100644 --- a/server/web/web.go +++ b/server/web/web.go @@ -24,10 +24,10 @@ import ( "path/filepath" "time" + "github.com/laszlocph/woodpecker-ui/dist" "github.com/laszlocph/woodpecker/model" "github.com/laszlocph/woodpecker/shared/token" "github.com/laszlocph/woodpecker/version" - "github.com/laszlocph/woodpecker-ui/dist" "github.com/dimfeld/httptreemux" ) diff --git a/server/z.go b/server/z.go index 32949fbd7..6dd9ea5f2 100644 --- a/server/z.go +++ b/server/z.go @@ -15,9 +15,9 @@ package server import ( + "github.com/gin-gonic/gin" "github.com/laszlocph/woodpecker/store" "github.com/laszlocph/woodpecker/version" - "github.com/gin-gonic/gin" ) // Health endpoint returns a 500 if the server state is unhealthy. diff --git a/store/datastore/repos_test.go b/store/datastore/repos_test.go index 0ef77e068..9f7b909a3 100644 --- a/store/datastore/repos_test.go +++ b/store/datastore/repos_test.go @@ -17,8 +17,8 @@ package datastore import ( "testing" - "github.com/laszlocph/woodpecker/model" "github.com/franela/goblin" + "github.com/laszlocph/woodpecker/model" ) func TestRepos(t *testing.T) { diff --git a/store/datastore/users_test.go b/store/datastore/users_test.go index 2bcf2575d..7ba76c9d0 100644 --- a/store/datastore/users_test.go +++ b/store/datastore/users_test.go @@ -17,8 +17,8 @@ package datastore import ( "testing" - "github.com/laszlocph/woodpecker/model" "github.com/franela/goblin" + "github.com/laszlocph/woodpecker/model" ) func TestUsers(t *testing.T) { diff --git a/store/store.go b/store/store.go index eca28fcc0..52cd13dcb 100644 --- a/store/store.go +++ b/store/store.go @@ -249,7 +249,3 @@ func GetBuildQueue(c context.Context) ([]*model.Feed, error) { func CreateBuild(c context.Context, build *model.Build, procs ...*model.Proc) error { return FromContext(c).CreateBuild(build, procs...) } - -func UpdateBuild(c context.Context, build *model.Build) error { - return FromContext(c).UpdateBuild(build) -}