service to batch cancel pending builds
This commit is contained in:
parent
c8d74457ab
commit
3fcbf1c78f
21 changed files with 664 additions and 29 deletions
27
core/cancel.go
Normal file
27
core/cancel.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
// 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 core
|
||||
|
||||
import "context"
|
||||
|
||||
// Canceler cancels a build.
|
||||
type Canceler interface {
|
||||
// Cancel cancels the provided build.
|
||||
Cancel(context.Context, *Repository, *Build) error
|
||||
|
||||
// CancelPending cancels all pending builds of the same
|
||||
// type of as the provided build.
|
||||
CancelPending(context.Context, *Repository, *Build) error
|
||||
}
|
|
@ -17,6 +17,7 @@ package core
|
|||
// Hook event constants.
|
||||
const (
|
||||
EventCron = "cron"
|
||||
EventCustom = "custom"
|
||||
EventPush = "push"
|
||||
EventPullRequest = "pull_request"
|
||||
EventTag = "tag"
|
||||
|
|
|
@ -51,6 +51,8 @@ type (
|
|||
Protected bool `json:"protected"`
|
||||
IgnoreForks bool `json:"ignore_forks"`
|
||||
IgnorePulls bool `json:"ignore_pull_requests"`
|
||||
CancelPulls bool `json:"auto_cancel_pull_requests"`
|
||||
CancelPush bool `json:"auto_cancel_branch"`
|
||||
Timeout int64 `json:"timeout"`
|
||||
Counter int64 `json:"counter"`
|
||||
Synced int64 `json:"synced"`
|
||||
|
|
|
@ -69,7 +69,7 @@ func HandleCreate(
|
|||
|
||||
hook := &core.Hook{
|
||||
Trigger: user.Login,
|
||||
Event: core.EventPush,
|
||||
Event: core.EventCustom,
|
||||
Link: commit.Link,
|
||||
Timestamp: commit.Author.Date,
|
||||
Title: "", // we expect this to be empty.
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestCreate(t *testing.T) {
|
|||
if got, want := hook.Trigger, mockUser.Login; got != want {
|
||||
t.Errorf("Want hook Trigger By %s, got %s", want, got)
|
||||
}
|
||||
if got, want := hook.Event, core.EventPush; got != want {
|
||||
if got, want := hook.Event, core.EventCustom; got != want {
|
||||
t.Errorf("Want hook Event %s, got %s", want, got)
|
||||
}
|
||||
if got, want := hook.Link, mockCommit.Link; got != want {
|
||||
|
|
232
service/canceler/canceler.go
Normal file
232
service/canceler/canceler.go
Normal file
|
@ -0,0 +1,232 @@
|
|||
// 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 canceler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
builds core.BuildStore
|
||||
repos core.RepositoryStore
|
||||
scheduler core.Scheduler
|
||||
stages core.StageStore
|
||||
status core.StatusService
|
||||
steps core.StepStore
|
||||
users core.UserStore
|
||||
webhooks core.WebhookSender
|
||||
}
|
||||
|
||||
// New returns a new cancellation service that encapsulates
|
||||
// all cancellation operations.
|
||||
func New(
|
||||
builds core.BuildStore,
|
||||
repos core.RepositoryStore,
|
||||
scheduler core.Scheduler,
|
||||
stages core.StageStore,
|
||||
status core.StatusService,
|
||||
steps core.StepStore,
|
||||
users core.UserStore,
|
||||
webhooks core.WebhookSender,
|
||||
) core.Canceler {
|
||||
return &service{
|
||||
builds: builds,
|
||||
repos: repos,
|
||||
scheduler: scheduler,
|
||||
stages: stages,
|
||||
status: status,
|
||||
steps: steps,
|
||||
users: users,
|
||||
webhooks: webhooks,
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel cancels a build.
|
||||
func (s *service) Cancel(ctx context.Context, repo *core.Repository, build *core.Build) error {
|
||||
return s.cancel(ctx, repo, build, core.StatusKilled)
|
||||
}
|
||||
|
||||
// CancelPending cancels all pending builds of the same event
|
||||
// and reference with lower build numbers.
|
||||
func (s *service) CancelPending(ctx context.Context, repo *core.Repository, build *core.Build) error {
|
||||
switch build.Event {
|
||||
// on the push and pull request builds can be automatically
|
||||
// cancelled by the system.
|
||||
case core.EventPush, core.EventPullRequest:
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
// get a list of all incomplete builds from the database
|
||||
// for all repositories. this will need to be filtered.
|
||||
incomplete, err := s.repos.ListIncomplete(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var result error
|
||||
for _, item := range incomplete {
|
||||
// ignore incomplete items in the list that do
|
||||
// not match the repository or build, are already
|
||||
// running, or are newer than the current build.
|
||||
if !match(build, item) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := s.cancel(ctx, repo, build, core.StatusSkipped)
|
||||
if err != nil {
|
||||
result = multierror.Append(result, err)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *service) cancel(ctx context.Context, repo *core.Repository, build *core.Build, status string) error {
|
||||
logger := logrus.WithFields(
|
||||
logrus.Fields{
|
||||
"repo": repo.Slug,
|
||||
"ref": build.Ref,
|
||||
"build": build.Number,
|
||||
"event": build.Event,
|
||||
"status": build.Status,
|
||||
},
|
||||
)
|
||||
|
||||
// do not cancel the build if the build status is
|
||||
// complete. only cancel the build if the status is
|
||||
// running or pending.
|
||||
switch build.Status {
|
||||
case core.StatusPending, core.StatusRunning:
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
// update the build status to killed. if the update fails
|
||||
// due to an optimistic lock error it means the build has
|
||||
// already started, and should now be ignored.
|
||||
build.Status = status
|
||||
build.Finished = time.Now().Unix()
|
||||
if build.Started == 0 {
|
||||
build.Started = time.Now().Unix()
|
||||
}
|
||||
|
||||
err := s.builds.Update(ctx, build)
|
||||
if err != nil {
|
||||
logger.WithError(err).
|
||||
Warnln("api: cannot update build status to cancelled")
|
||||
return err
|
||||
}
|
||||
|
||||
// notify the scheduler to cancel the build. this will
|
||||
// instruct runners subscribing to the scheduler to
|
||||
// cancel execution.
|
||||
err = s.scheduler.Cancel(ctx, build.ID)
|
||||
if err != nil {
|
||||
logger.WithError(err).
|
||||
Warnln("api: cannot signal cancelled build is complete")
|
||||
}
|
||||
|
||||
// update the commit status in the remote source
|
||||
// control management system.
|
||||
user, err := s.users.Find(ctx, repo.UserID)
|
||||
if err == nil {
|
||||
err := s.status.Send(ctx, user, &core.StatusInput{
|
||||
Repo: repo,
|
||||
Build: build,
|
||||
})
|
||||
if err != nil {
|
||||
logger.WithError(err).
|
||||
Debugln("api: cannot set status")
|
||||
}
|
||||
}
|
||||
|
||||
stages, err := s.stages.ListSteps(ctx, build.ID)
|
||||
if err != nil {
|
||||
logger.WithError(err).
|
||||
Debugln("api: cannot list build stages")
|
||||
}
|
||||
|
||||
// update the status of all steps to indicate they
|
||||
// were killed or skipped.
|
||||
for _, stage := range stages {
|
||||
if stage.IsDone() {
|
||||
continue
|
||||
}
|
||||
if stage.Started != 0 {
|
||||
stage.Status = core.StatusKilled
|
||||
} else {
|
||||
stage.Status = core.StatusSkipped
|
||||
stage.Started = time.Now().Unix()
|
||||
}
|
||||
stage.Stopped = time.Now().Unix()
|
||||
err := s.stages.Update(ctx, stage)
|
||||
if err != nil {
|
||||
logger.WithError(err).
|
||||
WithField("stage", stage.Number).
|
||||
Debugln("api: cannot update stage status")
|
||||
}
|
||||
|
||||
// update the status of all steps to indicate they
|
||||
// were killed or skipped.
|
||||
for _, step := range stage.Steps {
|
||||
if step.IsDone() {
|
||||
continue
|
||||
}
|
||||
if step.Started != 0 {
|
||||
step.Status = core.StatusKilled
|
||||
} else {
|
||||
step.Status = core.StatusSkipped
|
||||
step.Started = time.Now().Unix()
|
||||
}
|
||||
step.Stopped = time.Now().Unix()
|
||||
step.ExitCode = 130
|
||||
err := s.steps.Update(ctx, step)
|
||||
if err != nil {
|
||||
logger.WithError(err).
|
||||
WithField("stage", stage.Number).
|
||||
WithField("step", step.Number).
|
||||
Debugln("api: cannot update step status")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.WithError(err).
|
||||
Debugln("api: successfully cancelled build")
|
||||
|
||||
build.Stages = stages
|
||||
|
||||
// trigger a webhook to notify subscribing systems that
|
||||
// the build was cancelled.
|
||||
payload := &core.WebhookData{
|
||||
Event: core.WebhookEventBuild,
|
||||
Action: core.WebhookActionUpdated,
|
||||
Repo: repo,
|
||||
Build: build,
|
||||
}
|
||||
err = s.webhooks.Send(ctx, payload)
|
||||
if err != nil {
|
||||
logger.WithError(err).
|
||||
Warnln("manager: cannot send global webhook")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
126
service/canceler/canceler_test.go
Normal file
126
service/canceler/canceler_test.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
// 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 canceler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/mock"
|
||||
"github.com/go-chi/chi"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
var noContext = context.Background()
|
||||
|
||||
func TestCancelPending_IgnoreEvent(t *testing.T) {
|
||||
ignore := []string{
|
||||
core.EventCron,
|
||||
core.EventCustom,
|
||||
core.EventPromote,
|
||||
core.EventRollback,
|
||||
core.EventTag,
|
||||
}
|
||||
for _, event := range ignore {
|
||||
s := new(service)
|
||||
err := s.CancelPending(noContext, nil, &core.Build{Event: event})
|
||||
if err != nil {
|
||||
t.Errorf("Expect cancel skipped for event type %s", event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancel(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
mockStages := []*core.Stage{
|
||||
{Status: core.StatusPassing},
|
||||
{
|
||||
Status: core.StatusPending,
|
||||
Steps: []*core.Step{
|
||||
{Status: core.StatusPassing},
|
||||
{Status: core.StatusPending},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mockBuildCopy := new(core.Build)
|
||||
*mockBuildCopy = *mockBuild
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
|
||||
builds := mock.NewMockBuildStore(controller)
|
||||
builds.EXPECT().Update(gomock.Any(), mockBuildCopy).Return(nil)
|
||||
|
||||
users := mock.NewMockUserStore(controller)
|
||||
users.EXPECT().Find(gomock.Any(), mockRepo.UserID).Return(mockUser, nil)
|
||||
|
||||
stages := mock.NewMockStageStore(controller)
|
||||
stages.EXPECT().ListSteps(gomock.Any(), mockBuild.ID).Return(mockStages, nil)
|
||||
stages.EXPECT().Update(gomock.Any(), mockStages[1]).Return(nil)
|
||||
|
||||
steps := mock.NewMockStepStore(controller)
|
||||
steps.EXPECT().Update(gomock.Any(), mockStages[1].Steps[1]).Return(nil)
|
||||
|
||||
status := mock.NewMockStatusService(controller)
|
||||
status.EXPECT().Send(gomock.Any(), mockUser, gomock.Any()).Return(nil)
|
||||
|
||||
webhook := mock.NewMockWebhookSender(controller)
|
||||
webhook.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil)
|
||||
|
||||
scheduler := mock.NewMockScheduler(controller)
|
||||
scheduler.EXPECT().Cancel(gomock.Any(), mockBuild.ID).Return(nil)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("number", "1")
|
||||
|
||||
s := New(builds, repos, scheduler, stages, status, steps, users, webhook)
|
||||
err := s.Cancel(noContext, mockRepo, mockBuildCopy)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
mockRepo = &core.Repository{
|
||||
ID: 1,
|
||||
Namespace: "octocat",
|
||||
Name: "hello-world",
|
||||
Slug: "octocat/hello-world",
|
||||
Counter: 42,
|
||||
Branch: "master",
|
||||
}
|
||||
|
||||
mockBuild = &core.Build{
|
||||
ID: 1,
|
||||
Number: 1,
|
||||
RepoID: 1,
|
||||
Status: core.StatusPending,
|
||||
Event: core.EventPush,
|
||||
Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
|
||||
Timestamp: 1299283200,
|
||||
Message: "first commit",
|
||||
Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e",
|
||||
After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
|
||||
Ref: "refs/heads/master",
|
||||
Source: "master",
|
||||
Target: "master",
|
||||
Author: "octocat",
|
||||
AuthorName: "The Octocat",
|
||||
AuthorEmail: "octocat@hello-world.com",
|
||||
AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231",
|
||||
Sender: "octocat",
|
||||
}
|
||||
|
||||
mockUser = &core.User{
|
||||
ID: 1,
|
||||
Login: "octocat",
|
||||
}
|
||||
)
|
46
service/canceler/match.go
Normal file
46
service/canceler/match.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// 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 canceler
|
||||
|
||||
import "github.com/drone/drone/core"
|
||||
|
||||
func match(build *core.Build, with *core.Repository) bool {
|
||||
// filter out existing builds for others
|
||||
// repositories.
|
||||
if with.ID != build.RepoID {
|
||||
return false
|
||||
}
|
||||
// filter out builds that are newer than
|
||||
// the current build.
|
||||
if with.Build.Number >= build.Number {
|
||||
return false
|
||||
}
|
||||
// filter out builds that are not in a
|
||||
// pending state.
|
||||
if with.Build.Status != core.StatusPending {
|
||||
return false
|
||||
}
|
||||
// filter out builds that do not match
|
||||
// the same event type.
|
||||
if with.Build.Event != build.Event {
|
||||
return false
|
||||
}
|
||||
// filter out builds that do not match
|
||||
// the same reference.
|
||||
if with.Build.Ref != build.Ref {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
95
service/canceler/match_test.go
Normal file
95
service/canceler/match_test.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// 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 canceler
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
tests := []struct {
|
||||
build *core.Build
|
||||
repo *core.Repository
|
||||
want bool
|
||||
}{
|
||||
// does not match repository id
|
||||
{
|
||||
build: &core.Build{RepoID: 2},
|
||||
repo: &core.Repository{ID: 1},
|
||||
want: false,
|
||||
},
|
||||
// does not match build number requirement that
|
||||
// must be older than current build
|
||||
{
|
||||
build: &core.Build{RepoID: 1, Number: 2},
|
||||
repo: &core.Repository{ID: 1, Build: &core.Build{Number: 3}},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
build: &core.Build{RepoID: 1, Number: 2},
|
||||
repo: &core.Repository{ID: 1, Build: &core.Build{Number: 2}},
|
||||
want: false,
|
||||
},
|
||||
// does not match required status
|
||||
{
|
||||
build: &core.Build{RepoID: 1, Number: 2},
|
||||
repo: &core.Repository{ID: 1, Build: &core.Build{Number: 1, Status: core.StatusPassing}},
|
||||
want: false,
|
||||
},
|
||||
// does not match (one of) required event types
|
||||
{
|
||||
build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPullRequest},
|
||||
repo: &core.Repository{ID: 1, Build: &core.Build{
|
||||
Number: 1,
|
||||
Status: core.StatusPending,
|
||||
Event: core.EventPush,
|
||||
}},
|
||||
want: false,
|
||||
},
|
||||
// does not match ref
|
||||
{
|
||||
build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPush, Ref: "refs/heads/master"},
|
||||
repo: &core.Repository{ID: 1, Build: &core.Build{
|
||||
Number: 1,
|
||||
Status: core.StatusPending,
|
||||
Event: core.EventPush,
|
||||
Ref: "refs/heads/develop",
|
||||
}},
|
||||
want: false,
|
||||
},
|
||||
|
||||
//
|
||||
// successful matches
|
||||
//
|
||||
{
|
||||
build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPush, Ref: "refs/heads/master"},
|
||||
repo: &core.Repository{ID: 1, Build: &core.Build{
|
||||
Number: 1,
|
||||
Status: core.StatusPending,
|
||||
Event: core.EventPush,
|
||||
Ref: "refs/heads/master",
|
||||
}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
build: &core.Build{RepoID: 1, Number: 2, Event: core.EventPullRequest, Ref: "refs/heads/master"},
|
||||
repo: &core.Repository{ID: 1, Build: &core.Build{
|
||||
Number: 1,
|
||||
Status: core.StatusPending,
|
||||
Event: core.EventPullRequest,
|
||||
Ref: "refs/heads/master",
|
||||
}},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
if got, want := match(test.build, test.repo), test.want; got != want {
|
||||
t.Errorf("Want match %v at index %d, got %v", want, i, got)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -183,6 +183,8 @@ const stmtInsertBase = `
|
|||
,repo_protected
|
||||
,repo_no_forks
|
||||
,repo_no_pulls
|
||||
,repo_cancel_pulls
|
||||
,repo_cancel_push
|
||||
,repo_synced
|
||||
,repo_created
|
||||
,repo_updated
|
||||
|
@ -210,6 +212,8 @@ const stmtInsertBase = `
|
|||
,:repo_protected
|
||||
,:repo_no_forks
|
||||
,:repo_no_pulls
|
||||
,:repo_cancel_pulls
|
||||
,:repo_cancel_push
|
||||
,:repo_synced
|
||||
,:repo_created
|
||||
,:repo_updated
|
||||
|
|
|
@ -241,6 +241,8 @@ const stmtInsertBase = `
|
|||
,repo_protected
|
||||
,repo_no_forks
|
||||
,repo_no_pulls
|
||||
,repo_cancel_pulls
|
||||
,repo_cancel_push
|
||||
,repo_synced
|
||||
,repo_created
|
||||
,repo_updated
|
||||
|
@ -268,6 +270,8 @@ const stmtInsertBase = `
|
|||
,:repo_protected
|
||||
,:repo_no_forks
|
||||
,:repo_no_pulls
|
||||
,:repo_cancel_pulls
|
||||
,:repo_cancel_push
|
||||
,:repo_synced
|
||||
,:repo_created
|
||||
,:repo_updated
|
||||
|
|
|
@ -266,6 +266,8 @@ SELECT
|
|||
,repo_protected
|
||||
,repo_no_forks
|
||||
,repo_no_pulls
|
||||
,repo_cancel_pulls
|
||||
,repo_cancel_push
|
||||
,repo_synced
|
||||
,repo_created
|
||||
,repo_updated
|
||||
|
@ -353,6 +355,8 @@ INSERT INTO repos (
|
|||
,repo_protected
|
||||
,repo_no_forks
|
||||
,repo_no_pulls
|
||||
,repo_cancel_pulls
|
||||
,repo_cancel_push
|
||||
,repo_synced
|
||||
,repo_created
|
||||
,repo_updated
|
||||
|
@ -380,6 +384,8 @@ INSERT INTO repos (
|
|||
,:repo_protected
|
||||
,:repo_no_forks
|
||||
,:repo_no_pulls
|
||||
,:repo_cancel_pulls
|
||||
,:repo_cancel_push
|
||||
,:repo_synced
|
||||
,:repo_created
|
||||
,:repo_updated
|
||||
|
@ -425,6 +431,8 @@ UPDATE repos SET
|
|||
,repo_protected = :repo_protected
|
||||
,repo_no_forks = :repo_no_forks
|
||||
,repo_no_pulls = :repo_no_pulls
|
||||
,repo_cancel_pulls = :repo_cancel_pulls
|
||||
,repo_cancel_push = :repo_cancel_push
|
||||
,repo_timeout = :repo_timeout
|
||||
,repo_counter = :repo_counter
|
||||
,repo_synced = :repo_synced
|
||||
|
|
|
@ -25,33 +25,35 @@ import (
|
|||
// of named query parameters.
|
||||
func ToParams(v *core.Repository) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"repo_id": v.ID,
|
||||
"repo_uid": v.UID,
|
||||
"repo_user_id": v.UserID,
|
||||
"repo_namespace": v.Namespace,
|
||||
"repo_name": v.Name,
|
||||
"repo_slug": v.Slug,
|
||||
"repo_scm": v.SCM,
|
||||
"repo_clone_url": v.HTTPURL,
|
||||
"repo_ssh_url": v.SSHURL,
|
||||
"repo_html_url": v.Link,
|
||||
"repo_branch": v.Branch,
|
||||
"repo_private": v.Private,
|
||||
"repo_visibility": v.Visibility,
|
||||
"repo_active": v.Active,
|
||||
"repo_config": v.Config,
|
||||
"repo_trusted": v.Trusted,
|
||||
"repo_protected": v.Protected,
|
||||
"repo_no_forks": v.IgnoreForks,
|
||||
"repo_no_pulls": v.IgnorePulls,
|
||||
"repo_timeout": v.Timeout,
|
||||
"repo_counter": v.Counter,
|
||||
"repo_synced": v.Synced,
|
||||
"repo_created": v.Created,
|
||||
"repo_updated": v.Updated,
|
||||
"repo_version": v.Version,
|
||||
"repo_signer": v.Signer,
|
||||
"repo_secret": v.Secret,
|
||||
"repo_id": v.ID,
|
||||
"repo_uid": v.UID,
|
||||
"repo_user_id": v.UserID,
|
||||
"repo_namespace": v.Namespace,
|
||||
"repo_name": v.Name,
|
||||
"repo_slug": v.Slug,
|
||||
"repo_scm": v.SCM,
|
||||
"repo_clone_url": v.HTTPURL,
|
||||
"repo_ssh_url": v.SSHURL,
|
||||
"repo_html_url": v.Link,
|
||||
"repo_branch": v.Branch,
|
||||
"repo_private": v.Private,
|
||||
"repo_visibility": v.Visibility,
|
||||
"repo_active": v.Active,
|
||||
"repo_config": v.Config,
|
||||
"repo_trusted": v.Trusted,
|
||||
"repo_protected": v.Protected,
|
||||
"repo_no_forks": v.IgnoreForks,
|
||||
"repo_no_pulls": v.IgnorePulls,
|
||||
"repo_cancel_pulls": v.CancelPulls,
|
||||
"repo_cancel_push": v.CancelPush,
|
||||
"repo_timeout": v.Timeout,
|
||||
"repo_counter": v.Counter,
|
||||
"repo_synced": v.Synced,
|
||||
"repo_created": v.Created,
|
||||
"repo_updated": v.Updated,
|
||||
"repo_version": v.Version,
|
||||
"repo_signer": v.Signer,
|
||||
"repo_secret": v.Secret,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,6 +82,8 @@ func scanRow(scanner db.Scanner, dest *core.Repository) error {
|
|||
&dest.Protected,
|
||||
&dest.IgnoreForks,
|
||||
&dest.IgnorePulls,
|
||||
&dest.CancelPulls,
|
||||
&dest.CancelPush,
|
||||
&dest.Synced,
|
||||
&dest.Created,
|
||||
&dest.Updated,
|
||||
|
@ -132,6 +136,8 @@ func scanRowBuild(scanner db.Scanner, dest *core.Repository) error {
|
|||
&dest.Protected,
|
||||
&dest.IgnoreForks,
|
||||
&dest.IgnorePulls,
|
||||
&dest.CancelPulls,
|
||||
&dest.CancelPush,
|
||||
&dest.Synced,
|
||||
&dest.Created,
|
||||
&dest.Updated,
|
||||
|
|
|
@ -24,6 +24,14 @@ var migrations = []struct {
|
|||
name: "alter-table-repos-add-column-no-pulls",
|
||||
stmt: alterTableReposAddColumnNoPulls,
|
||||
},
|
||||
{
|
||||
name: "alter-table-repos-add-column-cancel-pulls",
|
||||
stmt: alterTableReposAddColumnCancelPulls,
|
||||
},
|
||||
{
|
||||
name: "alter-table-repos-add-column-cancel-push",
|
||||
stmt: alterTableReposAddColumnCancelPush,
|
||||
},
|
||||
{
|
||||
name: "create-table-perms",
|
||||
stmt: createTablePerms,
|
||||
|
@ -274,6 +282,14 @@ var alterTableReposAddColumnNoPulls = `
|
|||
ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT false;
|
||||
`
|
||||
|
||||
var alterTableReposAddColumnCancelPulls = `
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT false;
|
||||
`
|
||||
|
||||
var alterTableReposAddColumnCancelPush = `
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT false;
|
||||
`
|
||||
|
||||
//
|
||||
// 003_create_table_perms.sql
|
||||
//
|
||||
|
|
|
@ -37,3 +37,11 @@ ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT false;
|
|||
-- name: alter-table-repos-add-column-no-pulls
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- name: alter-table-repos-add-column-cancel-pulls
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- name: alter-table-repos-add-column-cancel-push
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT false;
|
||||
|
|
|
@ -24,6 +24,14 @@ var migrations = []struct {
|
|||
name: "alter-table-repos-add-column-no-pulls",
|
||||
stmt: alterTableReposAddColumnNoPulls,
|
||||
},
|
||||
{
|
||||
name: "alter-table-repos-add-column-cancel-pulls",
|
||||
stmt: alterTableReposAddColumnCancelPulls,
|
||||
},
|
||||
{
|
||||
name: "alter-table-repos-add-column-cancel-push",
|
||||
stmt: alterTableReposAddColumnCancelPush,
|
||||
},
|
||||
{
|
||||
name: "create-table-perms",
|
||||
stmt: createTablePerms,
|
||||
|
@ -270,6 +278,14 @@ var alterTableReposAddColumnNoPulls = `
|
|||
ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT false;
|
||||
`
|
||||
|
||||
var alterTableReposAddColumnCancelPulls = `
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT false;
|
||||
`
|
||||
|
||||
var alterTableReposAddColumnCancelPush = `
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT false;
|
||||
`
|
||||
|
||||
//
|
||||
// 003_create_table_perms.sql
|
||||
//
|
||||
|
|
|
@ -37,3 +37,11 @@ ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT false;
|
|||
-- name: alter-table-repos-add-column-no-pulls
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- name: alter-table-repos-add-column-cancel-pulls
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- name: alter-table-repos-add-column-cancel-push
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT false;
|
||||
|
|
|
@ -24,6 +24,14 @@ var migrations = []struct {
|
|||
name: "alter-table-repos-add-column-no-pulls",
|
||||
stmt: alterTableReposAddColumnNoPulls,
|
||||
},
|
||||
{
|
||||
name: "alter-table-repos-add-column-cancel-pulls",
|
||||
stmt: alterTableReposAddColumnCancelPulls,
|
||||
},
|
||||
{
|
||||
name: "alter-table-repos-add-column-cancel-push",
|
||||
stmt: alterTableReposAddColumnCancelPush,
|
||||
},
|
||||
{
|
||||
name: "create-table-perms",
|
||||
stmt: createTablePerms,
|
||||
|
@ -270,6 +278,14 @@ var alterTableReposAddColumnNoPulls = `
|
|||
ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT 0;
|
||||
`
|
||||
|
||||
var alterTableReposAddColumnCancelPulls = `
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT 0;
|
||||
`
|
||||
|
||||
var alterTableReposAddColumnCancelPush = `
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT 0;
|
||||
`
|
||||
|
||||
//
|
||||
// 003_create_table_perms.sql
|
||||
//
|
||||
|
|
|
@ -37,3 +37,11 @@ ALTER TABLE repos ADD COLUMN repo_no_forks BOOLEAN NOT NULL DEFAULT 0;
|
|||
-- name: alter-table-repos-add-column-no-pulls
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_no_pulls BOOLEAN NOT NULL DEFAULT 0;
|
||||
|
||||
-- name: alter-table-repos-add-column-cancel-pulls
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_pulls BOOLEAN NOT NULL DEFAULT 0;
|
||||
|
||||
-- name: alter-table-repos-add-column-cancel-push
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_cancel_push BOOLEAN NOT NULL DEFAULT 0;
|
||||
|
|
|
@ -59,6 +59,8 @@ func skipMessage(hook *core.Hook) bool {
|
|||
return false
|
||||
case hook.Event == core.EventCron:
|
||||
return false
|
||||
case hook.Event == core.EventCustom:
|
||||
return false
|
||||
case skipMessageEval(hook.Message):
|
||||
return true
|
||||
case skipMessageEval(hook.Title):
|
||||
|
|
|
@ -190,6 +190,16 @@ func Test_skipMessage(t *testing.T) {
|
|||
title: "update readme [CI SKIP]",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
event: "custom",
|
||||
title: "update readme [CI SKIP]",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
event: "custom",
|
||||
title: "update readme [CI SKIP]",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
hook := &core.Hook{
|
||||
|
|
Loading…
Reference in a new issue