Skip to content

Commit ccac05d

Browse files
committed
Fix 404 when upstream has disabled merge requests
1 parent 43fb927 commit ccac05d

File tree

12 files changed

+111
-4
lines changed

12 files changed

+111
-4
lines changed

app/helpers/merge_requests_helper.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module MergeRequestsHelper
22
def new_mr_path_from_push_event(event)
3-
target_project = event.project.forked_from_project || event.project
3+
target_project = event.project.default_merge_request_target
44
new_namespace_project_merge_request_path(
55
event.project.namespace,
66
event.project,
@@ -127,6 +127,10 @@ def format_mr_branch_names(merge_request)
127127
end
128128
end
129129

130+
def target_projects(project)
131+
[project, project.default_merge_request_target].uniq
132+
end
133+
130134
def merge_request_button_visibility(merge_request, closed)
131135
return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork?
132136
end

app/models/merge_request.rb

+7
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class MergeRequest < ActiveRecord::Base
100100
validates :merge_user, presence: true, if: :merge_when_pipeline_succeeds?, unless: :importing?
101101
validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?]
102102
validate :validate_fork, unless: :closed_without_fork?
103+
validate :validate_target_project, on: :create
103104

104105
scope :by_source_or_target_branch, ->(branch_name) do
105106
where("source_branch = :branch OR target_branch = :branch", branch: branch_name)
@@ -330,6 +331,12 @@ def validate_branches
330331
end
331332
end
332333

334+
def validate_target_project
335+
return true if target_project.merge_requests_enabled?
336+
337+
errors.add :base, 'Target project has disabled merge requests'
338+
end
339+
333340
def validate_fork
334341
return true unless target_project && source_project
335342
return true if target_project == source_project

app/models/project.rb

+8
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,14 @@ def parent_changed?
13141314
namespace_id_changed?
13151315
end
13161316

1317+
def default_merge_request_target
1318+
if forked_from_project&.merge_requests_enabled?
1319+
forked_from_project
1320+
else
1321+
self
1322+
end
1323+
end
1324+
13171325
alias_method :name_with_namespace, :full_name
13181326
alias_method :human_name, :full_name
13191327
alias_method :path_with_namespace, :full_path

app/services/merge_requests/build_service.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def find_source_project
2828

2929
def find_target_project
3030
return target_project if target_project.present? && can?(current_user, :read_project, target_project)
31-
project.forked_from_project || project
31+
project.default_merge_request_target
3232
end
3333

3434
def find_target_branch

app/views/projects/merge_requests/_new_compare.html.haml

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
.panel-heading
3939
Target branch
4040
.panel-body.clearfix
41-
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
41+
- projects = target_projects(@project)
4242
.merge-request-select.dropdown
4343
= f.hidden_field :target_project_id
4444
= dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: Disallow merge requests from fork when source project have disabled merge requests
3+
merge_request:
4+
author: mhasbini

lib/api/merge_requests.rb

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ def handle_merge_request_errors!(errors)
2020
error!(errors[:validate_fork], 422)
2121
elsif errors[:validate_branches].any?
2222
conflict!(errors[:validate_branches])
23+
elsif errors[:base].any?
24+
error!(errors[:base], 422)
2325
end
2426

2527
render_api_error!(errors, 400)

lib/api/v3/merge_requests.rb

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ def handle_merge_request_errors!(errors)
2323
error!(errors[:validate_fork], 422)
2424
elsif errors[:validate_branches].any?
2525
conflict!(errors[:validate_branches])
26+
elsif errors[:base].any?
27+
error!(errors[:base], 422)
2628
end
2729

2830
render_api_error!(errors, 400)

spec/helpers/merge_requests_helper_spec.rb

+45-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
it do
6666
@project = project
67-
67+
6868
is_expected.to eq("#1, #2, and #{other_project.namespace.path}/#{other_project.path}#3")
6969
end
7070
end
@@ -149,6 +149,50 @@
149149
end
150150
end
151151

152+
describe '#target_projects' do
153+
let(:project) { create(:empty_project) }
154+
let(:fork_project) { create(:empty_project, forked_from_project: project) }
155+
156+
context 'when target project has enabled merge requests' do
157+
it 'returns the forked_from project' do
158+
expect(target_projects(fork_project)).to contain_exactly(project, fork_project)
159+
end
160+
end
161+
162+
context 'when target project has disabled merge requests' do
163+
it 'returns the forked project' do
164+
project.project_feature.update(merge_requests_access_level: 0)
165+
166+
expect(target_projects(fork_project)).to contain_exactly(fork_project)
167+
end
168+
end
169+
end
170+
171+
describe '#new_mr_path_from_push_event' do
172+
subject(:url_params) { URI.decode_www_form(new_mr_path_from_push_event(event)).to_h }
173+
let(:user) { create(:user) }
174+
let(:project) { create(:empty_project, creator: user) }
175+
let(:fork_project) { create(:project, forked_from_project: project, creator: user) }
176+
let(:event) do
177+
push_data = Gitlab::DataBuilder::Push.build_sample(fork_project, user)
178+
create(:event, :pushed, project: fork_project, target: fork_project, author: user, data: push_data)
179+
end
180+
181+
context 'when target project has enabled merge requests' do
182+
it 'returns link to create merge request on source project' do
183+
expect(url_params['merge_request[target_project_id]'].to_i).to eq(project.id)
184+
end
185+
end
186+
187+
context 'when target project has disabled merge requests' do
188+
it 'returns link to create merge request on forked project' do
189+
project.project_feature.update(merge_requests_access_level: 0)
190+
191+
expect(url_params['merge_request[target_project_id]'].to_i).to eq(fork_project.id)
192+
end
193+
end
194+
end
195+
152196
describe '#mr_issues_mentioned_but_not_closing' do
153197
let(:user_1) { create(:user) }
154198
let(:user_2) { create(:user) }

spec/requests/api/merge_requests_spec.rb

+13
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,19 @@
434434
expect(json_response['title']).to eq('Test merge_request')
435435
end
436436

437+
it 'returns 422 when target project has disabled merge requests' do
438+
project.project_feature.update(merge_requests_access_level: 0)
439+
440+
post api("/projects/#{fork_project.id}/merge_requests", user2),
441+
title: 'Test',
442+
target_branch: 'master',
443+
source_branch: 'markdown',
444+
author: user2,
445+
target_project_id: project.id
446+
447+
expect(response).to have_http_status(422)
448+
end
449+
437450
it "returns 400 when source_branch is missing" do
438451
post api("/projects/#{fork_project.id}/merge_requests", user2),
439452
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id

spec/requests/api/v3/merge_requests_spec.rb

+13
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,19 @@
338338
expect(json_response['title']).to eq('Test merge_request')
339339
end
340340

341+
it "returns 422 when target project has disabled merge requests" do
342+
project.project_feature.update(merge_requests_access_level: 0)
343+
344+
post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
345+
title: 'Test',
346+
target_branch: "master",
347+
source_branch: 'markdown',
348+
author: user2,
349+
target_project_id: project.id
350+
351+
expect(response).to have_http_status(422)
352+
end
353+
341354
it "returns 400 when source_branch is missing" do
342355
post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
343356
title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id

spec/services/merge_requests/build_service_spec.rb

+10
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,16 @@
261261
end
262262
end
263263

264+
context 'upstream project has disabled merge requests' do
265+
let(:upstream_project) { create(:empty_project, :merge_requests_disabled) }
266+
let(:project) { create(:empty_project, forked_from_project: upstream_project) }
267+
let(:commits) { Commit.decorate([commit_1], project) }
268+
269+
it 'sets target project correctly' do
270+
expect(merge_request.target_project).to eq(project)
271+
end
272+
end
273+
264274
context 'target_project is set and accessible by current_user' do
265275
let(:target_project) { create(:project, :public, :repository)}
266276
let(:commits) { Commit.decorate([commit_1], project) }

0 commit comments

Comments
 (0)