option to auto-cancel pending builds

This commit is contained in:
Brad Rydzewski 2019-10-01 23:28:57 -07:00
parent 3fcbf1c78f
commit 0b4e5156ae
10 changed files with 106 additions and 10 deletions

View file

@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- added nsswitch to docker images
- option to auto-cancel pending builds when newer build enqueued, by [@bradrydzewski](https://github.com/bradrydzewski). [#1980](https://github.com/drone/drone/issues/1980).
## [1.5.1] - 2019-09-30
### Added

View file

@ -22,6 +22,7 @@ import (
"github.com/drone/drone/livelog"
"github.com/drone/drone/metric/sink"
"github.com/drone/drone/pubsub"
"github.com/drone/drone/service/canceler"
"github.com/drone/drone/service/commit"
contents "github.com/drone/drone/service/content"
"github.com/drone/drone/service/content/cache"
@ -46,6 +47,7 @@ import (
// wire set for loading the services.
var serviceSet = wire.NewSet(
canceler.New,
commit.New,
cron.New,
livelog.New,

View file

@ -12,6 +12,7 @@ import (
"github.com/drone/drone/livelog"
"github.com/drone/drone/operator/manager"
"github.com/drone/drone/pubsub"
"github.com/drone/drone/service/canceler"
"github.com/drone/drone/service/commit"
"github.com/drone/drone/service/hook/parser"
"github.com/drone/drone/service/license"
@ -47,17 +48,19 @@ func InitializeApplication(config2 config.Config) (application, error) {
commitService := commit.New(client, renewer)
cronStore := cron.New(db)
repositoryStore := provideRepoStore(db)
fileService := provideContentService(client, renewer)
configService := provideConfigPlugin(client, fileService, config2)
convertService := provideConvertPlugin(client, config2)
statusService := provideStatusService(client, renewer, config2)
buildStore := provideBuildStore(db)
stageStore := provideStageStore(db)
scheduler := provideScheduler(stageStore, config2)
validateService := provideValidatePlugin(config2)
statusService := provideStatusService(client, renewer, config2)
stepStore := step.New(db)
system := provideSystem(config2)
webhookSender := provideWebhookPlugin(config2, system)
triggerer := trigger.New(configService, convertService, commitService, statusService, buildStore, scheduler, repositoryStore, userStore, validateService, webhookSender)
coreCanceler := canceler.New(buildStore, repositoryStore, scheduler, stageStore, statusService, stepStore, userStore, webhookSender)
fileService := provideContentService(client, renewer)
configService := provideConfigPlugin(client, fileService, config2)
convertService := provideConvertPlugin(client, config2)
validateService := provideValidatePlugin(config2)
triggerer := trigger.New(coreCanceler, configService, convertService, commitService, statusService, buildStore, scheduler, repositoryStore, userStore, validateService, webhookSender)
cronScheduler := cron2.New(commitService, cronStore, repositoryStore, userStore, triggerer)
coreLicense := provideLicense(client, config2)
datadog := provideDatadog(userStore, repositoryStore, buildStore, system, coreLicense, config2)
@ -71,7 +74,6 @@ func InitializeApplication(config2 config.Config) (application, error) {
}
secretStore := secret.New(db, encrypter)
globalSecretStore := global.New(db, encrypter)
stepStore := step.New(db)
buildManager := manager.New(buildStore, configService, convertService, corePubsub, logStore, logStream, netrcService, repositoryStore, scheduler, secretStore, globalSecretStore, statusService, stageStore, stepStore, system, userStore, webhookSender)
secretService := provideSecretPlugin(config2)
registryService := provideRegistryPlugin(config2)

View file

@ -52,7 +52,7 @@ type (
IgnoreForks bool `json:"ignore_forks"`
IgnorePulls bool `json:"ignore_pull_requests"`
CancelPulls bool `json:"auto_cancel_pull_requests"`
CancelPush bool `json:"auto_cancel_branch"`
CancelPush bool `json:"auto_cancel_pushes"`
Timeout int64 `json:"timeout"`
Counter int64 `json:"counter"`
Synced int64 `json:"synced"`

View file

@ -34,6 +34,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_pushes"`
Timeout *int64 `json:"timeout"`
Counter *int64 `json:"counter"`
}
@ -86,6 +88,12 @@ func HandleUpdate(repos core.RepositoryStore) http.HandlerFunc {
if in.IgnorePulls != nil {
repo.IgnorePulls = *in.IgnorePulls
}
if in.CancelPulls != nil {
repo.CancelPulls = *in.CancelPulls
}
if in.CancelPush != nil {
repo.CancelPush = *in.CancelPush
}
//
// system administrator only

View file

@ -6,4 +6,4 @@
package mock
//go:generate mockgen -package=mock -destination=mock_gen.go github.com/drone/drone/core ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Triggerer,Syncer,LogStream,WebhookSender,LicenseService
//go:generate mockgen -package=mock -destination=mock_gen.go github.com/drone/drone/core Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Triggerer,Syncer,LogStream,WebhookSender,LicenseService

View file

@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/drone/drone/core (interfaces: ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Triggerer,Syncer,LogStream,WebhookSender,LicenseService)
// Source: github.com/drone/drone/core (interfaces: Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Triggerer,Syncer,LogStream,WebhookSender,LicenseService)
// Package mock is a generated GoMock package.
package mock
@ -13,6 +13,57 @@ import (
reflect "reflect"
)
// MockCanceler is a mock of Canceler interface
type MockCanceler struct {
ctrl *gomock.Controller
recorder *MockCancelerMockRecorder
}
// MockCancelerMockRecorder is the mock recorder for MockCanceler
type MockCancelerMockRecorder struct {
mock *MockCanceler
}
// NewMockCanceler creates a new mock instance
func NewMockCanceler(ctrl *gomock.Controller) *MockCanceler {
mock := &MockCanceler{ctrl: ctrl}
mock.recorder = &MockCancelerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockCanceler) EXPECT() *MockCancelerMockRecorder {
return m.recorder
}
// Cancel mocks base method
func (m *MockCanceler) Cancel(arg0 context.Context, arg1 *core.Repository, arg2 *core.Build) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Cancel", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// Cancel indicates an expected call of Cancel
func (mr *MockCancelerMockRecorder) Cancel(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cancel", reflect.TypeOf((*MockCanceler)(nil).Cancel), arg0, arg1, arg2)
}
// CancelPending mocks base method
func (m *MockCanceler) CancelPending(arg0 context.Context, arg1 *core.Repository, arg2 *core.Build) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CancelPending", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// CancelPending indicates an expected call of CancelPending
func (mr *MockCancelerMockRecorder) CancelPending(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelPending", reflect.TypeOf((*MockCanceler)(nil).CancelPending), arg0, arg1, arg2)
}
// MockConvertService is a mock of ConvertService interface
type MockConvertService struct {
ctrl *gomock.Controller

View file

@ -16,6 +16,7 @@ package canceler
import (
"context"
"runtime/debug"
"time"
"github.com/drone/drone/core"
@ -67,6 +68,19 @@ func (s *service) Cancel(ctx context.Context, repo *core.Repository, build *core
// 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 {
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
}
}()
// switch {
// case repo.CancelPulls && build.Event == core.EventPullRequest:
// case repo.CancelPush && build.Event == core.EventPush:
// default:
// return nil
// }
switch build.Event {
// on the push and pull request builds can be automatically
// cancelled by the system.

View file

@ -32,6 +32,7 @@ import (
)
type triggerer struct {
canceler core.Canceler
config core.ConfigService
convert core.ConvertService
commits core.CommitService
@ -46,6 +47,7 @@ type triggerer struct {
// New returns a new build triggerer.
func New(
canceler core.Canceler,
config core.ConfigService,
convert core.ConvertService,
commits core.CommitService,
@ -58,6 +60,7 @@ func New(
hooks core.WebhookSender,
) core.Triggerer {
return &triggerer{
canceler: canceler,
config: config,
convert: convert,
commits: commits,
@ -459,6 +462,12 @@ func (t *triggerer) Trigger(ctx context.Context, repo *core.Repository, base *co
logger = logger.WithError(err)
logger.Warnln("trigger: cannot send webhook")
}
if repo.CancelPush && build.Event == core.EventPush ||
repo.CancelPulls && build.Event == core.EventPullRequest {
go t.canceler.CancelPending(ctx, repo, build)
}
// err = t.hooks.SendEndpoint(ctx, payload, repo.Endpoints.Webhook)
// if err != nil {
// logger.Warn().Err(err).

View file

@ -79,6 +79,7 @@ func TestTrigger(t *testing.T) {
mockWebhooks.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil)
triggerer := New(
nil,
mockConfigService,
mockConvertService,
nil,
@ -115,6 +116,7 @@ func TestTrigger_SkipCI(t *testing.T) {
nil,
nil,
nil,
nil,
)
dummyHookSkip := *dummyHook
dummyHookSkip.Message = "foo [CI SKIP] bar"
@ -140,6 +142,7 @@ func TestTrigger_NoOwner(t *testing.T) {
nil,
nil,
nil,
nil,
mockUsers,
nil,
nil,
@ -164,6 +167,7 @@ func TestTrigger_MissingYaml(t *testing.T) {
mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(nil, io.EOF)
triggerer := New(
nil,
mockConfigService,
nil,
nil,
@ -204,6 +208,7 @@ func TestTrigger_ErrorYaml(t *testing.T) {
mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) // .Do(checkBuild).Return(nil)
triggerer := New(
nil,
mockConfigService,
mockConvertService,
nil,
@ -251,6 +256,7 @@ func TestTrigger_SkipBranch(t *testing.T) {
mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil)
triggerer := New(
nil,
mockConfigService,
mockConvertService,
nil,
@ -288,6 +294,7 @@ func TestTrigger_SkipEvent(t *testing.T) {
mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil)
triggerer := New(
nil,
mockConfigService,
mockConvertService,
nil,
@ -325,6 +332,7 @@ func TestTrigger_SkipAction(t *testing.T) {
mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil)
triggerer := New(
nil,
mockConfigService,
mockConvertService,
nil,
@ -366,6 +374,7 @@ func TestTrigger_ErrorIncrement(t *testing.T) {
mockValidateService.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(nil)
triggerer := New(
nil,
mockConfigService,
mockConvertService,
nil,