Commit 1bdfc561 authored by Kerri Miller's avatar Kerri Miller

Merge branch 'suggestions-use-target-instead-of-source-project' into 'master'

Suggestions: use template from target project instead of source project

See merge request gitlab-org/gitlab!82657
parents 1bb4f2b9 1c64a7a6
...@@ -16,10 +16,14 @@ class Suggestion < ApplicationRecord ...@@ -16,10 +16,14 @@ class Suggestion < ApplicationRecord
note.latest_diff_file note.latest_diff_file
end end
def project def source_project
noteable.source_project noteable.source_project
end end
def target_project
noteable.target_project
end
def branch def branch
noteable.source_branch noteable.source_branch
end end
......
# frozen_string_literal: true # frozen_string_literal: true
class SuggestionPolicy < BasePolicy class SuggestionPolicy < BasePolicy
delegate { @subject.project } delegate { @subject.source_project }
condition(:can_push_to_branch) do condition(:can_push_to_branch) do
Gitlab::UserAccess.new(@user, container: @subject.project).can_push_to_branch?(@subject.branch) Gitlab::UserAccess.new(@user, container: @subject.source_project).can_push_to_branch?(@subject.branch)
end end
rule { can_push_to_branch }.enable :apply_suggestion rule { can_push_to_branch }.enable :apply_suggestion
......
...@@ -54,7 +54,7 @@ module Suggestions ...@@ -54,7 +54,7 @@ module Suggestions
author_email: author&.email author_email: author&.email
} }
::Files::MultiService.new(suggestion_set.project, current_user, params) ::Files::MultiService.new(suggestion_set.source_project, current_user, params)
end end
def commit_message def commit_message
......
...@@ -108,6 +108,8 @@ For example, to customize the commit message to output ...@@ -108,6 +108,8 @@ For example, to customize the commit message to output
**Addresses user_1's review**, set the custom text to **Addresses user_1's review**, set the custom text to
`Addresses %{username}'s review`. `Addresses %{username}'s review`.
For merge requests created from forks, GitLab uses the template defined in target project.
NOTE: NOTE:
Custom commit messages for each applied suggestion is Custom commit messages for each applied suggestion is
introduced by [#25381](https://gitlab.com/gitlab-org/gitlab/-/issues/25381). introduced by [#25381](https://gitlab.com/gitlab-org/gitlab/-/issues/25381).
......
...@@ -13,7 +13,7 @@ module Gitlab ...@@ -13,7 +13,7 @@ module Gitlab
end end
def message def message
project = suggestion_set.project project = suggestion_set.target_project
user_defined_message = @custom_message.presence || project.suggestion_commit_message.presence user_defined_message = @custom_message.presence || project.suggestion_commit_message.presence
message = user_defined_message || DEFAULT_SUGGESTION_COMMIT_MESSAGE message = user_defined_message || DEFAULT_SUGGESTION_COMMIT_MESSAGE
...@@ -37,8 +37,8 @@ module Gitlab ...@@ -37,8 +37,8 @@ module Gitlab
'branch_name' => ->(user, suggestion_set) { suggestion_set.branch }, 'branch_name' => ->(user, suggestion_set) { suggestion_set.branch },
'files_count' => ->(user, suggestion_set) { suggestion_set.file_paths.length }, 'files_count' => ->(user, suggestion_set) { suggestion_set.file_paths.length },
'file_paths' => ->(user, suggestion_set) { format_paths(suggestion_set.file_paths) }, 'file_paths' => ->(user, suggestion_set) { format_paths(suggestion_set.file_paths) },
'project_name' => ->(user, suggestion_set) { suggestion_set.project.name }, 'project_name' => ->(user, suggestion_set) { suggestion_set.target_project.name },
'project_path' => ->(user, suggestion_set) { suggestion_set.project.path }, 'project_path' => ->(user, suggestion_set) { suggestion_set.target_project.path },
'user_full_name' => ->(user, suggestion_set) { user.name }, 'user_full_name' => ->(user, suggestion_set) { user.name },
'username' => ->(user, suggestion_set) { user.username }, 'username' => ->(user, suggestion_set) { user.username },
'suggestions_count' => ->(user, suggestion_set) { suggestion_set.suggestions.size } 'suggestions_count' => ->(user, suggestion_set) { suggestion_set.suggestions.size }
......
...@@ -9,8 +9,12 @@ module Gitlab ...@@ -9,8 +9,12 @@ module Gitlab
@suggestions = suggestions @suggestions = suggestions
end end
def project def source_project
first_suggestion.project first_suggestion.source_project
end
def target_project
first_suggestion.target_project
end end
def branch def branch
......
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Suggestions::CommitMessage do RSpec.describe Gitlab::Suggestions::CommitMessage do
def create_suggestion(file_path, new_line, to_content) include ProjectForksHelper
using RSpec::Parameterized::TableSyntax
def create_suggestion(merge_request, file_path, new_line, to_content)
position = Gitlab::Diff::Position.new(old_path: file_path, position = Gitlab::Diff::Position.new(old_path: file_path,
new_path: file_path, new_path: file_path,
old_line: nil, old_line: nil,
...@@ -29,69 +32,111 @@ RSpec.describe Gitlab::Suggestions::CommitMessage do ...@@ -29,69 +32,111 @@ RSpec.describe Gitlab::Suggestions::CommitMessage do
create(:project, :repository, path: 'project-1', name: 'Project_1') create(:project, :repository, path: 'project-1', name: 'Project_1')
end end
let_it_be(:merge_request) do let_it_be(:forked_project) { fork_project(project, nil, repository: true) }
let_it_be(:merge_request_same_project) do
create(:merge_request, source_project: project, target_project: project) create(:merge_request, source_project: project, target_project: project)
end end
let_it_be(:suggestion_set) do let_it_be(:merge_request_from_fork) do
suggestion1 = create_suggestion('files/ruby/popen.rb', 9, '*** SUGGESTION 1 ***') create(:merge_request, source_project: forked_project, target_project: project)
suggestion2 = create_suggestion('files/ruby/popen.rb', 13, '*** SUGGESTION 2 ***') end
suggestion3 = create_suggestion('files/ruby/regex.rb', 22, '*** SUGGESTION 3 ***')
let_it_be(:suggestion_set_same_project) do
suggestion1 = create_suggestion(merge_request_same_project, 'files/ruby/popen.rb', 9, '*** SUGGESTION 1 ***')
suggestion2 = create_suggestion(merge_request_same_project, 'files/ruby/popen.rb', 13, '*** SUGGESTION 2 ***')
suggestion3 = create_suggestion(merge_request_same_project, 'files/ruby/regex.rb', 22, '*** SUGGESTION 3 ***')
Gitlab::Suggestions::SuggestionSet.new([suggestion1, suggestion2, suggestion3])
end
let_it_be(:suggestion_set_forked_project) do
suggestion1 = create_suggestion(merge_request_from_fork, 'files/ruby/popen.rb', 9, '*** SUGGESTION 1 ***')
suggestion2 = create_suggestion(merge_request_from_fork, 'files/ruby/popen.rb', 13, '*** SUGGESTION 2 ***')
suggestion3 = create_suggestion(merge_request_from_fork, 'files/ruby/regex.rb', 22, '*** SUGGESTION 3 ***')
Gitlab::Suggestions::SuggestionSet.new([suggestion1, suggestion2, suggestion3]) Gitlab::Suggestions::SuggestionSet.new([suggestion1, suggestion2, suggestion3])
end end
describe '#message' do describe '#message' do
before do where(:suggestion_set) { [ref(:suggestion_set_same_project), ref(:suggestion_set_forked_project)] }
# Updating the suggestion_commit_message on a project shared across specs
# avoids recreating the repository for each spec. with_them do
project.update!(suggestion_commit_message: message) before do
end # Updating the suggestion_commit_message on a project shared across specs
# avoids recreating the repository for each spec.
project.update!(suggestion_commit_message: message)
forked_project.update!(suggestion_commit_message: fork_message)
end
let(:fork_message) { nil }
context 'when a custom commit message is not specified' do context 'when a custom commit message is not specified' do
let(:expected_message) { 'Apply 3 suggestion(s) to 2 file(s)' } let(:expected_message) { 'Apply 3 suggestion(s) to 2 file(s)' }
context 'and is nil' do context 'and is nil' do
let(:message) { nil } let(:message) { nil }
it 'uses the default commit message' do it 'uses the default commit message' do
expect(described_class expect(described_class
.new(user, suggestion_set) .new(user, suggestion_set)
.message).to eq(expected_message) .message).to eq(expected_message)
end
end end
end
context 'and is an empty string' do context 'and is an empty string' do
let(:message) { '' } let(:message) { '' }
it 'uses the default commit message' do it 'uses the default commit message' do
expect(described_class expect(described_class
.new(user, suggestion_set) .new(user, suggestion_set)
.message).to eq(expected_message) .message).to eq(expected_message)
end
end end
end
end
context 'when a custom commit message is specified' do context 'when a custom commit message is specified for forked project' do
let(:message) { "i'm a project message. a user's custom message takes precedence over me :(" } let(:message) { nil }
let(:custom_message) { "hello there! i'm a cool custom commit message." } let(:fork_message) { "I'm a sad message that will not be used :(" }
it 'shows the custom commit message' do it 'uses the default commit message' do
expect(Gitlab::Suggestions::CommitMessage expect(described_class
.new(user, suggestion_set, custom_message) .new(user, suggestion_set)
.message).to eq(custom_message) .message).to eq(expected_message)
end
end
end end
end
context 'is specified and includes all placeholders' do context 'when a custom commit message is specified' do
let(:message) do let(:message) { "i'm a project message. a user's custom message takes precedence over me :(" }
'*** %{branch_name} %{files_count} %{file_paths} %{project_name} %{project_path} %{user_full_name} %{username} %{suggestions_count} ***' let(:custom_message) { "hello there! i'm a cool custom commit message." }
it 'shows the custom commit message' do
expect(Gitlab::Suggestions::CommitMessage
.new(user, suggestion_set, custom_message)
.message).to eq(custom_message)
end
end end
it 'generates a custom commit message' do context 'is specified and includes all placeholders' do
expect(Gitlab::Suggestions::CommitMessage let(:message) do
.new(user, suggestion_set) '*** %{branch_name} %{files_count} %{file_paths} %{project_name} %{project_path} %{user_full_name} %{username} %{suggestions_count} ***'
.message).to eq('*** master 2 files/ruby/popen.rb, files/ruby/regex.rb Project_1 project-1 Test User test.user 3 ***') end
it 'generates a custom commit message' do
expect(Gitlab::Suggestions::CommitMessage
.new(user, suggestion_set)
.message).to eq('*** master 2 files/ruby/popen.rb, files/ruby/regex.rb Project_1 project-1 Test User test.user 3 ***')
end
context 'when a custom commit message is specified for forked project' do
let(:fork_message) { "I'm a sad message that will not be used :(" }
it 'uses the target project commit message' do
expect(Gitlab::Suggestions::CommitMessage
.new(user, suggestion_set)
.message).to eq('*** master 2 files/ruby/popen.rb, files/ruby/regex.rb Project_1 project-1 Test User test.user 3 ***')
end
end
end end
end end
end end
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Suggestions::SuggestionSet do RSpec.describe Gitlab::Suggestions::SuggestionSet do
include ProjectForksHelper
using RSpec::Parameterized::TableSyntax
def create_suggestion(file_path, new_line, to_content) def create_suggestion(file_path, new_line, to_content)
position = Gitlab::Diff::Position.new(old_path: file_path, position = Gitlab::Diff::Position.new(old_path: file_path,
new_path: file_path, new_path: file_path,
...@@ -24,86 +27,99 @@ RSpec.describe Gitlab::Suggestions::SuggestionSet do ...@@ -24,86 +27,99 @@ RSpec.describe Gitlab::Suggestions::SuggestionSet do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:forked_project) { fork_project(project, nil, repository: true) }
let_it_be(:merge_request) do let_it_be(:merge_request_same_project) do
create(:merge_request, source_project: project, target_project: project) create(:merge_request, source_project: project, target_project: project)
end end
let_it_be(:suggestion) { create(:suggestion)} let_it_be(:merge_request_from_fork) do
create(:merge_request, source_project: forked_project, target_project: project)
let_it_be(:suggestion2) do
create_suggestion('files/ruby/popen.rb', 13, "*** SUGGESTION 2 ***")
end
let_it_be(:suggestion3) do
create_suggestion('files/ruby/regex.rb', 22, "*** SUGGESTION 3 ***")
end end
let_it_be(:unappliable_suggestion) { create(:suggestion, :unappliable) } where(:merge_request) { [ref(:merge_request_same_project), ref(:merge_request_from_fork)] }
with_them do
let(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) }
let(:suggestion) { create(:suggestion, note: note) }
let(:suggestion_set) { described_class.new([suggestion]) } let(:suggestion2) do
create_suggestion('files/ruby/popen.rb', 13, "*** SUGGESTION 2 ***")
describe '#project' do end
it 'returns the project associated with the suggestions' do
expected_project = suggestion.project
expect(suggestion_set.project).to be(expected_project) let(:suggestion3) do
create_suggestion('files/ruby/regex.rb', 22, "*** SUGGESTION 3 ***")
end end
end
describe '#branch' do let(:unappliable_suggestion) { create(:suggestion, :unappliable) }
it 'returns the branch associated with the suggestions' do
expected_branch = suggestion.branch let(:suggestion_set) { described_class.new([suggestion]) }
expect(suggestion_set.branch).to be(expected_branch) describe '#source_project' do
it 'returns the source project associated with the suggestions' do
expect(suggestion_set.source_project).to be(merge_request.source_project)
end
end end
end
describe '#valid?' do describe '#target_project' do
it 'returns true if no errors are found' do it 'returns the target project associated with the suggestions' do
expect(suggestion_set.valid?).to be(true) expect(suggestion_set.target_project).to be(project)
end
end end
it 'returns false if an error is found' do describe '#branch' do
suggestion_set = described_class.new([unappliable_suggestion]) it 'returns the branch associated with the suggestions' do
expected_branch = suggestion.branch
expect(suggestion_set.valid?).to be(false) expect(suggestion_set.branch).to be(expected_branch)
end
end end
end
describe '#error_message' do describe '#valid?' do
it 'returns an error message if an error is found' do it 'returns true if no errors are found' do
suggestion_set = described_class.new([unappliable_suggestion]) expect(suggestion_set.valid?).to be(true)
end
expect(suggestion_set.error_message).to be_a(String) it 'returns false if an error is found' do
suggestion_set = described_class.new([unappliable_suggestion])
expect(suggestion_set.valid?).to be(false)
end
end end
it 'returns nil if no errors are found' do describe '#error_message' do
expect(suggestion_set.error_message).to be(nil) it 'returns an error message if an error is found' do
suggestion_set = described_class.new([unappliable_suggestion])
expect(suggestion_set.error_message).to be_a(String)
end
it 'returns nil if no errors are found' do
expect(suggestion_set.error_message).to be(nil)
end
end end
end
describe '#actions' do describe '#actions' do
it 'returns an array of hashes with proper key/value pairs' do it 'returns an array of hashes with proper key/value pairs' do
first_action = suggestion_set.actions.first first_action = suggestion_set.actions.first
file_suggestion = suggestion_set.send(:suggestions_per_file).first file_suggestion = suggestion_set.send(:suggestions_per_file).first
expect(first_action[:action]).to be('update') expect(first_action[:action]).to be('update')
expect(first_action[:file_path]).to eq(file_suggestion.file_path) expect(first_action[:file_path]).to eq(file_suggestion.file_path)
expect(first_action[:content]).to eq(file_suggestion.new_content) expect(first_action[:content]).to eq(file_suggestion.new_content)
end
end end
end
describe '#file_paths' do describe '#file_paths' do
it 'returns an array of unique file paths associated with the suggestions' do it 'returns an array of unique file paths associated with the suggestions' do
suggestion_set = described_class.new([suggestion, suggestion2, suggestion3]) suggestion_set = described_class.new([suggestion, suggestion2, suggestion3])
expected_paths = %w(files/ruby/popen.rb files/ruby/regex.rb) expected_paths = %w(files/ruby/popen.rb files/ruby/regex.rb)
actual_paths = suggestion_set.file_paths actual_paths = suggestion_set.file_paths
expect(actual_paths.sort).to eq(expected_paths) expect(actual_paths.sort).to eq(expected_paths)
end
end end
end end
end end
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