Commit ca09a858 authored by Nick Thomas's avatar Nick Thomas

Add namespace license checks for squash before merge

parent ad8e7eb1
...@@ -18,7 +18,8 @@ export default class MergeRequestStore extends CEMergeRequestStore { ...@@ -18,7 +18,8 @@ export default class MergeRequestStore extends CEMergeRequestStore {
initSquashBeforeMerge(data) { initSquashBeforeMerge(data) {
this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath
|| data.squash_before_merge_help_path; || data.squash_before_merge_help_path;
this.enableSquashBeforeMerge = true; this.enableSquashBeforeMerge = this.enableSquashBeforeMerge
|| data.enable_squash_before_merge;
} }
initRebase(data) { initRebase(data) {
......
...@@ -69,7 +69,10 @@ module EE ...@@ -69,7 +69,10 @@ module EE
end end
def merge_params_attributes def merge_params_attributes
super + [:squash] attrs = super
attrs << :squash if project.feature_available?(:merge_request_squash)
attrs
end end
def merge_request_params def merge_request_params
...@@ -77,12 +80,14 @@ module EE ...@@ -77,12 +80,14 @@ module EE
end end
def merge_request_params_attributes def merge_request_params_attributes
super + %i[ attrs = super.push(
approvals_before_merge :approvals_before_merge,
approver_group_ids :approver_group_ids,
approver_ids :approver_ids
squash )
] attrs << :squash if project.feature_available?(:merge_request_squash)
attrs
end end
# If the number of approvals is not greater than the project default, set to # If the number of approvals is not greater than the project default, set to
......
...@@ -45,5 +45,10 @@ module EE ...@@ -45,5 +45,10 @@ module EE
true true
end end
end end
def squash
super && project.feature_available?(:merge_request_squash)
end
alias_method :squash?, :squash
end end
end end
...@@ -11,6 +11,7 @@ class License < ActiveRecord::Base ...@@ -11,6 +11,7 @@ class License < ActiveRecord::Base
RELATED_ISSUES_FEATURE = 'RelatedIssues'.freeze RELATED_ISSUES_FEATURE = 'RelatedIssues'.freeze
EXPORT_ISSUES_FEATURE = 'GitLab_ExportIssues'.freeze EXPORT_ISSUES_FEATURE = 'GitLab_ExportIssues'.freeze
MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze MERGE_REQUEST_REBASE_FEATURE = 'GitLab_MergeRequestRebase'.freeze
MERGE_REQUEST_SQUASH_FEATURE = 'GitLab_MergeRequestSquash'.freeze
FEATURE_CODES = { FEATURE_CODES = {
geo: GEO_FEATURE, geo: GEO_FEATURE,
...@@ -23,7 +24,8 @@ class License < ActiveRecord::Base ...@@ -23,7 +24,8 @@ class License < ActiveRecord::Base
deploy_board: DEPLOY_BOARD_FEATURE, deploy_board: DEPLOY_BOARD_FEATURE,
file_lock: FILE_LOCK_FEATURE, file_lock: FILE_LOCK_FEATURE,
export_issues: EXPORT_ISSUES_FEATURE, export_issues: EXPORT_ISSUES_FEATURE,
merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE merge_request_rebase: MERGE_REQUEST_REBASE_FEATURE,
merge_request_squash: MERGE_REQUEST_SQUASH_FEATURE
}.freeze }.freeze
STARTER_PLAN = 'starter'.freeze STARTER_PLAN = 'starter'.freeze
...@@ -35,7 +37,8 @@ class License < ActiveRecord::Base ...@@ -35,7 +37,8 @@ class License < ActiveRecord::Base
{ ELASTIC_SEARCH_FEATURE => 1 }, { ELASTIC_SEARCH_FEATURE => 1 },
{ RELATED_ISSUES_FEATURE => 1 }, { RELATED_ISSUES_FEATURE => 1 },
{ EXPORT_ISSUES_FEATURE => 1 }, { EXPORT_ISSUES_FEATURE => 1 },
{ MERGE_REQUEST_REBASE_FEATURE => 1 } { MERGE_REQUEST_REBASE_FEATURE => 1 },
{ MERGE_REQUEST_SQUASH_FEATURE => 1 }
].freeze ].freeze
EEP_FEATURES = [ EEP_FEATURES = [
...@@ -69,7 +72,8 @@ class License < ActiveRecord::Base ...@@ -69,7 +72,8 @@ class License < ActiveRecord::Base
{ SERVICE_DESK_FEATURE => 1 }, { SERVICE_DESK_FEATURE => 1 },
{ OBJECT_STORAGE_FEATURE => 1 }, { OBJECT_STORAGE_FEATURE => 1 },
{ EXPORT_ISSUES_FEATURE => 1 }, { EXPORT_ISSUES_FEATURE => 1 },
{ MERGE_REQUEST_REBASE_FEATURE => 1 } { MERGE_REQUEST_REBASE_FEATURE => 1 },
{ MERGE_REQUEST_SQUASH_FEATURE => 1 }
].freeze ].freeze
FEATURES_BY_PLAN = { FEATURES_BY_PLAN = {
......
...@@ -17,6 +17,10 @@ module MergeRequests ...@@ -17,6 +17,10 @@ module MergeRequests
return success(squash_sha: merge_request.diff_head_sha) return success(squash_sha: merge_request.diff_head_sha)
end end
unless project.feature_available?(:merge_request_squash)
return error('License does not allow squashing')
end
if merge_request.squash_in_progress? if merge_request.squash_in_progress?
return error('Squash task canceled: another squash is already in progress.') return error('Squash task canceled: another squash is already in progress.')
end end
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
// Object.assign would be useful here, but it blows up Phantom.js in tests // Object.assign would be useful here, but it blows up Phantom.js in tests
window.gl.mrWidgetData.is_geo_secondary_node = '#{Gitlab::Geo.secondary?}' === 'true'; window.gl.mrWidgetData.is_geo_secondary_node = '#{Gitlab::Geo.secondary?}' === 'true';
window.gl.mrWidgetData.geo_secondary_help_path = '#{help_page_path("/gitlab-geo/configuration.md")}'; window.gl.mrWidgetData.geo_secondary_help_path = '#{help_page_path("/gitlab-geo/configuration.md")}';
window.gl.mrWidgetData.enable_squash_before_merge = '#{@merge_request.project.feature_available?(:merge_request_squash)}' === 'true';
window.gl.mrWidgetData.squash_before_merge_help_path = '#{help_page_path("user/project/merge_requests/squash_and_merge")}'; window.gl.mrWidgetData.squash_before_merge_help_path = '#{help_page_path("user/project/merge_requests/squash_and_merge")}';
#js-vue-mr-widget.mr-widget #js-vue-mr-widget.mr-widget
......
- issuable = local_assigns.fetch(:issuable)
- return unless issuable.project.feature_available?(:merge_request_squash)
.form-group .form-group
.col-sm-10.col-sm-offset-2 .col-sm-10.col-sm-offset-2
.checkbox .checkbox
......
---
title: Add namespace license checks for squash before merge
merge_request: 2249
author:
...@@ -363,7 +363,8 @@ module API ...@@ -363,7 +363,8 @@ module API
expose :approvals_before_merge expose :approvals_before_merge
expose :should_remove_source_branch?, as: :should_remove_source_branch expose :should_remove_source_branch?, as: :should_remove_source_branch
expose :force_remove_source_branch?, as: :force_remove_source_branch expose :force_remove_source_branch?, as: :force_remove_source_branch
expose :squash
expose :squash, if: -> (mr, _) { mr.project.feature_available?(:merge_request_squash) }
expose :web_url do |merge_request, options| expose :web_url do |merge_request, options|
Gitlab::UrlBuilder.build(merge_request) Gitlab::UrlBuilder.build(merge_request)
......
...@@ -232,7 +232,7 @@ module API ...@@ -232,7 +232,7 @@ module API
render_api_error!("SHA does not match HEAD of source branch: #{merge_request.diff_head_sha}", 409) render_api_error!("SHA does not match HEAD of source branch: #{merge_request.diff_head_sha}", 409)
end end
if params[:squash] if params[:squash] && merge_request.project.feature_available?(:merge_request_squash)
merge_request.update(squash: params[:squash]) merge_request.update(squash: params[:squash])
end end
......
...@@ -160,7 +160,8 @@ module API ...@@ -160,7 +160,8 @@ module API
expose :approvals_before_merge expose :approvals_before_merge
expose :should_remove_source_branch?, as: :should_remove_source_branch expose :should_remove_source_branch?, as: :should_remove_source_branch
expose :force_remove_source_branch?, as: :force_remove_source_branch expose :force_remove_source_branch?, as: :force_remove_source_branch
expose :squash
expose :squash, if: ->(mr, _) { mr.project.feature_available?(:merge_request_squash) }
expose :web_url do |merge_request, options| expose :web_url do |merge_request, options|
Gitlab::UrlBuilder.build(merge_request) Gitlab::UrlBuilder.build(merge_request)
......
...@@ -209,7 +209,7 @@ module API ...@@ -209,7 +209,7 @@ module API
render_api_error!("SHA does not match HEAD of source branch: #{merge_request.diff_head_sha}", 409) render_api_error!("SHA does not match HEAD of source branch: #{merge_request.diff_head_sha}", 409)
end end
if params[:squash] if params[:squash] && merge_request.project.feature_available?(:merge_request_squash)
merge_request.update(squash: params[:squash]) merge_request.update(squash: params[:squash])
end end
......
...@@ -121,4 +121,24 @@ feature 'Squashing merge requests', js: true, feature: true do ...@@ -121,4 +121,24 @@ feature 'Squashing merge requests', js: true, feature: true do
include_examples 'no squash' include_examples 'no squash'
end end
end end
context 'squash is unlicensed' do
let(:merge_request) { create(:merge_request, source_project: project, target_project: project, source_branch: source_branch, target_branch: 'master', squash: true) }
before do
stub_licensed_features(merge_request_squash: false)
end
it 'does not show squash option when creating MR' do
visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_branch: 'master', source_branch: source_branch })
expect(page).to have_no_field('merge_request[squash]')
end
it 'does not show squash option when viewing MR' do
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
expect(page).to have_no_field('squash')
end
end
end end
...@@ -90,4 +90,41 @@ describe MergeRequest, models: true do ...@@ -90,4 +90,41 @@ describe MergeRequest, models: true do
expect(subject.squash_in_progress?).to be_falsey expect(subject.squash_in_progress?).to be_falsey
end end
end end
describe '#squash?' do
let(:merge_request) { build(:merge_request, squash: squash) }
subject { merge_request.squash? }
context 'unlicensed' do
before do
stub_licensed_features(merge_request_squash: false)
end
context 'disabled in database' do
let(:squash) { false }
it { is_expected.to be_falsy }
end
context 'enabled in database' do
let(:squash) { true }
it { is_expected.to be_falsy }
end
end
context 'licensed' do
context 'disabled in database' do
let(:squash) { false }
it { is_expected.to be_falsy }
end
context 'licensed' do
let(:squash) { true }
it { is_expected.to be_truthy }
end
end
end
end end
...@@ -96,6 +96,16 @@ describe MergeRequests::SquashService do ...@@ -96,6 +96,16 @@ describe MergeRequests::SquashService do
include_examples 'the squash succeeds' include_examples 'the squash succeeds'
end end
context 'squashing is unlicensed' do
before do
stub_licensed_features(merge_request_squash: false)
end
subject { service.execute(merge_request_with_only_new_files) }
it { is_expected.to match(status: :error, message: a_string_including('License')) }
end
stages = { stages = {
'add worktree for squash' => 'worktree', 'add worktree for squash' => 'worktree',
'configure sparse checkout' => 'config', 'configure sparse checkout' => 'config',
......
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