Commit c81ef304 authored by Rydkin Maxim's avatar Rydkin Maxim

add auto-cancel for pending pipelines on branch, if they are not HEAD

fix changelog MR reference

add non-HEAD builds finder and add `created` pipelines to scope

add spec for auto-cancel non-HEAD pipelines and refactor create_pipeline_service_spec

more refactoring for spec

adds option for auto-cancel into CI/CD settings

fix spec to new configuration

fix rubocop

fix schema.rb

fix schema.rb

replace Gitlab 9.0 with 9.1 in doc

change wording on pipeline settings

added auto_canceled_by field as identifier of autocancel subject

remove unnecessary index

replace service with retry_lock

replace auto_cancel_pending_pipelines boolean setting with integer (and enum in model)

fix schema.rb

fix schema.rb

remove projekt attribute and clean up spec

clean up spec withcouple of shared examples

added spec for "It does not cancel current pipeline" scenario

add some specs to auto-cancel

add spec for another branch pipelines
parent 0a4b853f
...@@ -23,7 +23,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController ...@@ -23,7 +23,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
def update_params def update_params
params.require(:project).permit( params.require(:project).permit(
:runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, :runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds :public_builds, :auto_cancel_pending_pipelines
) )
end end
end end
...@@ -127,6 +127,14 @@ module Ci ...@@ -127,6 +127,14 @@ module Ci
where.not(duration: nil).sum(:duration) where.not(duration: nil).sum(:duration)
end end
def auto_cancelable_pipelines
project.pipelines
.where(ref: ref)
.where.not(id: id)
.where.not(sha: project.repository.sha_from_ref(ref))
.created_or_pending
end
def stage(name) def stage(name)
stage = Ci::Stage.new(self, name: name) stage = Ci::Stage.new(self, name: name)
stage unless stage.statuses_count.zero? stage unless stage.statuses_count.zero?
......
...@@ -76,6 +76,7 @@ module HasStatus ...@@ -76,6 +76,7 @@ module HasStatus
scope :canceled, -> { where(status: 'canceled') } scope :canceled, -> { where(status: 'canceled') }
scope :skipped, -> { where(status: 'skipped') } scope :skipped, -> { where(status: 'skipped') }
scope :manual, -> { where(status: 'manual') } scope :manual, -> { where(status: 'manual') }
scope :created_or_pending, -> { where(status: [:created, :pending]) }
scope :running_or_pending, -> { where(status: [:running, :pending]) } scope :running_or_pending, -> { where(status: [:running, :pending]) }
scope :finished, -> { where(status: [:success, :failed, :canceled]) } scope :finished, -> { where(status: [:success, :failed, :canceled]) }
scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) } scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) }
......
...@@ -256,6 +256,8 @@ class Project < ActiveRecord::Base ...@@ -256,6 +256,8 @@ class Project < ActiveRecord::Base
scope :with_builds_enabled, -> { with_feature_enabled(:builds) } scope :with_builds_enabled, -> { with_feature_enabled(:builds) }
scope :with_issues_enabled, -> { with_feature_enabled(:issues) } scope :with_issues_enabled, -> { with_feature_enabled(:issues) }
enum auto_cancel_pending_pipelines: { enabled: 1, disabled: 0 }
# project features may be "disabled", "internal" or "enabled". If "internal", # project features may be "disabled", "internal" or "enabled". If "internal",
# they are only available to team members. This scope returns projects where # they are only available to team members. This scope returns projects where
# the feature is either enabled, or internal with permission for the user. # the feature is either enabled, or internal with permission for the user.
......
...@@ -53,6 +53,8 @@ module Ci ...@@ -53,6 +53,8 @@ module Ci
.execute(pipeline) .execute(pipeline)
end end
cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
pipeline.tap(&:process!) pipeline.tap(&:process!)
end end
...@@ -63,6 +65,16 @@ module Ci ...@@ -63,6 +65,16 @@ module Ci
pipeline.git_commit_message =~ /\[(ci[ _-]skip|skip[ _-]ci)\]/i pipeline.git_commit_message =~ /\[(ci[ _-]skip|skip[ _-]ci)\]/i
end end
def cancel_pending_pipelines
Gitlab::OptimisticLocking.retry_lock(
pipeline.auto_cancelable_pipelines) do |cancelables|
cancelables.find_each do |cancelable|
cancelable.cancel_running
cancelable.update_attributes(auto_canceled_by: pipeline.id)
end
end
end
def commit def commit
@commit ||= project.commit(origin_sha || origin_ref) @commit ||= project.commit(origin_sha || origin_ref)
end end
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
Git strategy for pipelines Git strategy for pipelines
%p %p
Choose between <code>clone</code> or <code>fetch</code> to get the recent application code Choose between <code>clone</code> or <code>fetch</code> to get the recent application code
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'git-strategy') = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'git-strategy'), target: '_blank'
.radio .radio
= f.label :build_allow_git_fetch_false do = f.label :build_allow_git_fetch_false do
= f.radio_button :build_allow_git_fetch, 'false' = f.radio_button :build_allow_git_fetch, 'false'
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
= f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
%p.help-block %p.help-block
Per job in minutes. If a job passes this threshold, it will be marked as failed. Per job in minutes. If a job passes this threshold, it will be marked as failed.
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'timeout') = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'timeout'), target: '_blank'
%hr %hr
.form-group .form-group
...@@ -53,7 +53,16 @@ ...@@ -53,7 +53,16 @@
%strong Public pipelines %strong Public pipelines
.help-block .help-block
Allow everyone to access pipelines for public and internal projects Allow everyone to access pipelines for public and internal projects
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'visibility-of-pipelines') = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'visibility-of-pipelines'), target: '_blank'
%hr
.form-group
.checkbox
= f.label :auto_cancel_pending_pipelines do
= f.check_box :auto_cancel_pending_pipelines, {}, 'enabled', 'disabled'
%strong Auto-cancel redundant, pending pipelines
.help-block
New pipelines will cancel older, pending pipelines on the same branch
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'auto-cancel-pending-pipelines'), target: '_blank'
%hr %hr
.form-group .form-group
...@@ -65,7 +74,7 @@ ...@@ -65,7 +74,7 @@
%p.help-block %p.help-block
A regular expression that will be used to find the test coverage A regular expression that will be used to find the test coverage
output in the job trace. Leave blank to disable output in the job trace. Leave blank to disable
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'test-coverage-parsing') = link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'test-coverage-parsing'), target: '_blank'
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
%p Below are examples of regex for existing tools: %p Below are examples of regex for existing tools:
%ul %ul
......
---
title: Cancel pending pipelines if commits not HEAD
merge_request: 9362
author: Rydkin Maxim
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddAutoCancelPendingPipelinesToProject < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = 'Creating column with default value'
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
disable_ddl_transaction!
def up
add_column_with_default(:projects, :auto_cancel_pending_pipelines, :integer, default: 0)
end
def down
remove_column(:projects, :auto_cancel_pending_pipelines)
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddAutoCanceledByToPipeline < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
add_column :ci_commits, :auto_canceled_by, :integer
end
end
...@@ -251,6 +251,7 @@ ActiveRecord::Schema.define(version: 20170402231018) do ...@@ -251,6 +251,7 @@ ActiveRecord::Schema.define(version: 20170402231018) do
t.integer "duration" t.integer "duration"
t.integer "user_id" t.integer "user_id"
t.integer "lock_version" t.integer "lock_version"
t.integer "auto_canceled_by"
end end
add_index "ci_pipelines", ["project_id", "ref", "status"], name: "index_ci_pipelines_on_project_id_and_ref_and_status", using: :btree add_index "ci_pipelines", ["project_id", "ref", "status"], name: "index_ci_pipelines_on_project_id_and_ref_and_status", using: :btree
...@@ -687,8 +688,8 @@ ActiveRecord::Schema.define(version: 20170402231018) do ...@@ -687,8 +688,8 @@ ActiveRecord::Schema.define(version: 20170402231018) do
t.string "avatar" t.string "avatar"
t.boolean "share_with_group_lock", default: false t.boolean "share_with_group_lock", default: false
t.integer "visibility_level", default: 20, null: false t.integer "visibility_level", default: 20, null: false
t.boolean "request_access_enabled", default: false, null: false
t.datetime "deleted_at" t.datetime "deleted_at"
t.boolean "request_access_enabled", default: false, null: false
t.text "description_html" t.text "description_html"
t.boolean "lfs_enabled" t.boolean "lfs_enabled"
t.integer "parent_id" t.integer "parent_id"
...@@ -920,6 +921,7 @@ ActiveRecord::Schema.define(version: 20170402231018) do ...@@ -920,6 +921,7 @@ ActiveRecord::Schema.define(version: 20170402231018) do
t.text "description_html" t.text "description_html"
t.boolean "only_allow_merge_if_all_discussions_are_resolved" t.boolean "only_allow_merge_if_all_discussions_are_resolved"
t.boolean "printing_merge_request_link_enabled", default: true, null: false t.boolean "printing_merge_request_link_enabled", default: true, null: false
t.integer "auto_cancel_pending_pipelines", default: 0, null: false
end end
add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
...@@ -1239,9 +1241,9 @@ ActiveRecord::Schema.define(version: 20170402231018) do ...@@ -1239,9 +1241,9 @@ ActiveRecord::Schema.define(version: 20170402231018) do
t.boolean "hide_project_limit", default: false t.boolean "hide_project_limit", default: false
t.string "unlock_token" t.string "unlock_token"
t.datetime "otp_grace_period_started_at" t.datetime "otp_grace_period_started_at"
t.string "incoming_email_token"
t.boolean "ldap_email", default: false, null: false t.boolean "ldap_email", default: false, null: false
t.boolean "external", default: false t.boolean "external", default: false
t.string "incoming_email_token"
t.string "organization" t.string "organization"
t.boolean "authorized_projects_populated" t.boolean "authorized_projects_populated"
t.boolean "ghost" t.boolean "ghost"
......
...@@ -60,6 +60,14 @@ anyone and those logged in respectively. If you wish to hide it so that only ...@@ -60,6 +60,14 @@ anyone and those logged in respectively. If you wish to hide it so that only
the members of the project or group have access to it, uncheck the **Public the members of the project or group have access to it, uncheck the **Public
pipelines** checkbox and save the changes. pipelines** checkbox and save the changes.
## Auto-cancel pending pipelines
> [Introduced][ce-9362] in GitLab 9.1.
If you want to auto-cancel all pending non-HEAD pipelines on branch, when
new pipeline will be created (after your git push or manually from UI),
check **Auto-cancel pending pipelines** checkbox and save the changes.
## Badges ## Badges
In the pipelines settings page you can find pipeline status and test coverage In the pipelines settings page you can find pipeline status and test coverage
...@@ -111,3 +119,4 @@ into your `README.md`: ...@@ -111,3 +119,4 @@ into your `README.md`:
[var]: ../../../ci/yaml/README.md#git-strategy [var]: ../../../ci/yaml/README.md#git-strategy
[coverage report]: #test-coverage-parsing [coverage report]: #test-coverage-parsing
[ce-9362]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9362
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment