added endpoints for branch summary
This commit is contained in:
parent
6815ec61cb
commit
a88f9d0743
30 changed files with 933 additions and 148 deletions
|
@ -143,16 +143,17 @@ tasks:
|
|||
test-postgres:
|
||||
env:
|
||||
DRONE_DATABASE_DRIVER: postgres
|
||||
DRONE_DATABASE_DATASOURCE: host=localhost user=postgres dbname=postgres sslmode=disable
|
||||
DRONE_DATABASE_DATASOURCE: host=localhost user=postgres password=postgres dbname=postgres sslmode=disable
|
||||
GO111MODULE: 'on'
|
||||
cmds:
|
||||
- cmd: docker kill postgres
|
||||
ignore_error: true
|
||||
silent: true
|
||||
- silent: true
|
||||
silent: false
|
||||
- silent: false
|
||||
cmd: >
|
||||
docker run
|
||||
-p 5432:5432
|
||||
--env POSTGRES_PASSWORD=postgres
|
||||
--env POSTGRES_USER=postgres
|
||||
--name postgres
|
||||
--detach
|
||||
|
|
|
@ -71,6 +71,14 @@ type BuildStore interface {
|
|||
// ListRef returns a list of builds from the datastore by ref.
|
||||
ListRef(context.Context, int64, string, int, int) ([]*Build, error)
|
||||
|
||||
// LatestBranches returns the latest builds from the
|
||||
// datastore by branch.
|
||||
LatestBranches(context.Context, int64) ([]*Build, error)
|
||||
|
||||
// LatestPulls returns the latest builds from the
|
||||
// datastore by pull requeset.
|
||||
LatestPulls(context.Context, int64) ([]*Build, error)
|
||||
|
||||
// Pending returns a list of pending builds from the
|
||||
// datastore by repository id (DEPRECATED).
|
||||
Pending(context.Context) ([]*Build, error)
|
||||
|
@ -88,6 +96,15 @@ type BuildStore interface {
|
|||
// Delete deletes a build from the datastore.
|
||||
Delete(context.Context, *Build) error
|
||||
|
||||
// DeletePull deletes a pull request index from the datastore.
|
||||
DeletePull(context.Context, int64, int) error
|
||||
|
||||
// DeleteBranch deletes a branch index from the datastore.
|
||||
DeleteBranch(context.Context, int64, string) error
|
||||
|
||||
// DeleteDeploy deletes a deploy index from the datastore.
|
||||
DeleteDeploy(context.Context, int64, string) error
|
||||
|
||||
// Purge deletes builds from the database where the build number is less than n.
|
||||
Purge(context.Context, int64, int64) error
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
// Hook action constants.
|
||||
const (
|
||||
ActionOpen = "open"
|
||||
ActionClose = "close"
|
||||
ActionCreate = "create"
|
||||
ActionDelete = "delete"
|
||||
ActionSync = "sync"
|
||||
|
|
2
go.mod
2
go.mod
|
@ -100,3 +100,5 @@ require (
|
|||
)
|
||||
|
||||
replace github.com/h2non/gock => gopkg.in/h2non/gock.v1 v1.0.14
|
||||
|
||||
go 1.13
|
||||
|
|
|
@ -27,7 +27,9 @@ import (
|
|||
"github.com/drone/drone/handler/api/queue"
|
||||
"github.com/drone/drone/handler/api/repos"
|
||||
"github.com/drone/drone/handler/api/repos/builds"
|
||||
"github.com/drone/drone/handler/api/repos/builds/branches"
|
||||
"github.com/drone/drone/handler/api/repos/builds/logs"
|
||||
"github.com/drone/drone/handler/api/repos/builds/pulls"
|
||||
"github.com/drone/drone/handler/api/repos/builds/stages"
|
||||
"github.com/drone/drone/handler/api/repos/collabs"
|
||||
"github.com/drone/drone/handler/api/repos/crons"
|
||||
|
@ -184,6 +186,12 @@ func (s Server) Handler() http.Handler {
|
|||
r.Get("/", builds.HandleList(s.Repos, s.Builds))
|
||||
r.With(acl.CheckWriteAccess()).Post("/", builds.HandleCreate(s.Repos, s.Commits, s.Triggerer))
|
||||
|
||||
r.Get("/branches", branches.HandleList(s.Repos, s.Builds))
|
||||
r.With(acl.CheckWriteAccess()).Delete("/branches/*", branches.HandleDelete(s.Repos, s.Builds))
|
||||
|
||||
r.Get("/pulls", pulls.HandleList(s.Repos, s.Builds))
|
||||
r.With(acl.CheckWriteAccess()).Delete("/pulls/{pull}", pulls.HandleDelete(s.Repos, s.Builds))
|
||||
|
||||
r.Get("/latest", builds.HandleLast(s.Repos, s.Builds, s.Stages))
|
||||
r.Get("/{number}", builds.HandleFind(s.Repos, s.Builds, s.Stages))
|
||||
r.Get("/{number}/logs/{stage}/{step}", logs.HandleFind(s.Repos, s.Builds, s.Stages, s.Steps, s.Logs))
|
||||
|
|
15
handler/api/repos/builds/branches/create.go
Normal file
15
handler/api/repos/builds/branches/create.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2019 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.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package branches
|
5
handler/api/repos/builds/branches/create_test.go
Normal file
5
handler/api/repos/builds/branches/create_test.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package branches
|
62
handler/api/repos/builds/branches/delete.go
Normal file
62
handler/api/repos/builds/branches/delete.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2019 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.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package branches
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
"github.com/drone/drone/logger"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleDelete returns an http.HandlerFunc that handles an
|
||||
// http.Request to delete a branch entry from the datastore.
|
||||
func HandleDelete(
|
||||
repos core.RepositoryStore,
|
||||
builds core.BuildStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
branch = chi.URLParam(r, "*")
|
||||
)
|
||||
repo, err := repos.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
logger.FromRequest(r).
|
||||
WithError(err).
|
||||
WithField("namespace", namespace).
|
||||
WithField("name", name).
|
||||
Debugln("api: cannot find repository")
|
||||
return
|
||||
}
|
||||
|
||||
err = builds.DeleteBranch(r.Context(), repo.ID, branch)
|
||||
if err != nil {
|
||||
render.InternalError(w, err)
|
||||
logger.FromRequest(r).
|
||||
WithError(err).
|
||||
WithField("namespace", namespace).
|
||||
WithField("name", name).
|
||||
Debugln("api: cannot delete branch")
|
||||
} else {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
}
|
5
handler/api/repos/builds/branches/delete_test.go
Normal file
5
handler/api/repos/builds/branches/delete_test.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package branches
|
61
handler/api/repos/builds/branches/list.go
Normal file
61
handler/api/repos/builds/branches/list.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2019 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.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package branches
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
"github.com/drone/drone/logger"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleList returns an http.HandlerFunc that writes a json-encoded
|
||||
// list of build history to the response body.
|
||||
func HandleList(
|
||||
repos core.RepositoryStore,
|
||||
builds core.BuildStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
)
|
||||
repo, err := repos.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
logger.FromRequest(r).
|
||||
WithError(err).
|
||||
WithField("namespace", namespace).
|
||||
WithField("name", name).
|
||||
Debugln("api: cannot find repository")
|
||||
return
|
||||
}
|
||||
|
||||
results, err := builds.LatestBranches(r.Context(), repo.ID)
|
||||
if err != nil {
|
||||
render.InternalError(w, err)
|
||||
logger.FromRequest(r).
|
||||
WithError(err).
|
||||
WithField("namespace", namespace).
|
||||
WithField("name", name).
|
||||
Debugln("api: cannot list builds")
|
||||
} else {
|
||||
render.JSON(w, results, 200)
|
||||
}
|
||||
}
|
||||
}
|
5
handler/api/repos/builds/branches/list_test.go
Normal file
5
handler/api/repos/builds/branches/list_test.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package branches
|
|
@ -10,8 +10,8 @@ import (
|
|||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/mock"
|
||||
"github.com/drone/drone/handler/api/errors"
|
||||
"github.com/drone/drone/mock"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/golang/mock/gomock"
|
||||
|
|
15
handler/api/repos/builds/pulls/create.go
Normal file
15
handler/api/repos/builds/pulls/create.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2019 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.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pulls
|
5
handler/api/repos/builds/pulls/create_test.go
Normal file
5
handler/api/repos/builds/pulls/create_test.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pulls
|
62
handler/api/repos/builds/pulls/delete.go
Normal file
62
handler/api/repos/builds/pulls/delete.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2019 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.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
"github.com/drone/drone/logger"
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleDelete returns an http.HandlerFunc that handles an
|
||||
// http.Request to delete a branch entry from the datastore.
|
||||
func HandleDelete(
|
||||
repos core.RepositoryStore,
|
||||
builds core.BuildStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
number, _ = strconv.Atoi(chi.URLParam(r, "pull"))
|
||||
)
|
||||
repo, err := repos.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
logger.FromRequest(r).
|
||||
WithError(err).
|
||||
WithField("namespace", namespace).
|
||||
WithField("name", name).
|
||||
Debugln("api: cannot find repository")
|
||||
return
|
||||
}
|
||||
|
||||
err = builds.DeletePull(r.Context(), repo.ID, number)
|
||||
if err != nil {
|
||||
render.InternalError(w, err)
|
||||
logger.FromRequest(r).
|
||||
WithError(err).
|
||||
WithField("namespace", namespace).
|
||||
WithField("name", name).
|
||||
Debugln("api: cannot delete pr")
|
||||
} else {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
}
|
5
handler/api/repos/builds/pulls/delete_test.go
Normal file
5
handler/api/repos/builds/pulls/delete_test.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pulls
|
61
handler/api/repos/builds/pulls/list.go
Normal file
61
handler/api/repos/builds/pulls/list.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2019 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.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pulls
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
"github.com/drone/drone/logger"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleList returns an http.HandlerFunc that writes a json-encoded
|
||||
// list of build history to the response body.
|
||||
func HandleList(
|
||||
repos core.RepositoryStore,
|
||||
builds core.BuildStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
)
|
||||
repo, err := repos.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
logger.FromRequest(r).
|
||||
WithError(err).
|
||||
WithField("namespace", namespace).
|
||||
WithField("name", name).
|
||||
Debugln("api: cannot find repository")
|
||||
return
|
||||
}
|
||||
|
||||
results, err := builds.LatestPulls(r.Context(), repo.ID)
|
||||
if err != nil {
|
||||
render.InternalError(w, err)
|
||||
logger.FromRequest(r).
|
||||
WithError(err).
|
||||
WithField("namespace", namespace).
|
||||
WithField("name", name).
|
||||
Debugln("api: cannot list builds")
|
||||
} else {
|
||||
render.JSON(w, results, 200)
|
||||
}
|
||||
}
|
||||
}
|
5
handler/api/repos/builds/pulls/list_test.go
Normal file
5
handler/api/repos/builds/pulls/list_test.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package pulls
|
|
@ -84,146 +84,6 @@ func HandleHook(
|
|||
// TODO handle ping requests
|
||||
// TODO consider using scm.Repository in the function callback.
|
||||
|
||||
// // TODO break this to a separate function that
|
||||
// // we can unit test.
|
||||
// fn := func(webhook interface{}) (string, error) {
|
||||
// var remote scm.Repository
|
||||
// switch v := core.(type) {
|
||||
// case *scm.PushHook:
|
||||
// remote = v.Repo
|
||||
// case *scm.BranchHook:
|
||||
// remote = v.Repo
|
||||
// case *scm.TagHook:
|
||||
// remote = v.Repo
|
||||
// case *scm.PullRequestHook:
|
||||
// remote = v.Repo
|
||||
// case *scm.IssueHook:
|
||||
// remote = v.Repo
|
||||
// case *scm.IssueCommentHook:
|
||||
// remote = v.Repo
|
||||
// case *scm.PullRequestCommentHook:
|
||||
// remote = v.Repo
|
||||
// case *scm.ReviewCommentHook:
|
||||
// remote = v.Repo
|
||||
// }
|
||||
// repo, err := repos.FindName(r.Context(), remote.Namespace, remote.Name)
|
||||
// if err != nil {
|
||||
// hlog.FromRequest(r).Error().
|
||||
// Err(err).
|
||||
// Str("namespace", remote.Namespace).
|
||||
// Str("name", remote.Name).
|
||||
// Msg("cannot find repository")
|
||||
// return "", err
|
||||
// }
|
||||
// return repo.Token, nil
|
||||
// }
|
||||
|
||||
// hook, err := client.Webhooks.Parse(r, fn)
|
||||
// if err != nil {
|
||||
// hlog.FromRequest(r).Error().
|
||||
// Err(err).
|
||||
// Msg("cannot parse webhook")
|
||||
// writeError(w, err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// var name, namespace string
|
||||
// var base = new(core.Build)
|
||||
// switch v := hook.(type) {
|
||||
// case *scm.PushHook:
|
||||
// namespace, name = v.Repo.Namespace, v.Repo.Name
|
||||
// base.Event = core.EventPush
|
||||
|
||||
// base.Link = v.Commit.Link
|
||||
// base.Timestamp = v.Commit.Author.Date.Unix()
|
||||
// base.Title = ""
|
||||
// base.Message = v.Commit.Message
|
||||
// base.Before = ""
|
||||
// base.After = v.Commit.Sha
|
||||
// base.Ref = v.Ref
|
||||
// base.Source = strings.TrimPrefix(v.Ref, "refs/heads/")
|
||||
// base.Target = strings.TrimPrefix(v.Ref, "refs/heads/")
|
||||
// base.Author = v.Commit.Author.Login
|
||||
// base.AuthorName = v.Commit.Author.Name
|
||||
// base.AuthorEmail = v.Commit.Author.Email
|
||||
// base.AuthorAvatar = v.Commit.Author.Avatar
|
||||
// base.Sender = v.Sender.Login
|
||||
|
||||
// // TODO: this is a deficiency with gogs
|
||||
// if base.AuthorAvatar == "" {
|
||||
// base.AuthorAvatar = v.Sender.Avatar
|
||||
// }
|
||||
// case *scm.TagHook:
|
||||
|
||||
// namespace, name = v.Repo.Namespace, v.Repo.Name
|
||||
// base.Event = core.EventTag
|
||||
// base.Action = v.Action.String()
|
||||
|
||||
// base.Link = "" // TODO
|
||||
// base.Timestamp = 0 // TODO
|
||||
// base.Title = ""
|
||||
// base.Message = "" // TODO
|
||||
// base.Before = "" // TODO
|
||||
// base.After = v.Ref.Sha
|
||||
// base.Ref = v.Ref.Name // TODO prepend refs/tags?
|
||||
// base.Source = ""
|
||||
// base.Target = ""
|
||||
// base.Author = v.Sender.Login
|
||||
// base.AuthorName = v.Sender.Name
|
||||
// base.AuthorEmail = v.Sender.Email
|
||||
// base.AuthorAvatar = v.Sender.Avatar
|
||||
// base.Sender = v.Sender.Login
|
||||
|
||||
// switch v.Action {
|
||||
// case scm.ActionCreate:
|
||||
// default:
|
||||
// hlog.FromRequest(r).Debug().
|
||||
// Str("namespace", namespace).
|
||||
// Str("name", name).
|
||||
// Str("event", base.Event).
|
||||
// Str("action", base.Action).
|
||||
// Msgf("ignore webhook with action %s", base.Action)
|
||||
// w.WriteHeader(200)
|
||||
// return
|
||||
// }
|
||||
|
||||
// case *scm.PullRequestHook:
|
||||
// namespace, name = v.Repo.Namespace, v.Repo.Name
|
||||
// base.Event = core.EventPullRequest
|
||||
// base.Action = v.Action.String()
|
||||
|
||||
// base.Link = v.PullRequest.Link
|
||||
// base.Timestamp = v.PullRequest.Created.Unix()
|
||||
// base.Title = v.PullRequest.Title
|
||||
// base.Message = "" // TODO
|
||||
// base.Before = "" // TODO
|
||||
// base.After = v.PullRequest.Sha
|
||||
// base.Ref = v.PullRequest.Ref
|
||||
// base.Source = v.PullRequest.Source
|
||||
// base.Target = v.PullRequest.Target
|
||||
// base.Author = v.PullRequest.Author.Login
|
||||
// base.AuthorName = v.PullRequest.Author.Name
|
||||
// base.AuthorEmail = v.PullRequest.Author.Email
|
||||
// base.AuthorAvatar = v.PullRequest.Author.Avatar
|
||||
// base.Sender = v.Sender.Login
|
||||
|
||||
// switch v.Action {
|
||||
// case scm.ActionCreate, scm.ActionOpen, scm.ActionSync:
|
||||
// default:
|
||||
// hlog.FromRequest(r).Debug().
|
||||
// Str("namespace", namespace).
|
||||
// Str("name", name).
|
||||
// Str("event", base.Event).
|
||||
// Str("action", base.Action).
|
||||
// Msgf("ignore pull request hook with action %s", base.Action)
|
||||
// w.WriteHeader(200)
|
||||
// return
|
||||
// }
|
||||
// default:
|
||||
// w.WriteHeader(200)
|
||||
// return
|
||||
// }
|
||||
|
||||
log := logrus.WithFields(logrus.Fields{
|
||||
"namespace": remote.Namespace,
|
||||
"name": remote.Name,
|
||||
|
@ -251,6 +111,19 @@ func HandleHook(
|
|||
ctx = logger.WithContext(ctx, log)
|
||||
defer cancel()
|
||||
|
||||
if hook.Event == core.EventPush && hook.Action == core.ActionDelete {
|
||||
log.WithField("branch", hook.Target).Debugln("branch deleted")
|
||||
builds.DeleteBranch(ctx, repo.ID, hook.Target)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
if hook.Event == core.EventPullRequest && hook.Action == core.ActionClose {
|
||||
log.WithField("ref", hook.Ref).Debugln("pull request closed")
|
||||
builds.DeletePull(ctx, repo.ID, scm.ExtractPullRequest(hook.Ref))
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
builds, err := triggerer.Trigger(ctx, repo, hook)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
|
|
|
@ -737,6 +737,48 @@ func (mr *MockBuildStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Cal
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockBuildStore)(nil).Delete), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteBranch mocks base method
|
||||
func (m *MockBuildStore) DeleteBranch(arg0 context.Context, arg1 int64, arg2 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteBranch", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteBranch indicates an expected call of DeleteBranch
|
||||
func (mr *MockBuildStoreMockRecorder) DeleteBranch(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBranch", reflect.TypeOf((*MockBuildStore)(nil).DeleteBranch), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// DeleteDeploy mocks base method
|
||||
func (m *MockBuildStore) DeleteDeploy(arg0 context.Context, arg1 int64, arg2 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteDeploy", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteDeploy indicates an expected call of DeleteDeploy
|
||||
func (mr *MockBuildStoreMockRecorder) DeleteDeploy(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDeploy", reflect.TypeOf((*MockBuildStore)(nil).DeleteDeploy), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// DeletePull mocks base method
|
||||
func (m *MockBuildStore) DeletePull(arg0 context.Context, arg1 int64, arg2 int) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeletePull", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeletePull indicates an expected call of DeletePull
|
||||
func (mr *MockBuildStoreMockRecorder) DeletePull(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePull", reflect.TypeOf((*MockBuildStore)(nil).DeletePull), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// Find mocks base method
|
||||
func (m *MockBuildStore) Find(arg0 context.Context, arg1 int64) (*core.Build, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -782,6 +824,36 @@ func (mr *MockBuildStoreMockRecorder) FindRef(arg0, arg1, arg2 interface{}) *gom
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindRef", reflect.TypeOf((*MockBuildStore)(nil).FindRef), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// LatestBranches mocks base method
|
||||
func (m *MockBuildStore) LatestBranches(arg0 context.Context, arg1 int64) ([]*core.Build, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LatestBranches", arg0, arg1)
|
||||
ret0, _ := ret[0].([]*core.Build)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LatestBranches indicates an expected call of LatestBranches
|
||||
func (mr *MockBuildStoreMockRecorder) LatestBranches(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestBranches", reflect.TypeOf((*MockBuildStore)(nil).LatestBranches), arg0, arg1)
|
||||
}
|
||||
|
||||
// LatestPulls mocks base method
|
||||
func (m *MockBuildStore) LatestPulls(arg0 context.Context, arg1 int64) ([]*core.Build, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LatestPulls", arg0, arg1)
|
||||
ret0, _ := ret[0].([]*core.Build)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LatestPulls indicates an expected call of LatestPulls
|
||||
func (mr *MockBuildStoreMockRecorder) LatestPulls(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestPulls", reflect.TypeOf((*MockBuildStore)(nil).LatestPulls), arg0, arg1)
|
||||
}
|
||||
|
||||
// List mocks base method
|
||||
func (m *MockBuildStore) List(arg0 context.Context, arg1 int64, arg2, arg3 int) ([]*core.Build, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
|
@ -232,6 +232,23 @@ func (p *parser) Parse(req *http.Request, secretFunc func(string) string) (*core
|
|||
}
|
||||
return hook, repo, nil
|
||||
case *scm.PullRequestHook:
|
||||
|
||||
// TODO(bradrydzewski) cleanup the pr close hook code.
|
||||
if v.Action == scm.ActionClose {
|
||||
return &core.Hook{
|
||||
Trigger: core.TriggerHook,
|
||||
Event: core.EventPullRequest,
|
||||
Action: core.ActionClose,
|
||||
After: v.PullRequest.Sha,
|
||||
Ref: v.PullRequest.Ref,
|
||||
}, &core.Repository{
|
||||
UID: v.Repo.ID,
|
||||
Namespace: v.Repo.Namespace,
|
||||
Name: v.Repo.Name,
|
||||
Slug: scm.Join(v.Repo.Namespace, v.Repo.Name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if v.Action != scm.ActionOpen && v.Action != scm.ActionSync {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
@ -284,6 +301,23 @@ func (p *parser) Parse(req *http.Request, secretFunc func(string) string) (*core
|
|||
}
|
||||
return hook, repo, nil
|
||||
case *scm.BranchHook:
|
||||
|
||||
// TODO(bradrydzewski) cleanup the branch hook code.
|
||||
if v.Action == scm.ActionDelete {
|
||||
return &core.Hook{
|
||||
Trigger: core.TriggerHook,
|
||||
Event: core.EventPush,
|
||||
After: v.Ref.Sha,
|
||||
Action: core.ActionDelete,
|
||||
Target: scm.TrimRef(v.Ref.Name),
|
||||
}, &core.Repository{
|
||||
UID: v.Repo.ID,
|
||||
Namespace: v.Repo.Namespace,
|
||||
Name: v.Repo.Name,
|
||||
Slug: scm.Join(v.Repo.Namespace, v.Repo.Name),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if v.Action != scm.ActionCreate {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
|
|
@ -16,11 +16,18 @@ package build
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/store/shared/db"
|
||||
)
|
||||
|
||||
// regular expression to extract the pull request number
|
||||
// from the git ref (e.g. refs/pulls/{d}/head)
|
||||
var pr = regexp.MustCompile("\\d+")
|
||||
|
||||
// New returns a new Buildcore.
|
||||
func New(db *db.DB) core.BuildStore {
|
||||
return &buildStore{db}
|
||||
|
@ -122,6 +129,42 @@ func (s *buildStore) ListRef(ctx context.Context, repo int64, ref string, limit,
|
|||
return out, err
|
||||
}
|
||||
|
||||
// LatestBranches returns a list of the latest build by branch.
|
||||
func (s *buildStore) LatestBranches(ctx context.Context, repo int64) ([]*core.Build, error) {
|
||||
return s.latest(ctx, repo, "branch")
|
||||
}
|
||||
|
||||
// LatestPulls returns a list of the latest builds by pull requests.
|
||||
func (s *buildStore) LatestPulls(ctx context.Context, repo int64) ([]*core.Build, error) {
|
||||
return s.latest(ctx, repo, "pull_request")
|
||||
}
|
||||
|
||||
// LatestDeploys returns a list of the latest builds by target deploy.
|
||||
func (s *buildStore) LatestDeploys(ctx context.Context, repo int64) ([]*core.Build, error) {
|
||||
return s.latest(ctx, repo, "deployment")
|
||||
}
|
||||
|
||||
func (s *buildStore) latest(ctx context.Context, repo int64, event string) ([]*core.Build, error) {
|
||||
var out []*core.Build
|
||||
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
||||
params := map[string]interface{}{
|
||||
"latest_repo_id": repo,
|
||||
"latest_type": event,
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(queryLatestList, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := queryer.Query(stmt, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err = scanRows(rows)
|
||||
return err
|
||||
})
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Pending returns a list of pending builds from the datastore by repository id.
|
||||
func (s *buildStore) Pending(ctx context.Context) ([]*core.Build, error) {
|
||||
var out []*core.Build
|
||||
|
@ -152,10 +195,31 @@ func (s *buildStore) Running(ctx context.Context) ([]*core.Build, error) {
|
|||
|
||||
// Create persists a build to the datacore.
|
||||
func (s *buildStore) Create(ctx context.Context, build *core.Build, stages []*core.Stage) error {
|
||||
if s.db.Driver() == db.Postgres {
|
||||
return s.createPostgres(ctx, build, stages)
|
||||
var err error
|
||||
switch s.db.Driver() {
|
||||
case db.Postgres:
|
||||
err = s.createPostgres(ctx, build, stages)
|
||||
default:
|
||||
err = s.create(ctx, build, stages)
|
||||
}
|
||||
return s.create(ctx, build, stages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var event, name string
|
||||
switch build.Event {
|
||||
case core.EventPullRequest:
|
||||
event = "pull_request"
|
||||
name = pr.FindString(build.Ref)
|
||||
case core.EventPush:
|
||||
event = "branch"
|
||||
name = build.Target
|
||||
case core.EventPromote, core.EventRollback:
|
||||
event = "deployment"
|
||||
name = build.Deploy
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return s.index(ctx, build.ID, build.RepoID, event, name)
|
||||
}
|
||||
|
||||
func (s *buildStore) create(ctx context.Context, build *core.Build, stages []*core.Stage) error {
|
||||
|
@ -255,6 +319,33 @@ func (s *buildStore) Update(ctx context.Context, build *core.Build) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (s *buildStore) index(ctx context.Context, build, repo int64, event, name string) error {
|
||||
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
params := map[string]interface{}{
|
||||
"latest_repo_id": repo,
|
||||
"latest_build_id": build,
|
||||
"latest_type": event,
|
||||
"latest_name": name,
|
||||
"latest_created": time.Now().Unix(),
|
||||
"latest_updated": time.Now().Unix(),
|
||||
"latest_deleted": time.Now().Unix(),
|
||||
}
|
||||
stmtInsert := stmtInsertLatest
|
||||
switch s.db.Driver() {
|
||||
case db.Postgres:
|
||||
stmtInsert = stmtInsertLatestPg
|
||||
case db.Mysql:
|
||||
stmtInsert = stmtInsertLatestMysql
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(stmtInsert, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = execer.Exec(stmt, args...)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Delete deletes a build from the datacore.
|
||||
func (s *buildStore) Delete(ctx context.Context, build *core.Build) error {
|
||||
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
|
@ -268,6 +359,57 @@ func (s *buildStore) Delete(ctx context.Context, build *core.Build) error {
|
|||
})
|
||||
}
|
||||
|
||||
// DeletePull deletes a pull request index from the datastore.
|
||||
func (s *buildStore) DeletePull(ctx context.Context, repo int64, number int) error {
|
||||
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
params := map[string]interface{}{
|
||||
"latest_repo_id": repo,
|
||||
"latest_name": fmt.Sprint(number),
|
||||
"latest_type": "pull_request",
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(stmtDeleteLatest, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = execer.Exec(stmt, args...)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteBranch deletes a branch index from the datastore.
|
||||
func (s *buildStore) DeleteBranch(ctx context.Context, repo int64, branch string) error {
|
||||
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
params := map[string]interface{}{
|
||||
"latest_repo_id": repo,
|
||||
"latest_name": branch,
|
||||
"latest_type": "branch",
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(stmtDeleteLatest, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = execer.Exec(stmt, args...)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteDeploy deletes a deploy index from the datastore.
|
||||
func (s *buildStore) DeleteDeploy(ctx context.Context, repo int64, environment string) error {
|
||||
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
params := map[string]interface{}{
|
||||
"latest_repo_id": repo,
|
||||
"latest_name": environment,
|
||||
"latest_type": "deployment",
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(stmtDeleteLatest, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = execer.Exec(stmt, args...)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Purge deletes builds from the database where the build number is less than n.
|
||||
func (s *buildStore) Purge(ctx context.Context, repo, number int64) error {
|
||||
build := &core.Build{
|
||||
|
@ -580,3 +722,86 @@ DELETE FROM builds
|
|||
WHERE build_repo_id = :build_repo_id
|
||||
AND build_number < :build_number
|
||||
`
|
||||
|
||||
//
|
||||
// latest builds index
|
||||
//
|
||||
|
||||
const stmtInsertLatest = `
|
||||
INSERT INTO latest (
|
||||
latest_repo_id
|
||||
,latest_build_id
|
||||
,latest_type
|
||||
,latest_name
|
||||
,latest_created
|
||||
,latest_updated
|
||||
,latest_deleted
|
||||
) VALUES (
|
||||
:latest_repo_id
|
||||
,:latest_build_id
|
||||
,:latest_type
|
||||
,:latest_name
|
||||
,:latest_created
|
||||
,:latest_updated
|
||||
,:latest_deleted
|
||||
) ON CONFLICT (latest_repo_id, latest_type, latest_name)
|
||||
DO UPDATE SET latest_build_id = EXCLUDED.latest_build_id
|
||||
`
|
||||
|
||||
const stmtInsertLatestPg = `
|
||||
INSERT INTO latest (
|
||||
latest_repo_id
|
||||
,latest_build_id
|
||||
,latest_type
|
||||
,latest_name
|
||||
,latest_created
|
||||
,latest_updated
|
||||
,latest_deleted
|
||||
) VALUES (
|
||||
:latest_repo_id
|
||||
,:latest_build_id
|
||||
,:latest_type
|
||||
,:latest_name
|
||||
,:latest_created
|
||||
,:latest_updated
|
||||
,:latest_deleted
|
||||
) ON CONFLICT (latest_repo_id, latest_type, latest_name)
|
||||
DO UPDATE SET latest_build_id = EXCLUDED.latest_build_id
|
||||
`
|
||||
|
||||
const stmtInsertLatestMysql = `
|
||||
INSERT INTO latest (
|
||||
latest_repo_id
|
||||
,latest_build_id
|
||||
,latest_type
|
||||
,latest_name
|
||||
,latest_created
|
||||
,latest_updated
|
||||
,latest_deleted
|
||||
) VALUES (
|
||||
:latest_repo_id
|
||||
,:latest_build_id
|
||||
,:latest_type
|
||||
,:latest_name
|
||||
,:latest_created
|
||||
,:latest_updated
|
||||
,:latest_deleted
|
||||
) ON DUPLICATE KEY UPDATE latest_build_id = :latest_build_id
|
||||
`
|
||||
|
||||
const stmtDeleteLatest = `
|
||||
DELETE FROM latest
|
||||
WHERE latest_repo_id = :latest_repo_id
|
||||
AND latest_type = :latest_type
|
||||
AND latest_name = :latest_name
|
||||
`
|
||||
|
||||
const queryLatestList = queryBase + `
|
||||
FROM builds
|
||||
WHERE build_id IN (
|
||||
SELECT latest_build_id
|
||||
FROM latest
|
||||
WHERE latest_repo_id = :latest_repo_id
|
||||
AND latest_type = :latest_type
|
||||
)
|
||||
`
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/store/shared/db"
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/store/shared/db"
|
||||
|
||||
"github.com/drone/drone/store/shared/db/dbtest"
|
||||
)
|
||||
|
@ -34,6 +34,7 @@ func TestBuild(t *testing.T) {
|
|||
t.Run("Count", testBuildCount(store))
|
||||
t.Run("Pending", testBuildPending(store))
|
||||
t.Run("Running", testBuildRunning(store))
|
||||
t.Run("Latest", testBuildLatest(store))
|
||||
}
|
||||
|
||||
func testBuildCreate(store *buildStore) func(t *testing.T) {
|
||||
|
@ -41,7 +42,9 @@ func testBuildCreate(store *buildStore) func(t *testing.T) {
|
|||
build := &core.Build{
|
||||
RepoID: 1,
|
||||
Number: 99,
|
||||
Event: core.EventPush,
|
||||
Ref: "refs/heads/master",
|
||||
Target: "master",
|
||||
}
|
||||
stage := &core.Stage{
|
||||
RepoID: 42,
|
||||
|
@ -307,6 +310,113 @@ func testBuildRunning(store *buildStore) func(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testBuildLatest(store *buildStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
store.db.Update(func(execer db.Execer, binder db.Binder) error {
|
||||
execer.Exec("DELETE FROM stages")
|
||||
execer.Exec("DELETE FROM latest")
|
||||
execer.Exec("DELETE FROM builds")
|
||||
return nil
|
||||
})
|
||||
|
||||
//
|
||||
// step 1: insert the initial builds
|
||||
//
|
||||
|
||||
build := &core.Build{
|
||||
RepoID: 1,
|
||||
Number: 99,
|
||||
Event: core.EventPush,
|
||||
Ref: "refs/heads/master",
|
||||
Target: "master",
|
||||
}
|
||||
|
||||
err := store.Create(noContext, build, []*core.Stage{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
develop := &core.Build{
|
||||
RepoID: 1,
|
||||
Number: 100,
|
||||
Event: core.EventPush,
|
||||
Ref: "refs/heads/develop",
|
||||
Target: "develop",
|
||||
}
|
||||
err = store.Create(noContext, develop, []*core.Stage{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = store.Create(noContext, &core.Build{
|
||||
RepoID: 1,
|
||||
Number: 999,
|
||||
Event: core.EventPullRequest,
|
||||
Ref: "refs/pulls/10/head",
|
||||
Source: "develop",
|
||||
Target: "master",
|
||||
}, []*core.Stage{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// step 2: verify the latest build number was captured
|
||||
//
|
||||
|
||||
latest, _ := store.LatestBranches(noContext, build.RepoID)
|
||||
if len(latest) != 2 {
|
||||
t.Errorf("Expect latest branch list == 1, got %d", len(latest))
|
||||
return
|
||||
}
|
||||
if got, want := latest[0].Number, build.Number; got != want {
|
||||
t.Errorf("Expected latest master build number %d, got %d", want, got)
|
||||
}
|
||||
if got, want := latest[1].Number, develop.Number; got != want {
|
||||
t.Errorf("Expected latest develop build number %d, got %d", want, got)
|
||||
return
|
||||
}
|
||||
|
||||
build = &core.Build{
|
||||
RepoID: 1,
|
||||
Number: 101,
|
||||
Event: core.EventPush,
|
||||
Ref: "refs/heads/master",
|
||||
Target: "master",
|
||||
}
|
||||
err = store.Create(noContext, build, []*core.Stage{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
latest, _ = store.LatestBranches(noContext, build.RepoID)
|
||||
if len(latest) != 2 {
|
||||
t.Errorf("Expect latest branch list == 1")
|
||||
return
|
||||
}
|
||||
if got, want := latest[1].Number, build.Number; got != want {
|
||||
t.Errorf("Expected latest build number %d, got %d", want, got)
|
||||
return
|
||||
}
|
||||
|
||||
err = store.DeleteBranch(noContext, build.RepoID, build.Target)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
latest, _ = store.LatestBranches(noContext, build.RepoID)
|
||||
if len(latest) != 1 {
|
||||
t.Errorf("Expect latest branch list == 1 after delete")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBuild(item *core.Build) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
if got, want := item.RepoID, int64(1); got != want {
|
||||
|
|
|
@ -47,6 +47,7 @@ func Reset(d *db.DB) {
|
|||
tx.Exec("DELETE FROM logs")
|
||||
tx.Exec("DELETE FROM steps")
|
||||
tx.Exec("DELETE FROM stages")
|
||||
tx.Exec("DELETE FROM latest")
|
||||
tx.Exec("DELETE FROM builds")
|
||||
tx.Exec("DELETE FROM perms")
|
||||
tx.Exec("DELETE FROM repos")
|
||||
|
|
|
@ -136,6 +136,14 @@ var migrations = []struct {
|
|||
name: "alter-table-builds-add-column-deploy-id",
|
||||
stmt: alterTableBuildsAddColumnDeployId,
|
||||
},
|
||||
{
|
||||
name: "create-table-latest",
|
||||
stmt: createTableLatest,
|
||||
},
|
||||
{
|
||||
name: "create-index-latest-repo",
|
||||
stmt: createIndexLatestRepo,
|
||||
},
|
||||
}
|
||||
|
||||
// Migrate performs the database migration. If the migration fails
|
||||
|
@ -605,3 +613,24 @@ CREATE TABLE IF NOT EXISTS orgsecrets (
|
|||
var alterTableBuildsAddColumnDeployId = `
|
||||
ALTER TABLE builds ADD COLUMN build_deploy_id INTEGER NOT NULL DEFAULT 0;
|
||||
`
|
||||
|
||||
//
|
||||
// 014_create_table_refs.sql
|
||||
//
|
||||
|
||||
var createTableLatest = `
|
||||
CREATE TABLE IF NOT EXISTS latest (
|
||||
latest_repo_id INTEGER
|
||||
,latest_build_id INTEGER
|
||||
,latest_type VARCHAR(50)
|
||||
,latest_name VARCHAR(500)
|
||||
,latest_created INTEGER
|
||||
,latest_updated INTEGER
|
||||
,latest_deleted INTEGER
|
||||
,PRIMARY KEY(latest_repo_id, latest_type, latest_name)
|
||||
);
|
||||
`
|
||||
|
||||
var createIndexLatestRepo = `
|
||||
CREATE INDEX ix_latest_repo ON latest (latest_repo_id);
|
||||
`
|
||||
|
|
16
store/shared/migrate/mysql/files/014_create_table_refs.sql
Normal file
16
store/shared/migrate/mysql/files/014_create_table_refs.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
-- name: create-table-latest
|
||||
|
||||
CREATE TABLE IF NOT EXISTS latest (
|
||||
latest_repo_id INTEGER
|
||||
,latest_build_id INTEGER
|
||||
,latest_type VARCHAR(50)
|
||||
,latest_name VARCHAR(500)
|
||||
,latest_created INTEGER
|
||||
,latest_updated INTEGER
|
||||
,latest_deleted INTEGER
|
||||
,PRIMARY KEY(latest_repo_id, latest_type, latest_name)
|
||||
);
|
||||
|
||||
-- name: create-index-latest-repo
|
||||
|
||||
CREATE INDEX ix_latest_repo ON latest (latest_repo_id);
|
|
@ -132,6 +132,14 @@ var migrations = []struct {
|
|||
name: "alter-table-builds-add-column-deploy-id",
|
||||
stmt: alterTableBuildsAddColumnDeployId,
|
||||
},
|
||||
{
|
||||
name: "create-table-latest",
|
||||
stmt: createTableLatest,
|
||||
},
|
||||
{
|
||||
name: "create-index-latest-repo",
|
||||
stmt: createIndexLatestRepo,
|
||||
},
|
||||
}
|
||||
|
||||
// Migrate performs the database migration. If the migration fails
|
||||
|
@ -583,3 +591,24 @@ CREATE TABLE IF NOT EXISTS orgsecrets (
|
|||
var alterTableBuildsAddColumnDeployId = `
|
||||
ALTER TABLE builds ADD COLUMN build_deploy_id INTEGER NOT NULL DEFAULT 0;
|
||||
`
|
||||
|
||||
//
|
||||
// 015_create_table_refs.sql
|
||||
//
|
||||
|
||||
var createTableLatest = `
|
||||
CREATE TABLE IF NOT EXISTS latest (
|
||||
latest_repo_id INTEGER
|
||||
,latest_build_id INTEGER
|
||||
,latest_type VARCHAR(50)
|
||||
,latest_name VARCHAR(500)
|
||||
,latest_created INTEGER
|
||||
,latest_updated INTEGER
|
||||
,latest_deleted INTEGER
|
||||
,PRIMARY KEY(latest_repo_id, latest_type, latest_name)
|
||||
);
|
||||
`
|
||||
|
||||
var createIndexLatestRepo = `
|
||||
CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id);
|
||||
`
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
-- name: create-table-latest
|
||||
|
||||
CREATE TABLE IF NOT EXISTS latest (
|
||||
latest_repo_id INTEGER
|
||||
,latest_build_id INTEGER
|
||||
,latest_type VARCHAR(50)
|
||||
,latest_name VARCHAR(500)
|
||||
,latest_created INTEGER
|
||||
,latest_updated INTEGER
|
||||
,latest_deleted INTEGER
|
||||
,PRIMARY KEY(latest_repo_id, latest_type, latest_name)
|
||||
);
|
||||
|
||||
-- name: create-index-latest-repo
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id);
|
|
@ -132,6 +132,14 @@ var migrations = []struct {
|
|||
name: "alter-table-builds-add-column-deploy-id",
|
||||
stmt: alterTableBuildsAddColumnDeployId,
|
||||
},
|
||||
{
|
||||
name: "create-table-latest",
|
||||
stmt: createTableLatest,
|
||||
},
|
||||
{
|
||||
name: "create-index-latest-repo",
|
||||
stmt: createIndexLatestRepo,
|
||||
},
|
||||
}
|
||||
|
||||
// Migrate performs the database migration. If the migration fails
|
||||
|
@ -585,3 +593,24 @@ CREATE TABLE IF NOT EXISTS orgsecrets (
|
|||
var alterTableBuildsAddColumnDeployId = `
|
||||
ALTER TABLE builds ADD COLUMN build_deploy_id NUMBER NOT NULL DEFAULT 0;
|
||||
`
|
||||
|
||||
//
|
||||
// 014_create_table_refs.sql
|
||||
//
|
||||
|
||||
var createTableLatest = `
|
||||
CREATE TABLE IF NOT EXISTS latest (
|
||||
latest_repo_id INTEGER
|
||||
,latest_build_id INTEGER
|
||||
,latest_type TEXT -- branch | tag | pull_request | promote
|
||||
,latest_name TEXT -- master | v1.0.0, | 42 | production
|
||||
,latest_created INTEGER
|
||||
,latest_updated INTEGER
|
||||
,latest_deleted INTEGER
|
||||
,PRIMARY KEY(latest_repo_id, latest_type, latest_name)
|
||||
);
|
||||
`
|
||||
|
||||
var createIndexLatestRepo = `
|
||||
CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id);
|
||||
`
|
||||
|
|
16
store/shared/migrate/sqlite/files/014_create_table_refs.sql
Normal file
16
store/shared/migrate/sqlite/files/014_create_table_refs.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
-- name: create-table-latest
|
||||
|
||||
CREATE TABLE IF NOT EXISTS latest (
|
||||
latest_repo_id INTEGER
|
||||
,latest_build_id INTEGER
|
||||
,latest_type TEXT -- branch | tag | pull_request | promote
|
||||
,latest_name TEXT -- master | v1.0.0, | 42 | production
|
||||
,latest_created INTEGER
|
||||
,latest_updated INTEGER
|
||||
,latest_deleted INTEGER
|
||||
,PRIMARY KEY(latest_repo_id, latest_type, latest_name)
|
||||
);
|
||||
|
||||
-- name: create-index-latest-repo
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_latest_repo ON latest (latest_repo_id);
|
Loading…
Reference in a new issue