diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index d0051010b65..519d9cd0d52 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -472,8 +472,8 @@ class ProjectsController < Projects::ApplicationController :suggestion_commit_message, :packages_enabled, :service_desk_enabled, - :merge_commit_template, - :squash_commit_template, + :merge_commit_template_or_default, + :squash_commit_template_or_default, project_setting_attributes: project_setting_attributes ] + [project_feature_attributes: project_feature_attributes] end diff --git a/app/models/project.rb b/app/models/project.rb index ebf7a2ac25b..512c6ac1acb 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -74,6 +74,21 @@ class Project < ApplicationRecord GL_REPOSITORY_TYPES = [Gitlab::GlRepository::PROJECT, Gitlab::GlRepository::WIKI, Gitlab::GlRepository::DESIGN].freeze + MAX_SUGGESTIONS_TEMPLATE_LENGTH = 255 + MAX_COMMIT_TEMPLATE_LENGTH = 500 + + DEFAULT_MERGE_COMMIT_TEMPLATE = <<~MSG.rstrip.freeze + Merge branch '%{source_branch}' into '%{target_branch}' + + %{title} + + %{issues} + + See merge request %{reference} + MSG + + DEFAULT_SQUASH_COMMIT_TEMPLATE = '%{title}' + cache_markdown_field :description, pipeline: :description default_value_for :packages_enabled, true @@ -506,7 +521,7 @@ class Project < ApplicationRecord validates :variables, nested_attributes_duplicates: { scope: :environment_scope } validates :bfg_object_map, file_size: { maximum: :max_attachment_size } validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true } - validates :suggestion_commit_message, length: { maximum: 255 } + validates :suggestion_commit_message, length: { maximum: MAX_SUGGESTIONS_TEMPLATE_LENGTH } # Scopes scope :pending_delete, -> { where(pending_delete: true) } @@ -2764,6 +2779,32 @@ class Project < ApplicationRecord ].compact.min end + def merge_commit_template_or_default + merge_commit_template.presence || DEFAULT_MERGE_COMMIT_TEMPLATE + end + + def merge_commit_template_or_default=(value) + project_setting.merge_commit_template = + if value.blank? || value.delete("\r") == DEFAULT_MERGE_COMMIT_TEMPLATE + nil + else + value + end + end + + def squash_commit_template_or_default + squash_commit_template.presence || DEFAULT_SQUASH_COMMIT_TEMPLATE + end + + def squash_commit_template_or_default=(value) + project_setting.squash_commit_template = + if value.blank? || value.delete("\r") == DEFAULT_SQUASH_COMMIT_TEMPLATE + nil + else + value + end + end + private # overridden in EE diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb index a3034488c38..c17dff553a0 100644 --- a/app/models/project_setting.rb +++ b/app/models/project_setting.rb @@ -16,8 +16,8 @@ class ProjectSetting < ApplicationRecord self.primary_key = :project_id - validates :merge_commit_template, length: { maximum: 500 } - validates :squash_commit_template, length: { maximum: 500 } + validates :merge_commit_template, length: { maximum: Project::MAX_COMMIT_TEMPLATE_LENGTH } + validates :squash_commit_template, length: { maximum: Project::MAX_COMMIT_TEMPLATE_LENGTH } def squash_enabled_by_default? %w[always default_on].include?(squash_option) diff --git a/app/views/projects/_merge_request_merge_commit_template.html.haml b/app/views/projects/_merge_request_merge_commit_template.html.haml index 1c023ae6ceb..502014b7279 100644 --- a/app/views/projects/_merge_request_merge_commit_template.html.haml +++ b/app/views/projects/_merge_request_merge_commit_template.html.haml @@ -5,10 +5,10 @@ %p.text-secondary = s_('ProjectSettings|The commit message used when merging, if the merge method creates a merge commit.') .mb-2 - - default_merge_commit_template = "Merge branch '%{source_branch}' into '%{target_branch}'\n\n%{title}\n\n%{issues}\n\nSee merge request %{reference}" - = form.text_area :merge_commit_template, class: 'form-control gl-form-input', rows: 8, maxlength: 500, placeholder: default_merge_commit_template + = form.text_area :merge_commit_template_or_default, class: 'form-control gl-form-input', rows: 8, maxlength: Project::MAX_COMMIT_TEMPLATE_LENGTH, placeholder: s_('ProjectSettings|The default template will be applied on save.') %p.form-text.text-muted - = s_('ProjectSettings|Maximum 500 characters.') + = s_('ProjectSettings|Leave empty to use default template.') + = sprintf(s_('ProjectSettings|Maximum %{maxLength} characters.'), { maxLength: Project::MAX_COMMIT_TEMPLATE_LENGTH }) - configure_the_merge_commit_message_help_link_url = help_page_path('user/project/merge_requests/commit_templates.md') - configure_the_merge_commit_message_help_link_start = ''.html_safe % { url: configure_the_merge_commit_message_help_link_url } = s_('ProjectSettings|%{link_start}What variables can I use?%{link_end}').html_safe % { link_start: configure_the_merge_commit_message_help_link_start, link_end: ''.html_safe } diff --git a/app/views/projects/_merge_request_merge_suggestions_settings.html.haml b/app/views/projects/_merge_request_merge_suggestions_settings.html.haml index 9ed21593203..eb2fc05686c 100644 --- a/app/views/projects/_merge_request_merge_suggestions_settings.html.haml +++ b/app/views/projects/_merge_request_merge_suggestions_settings.html.haml @@ -7,6 +7,8 @@ .mb-2 = form.text_field :suggestion_commit_message, class: 'form-control mb-2', placeholder: Gitlab::Suggestions::CommitMessage::DEFAULT_SUGGESTION_COMMIT_MESSAGE %p.form-text.text-muted + = s_('ProjectSettings|Leave empty to use default template.') + = sprintf(s_('ProjectSettings|Maximum %{maxLength} characters.'), { maxLength: Project::MAX_SUGGESTIONS_TEMPLATE_LENGTH }) - configure_the_commit_message_for_applied_suggestions_help_link_url = help_page_path('user/project/merge_requests/reviews/suggestions.md', anchor: 'configure-the-commit-message-for-applied-suggestions') - configure_the_commit_message_for_applied_suggestions_help_link_start = ''.html_safe % { url: configure_the_commit_message_for_applied_suggestions_help_link_url } = s_('ProjectSettings|%{link_start}What variables can I use?%{link_end}').html_safe % { link_start: configure_the_commit_message_for_applied_suggestions_help_link_start, link_end: ''.html_safe } diff --git a/app/views/projects/_merge_request_squash_commit_template.html.haml b/app/views/projects/_merge_request_squash_commit_template.html.haml index be1d78154c6..4d1b89bea83 100644 --- a/app/views/projects/_merge_request_squash_commit_template.html.haml +++ b/app/views/projects/_merge_request_squash_commit_template.html.haml @@ -5,9 +5,10 @@ %p.text-secondary = s_('ProjectSettings|The commit message used when squashing commits.') .mb-2 - = form.text_area :squash_commit_template, class: 'form-control gl-form-input', rows: 8, maxlength: 500, placeholder: '%{title}' + = form.text_area :squash_commit_template_or_default, class: 'form-control gl-form-input', rows: 8, maxlength: Project::MAX_COMMIT_TEMPLATE_LENGTH, placeholder: s_('ProjectSettings|The default template will be applied on save.') %p.form-text.text-muted - = s_('ProjectSettings|Maximum 500 characters.') + = s_('ProjectSettings|Leave empty to use default template.') + = sprintf(s_('ProjectSettings|Maximum %{maxLength} characters.'), { maxLength: Project::MAX_COMMIT_TEMPLATE_LENGTH }) - configure_the_squash_commit_message_help_link_url = help_page_path('user/project/merge_requests/commit_templates.md') - configure_the_squash_commit_message_help_link_start = ''.html_safe % { url: configure_the_squash_commit_message_help_link_url } = s_('ProjectSettings|%{link_start}What variables can I use?%{link_end}').html_safe % { link_start: configure_the_squash_commit_message_help_link_start, link_end: ''.html_safe } diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c6c038b3b01..76ce6fc67c9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -28455,13 +28455,16 @@ msgstr "" msgid "ProjectSettings|Learn about commit history." msgstr "" +msgid "ProjectSettings|Leave empty to use default template." +msgstr "" + msgid "ProjectSettings|Manage who can see the project in the public access directory." msgstr "" msgid "ProjectSettings|Manages large files such as audio, video, and graphics files." msgstr "" -msgid "ProjectSettings|Maximum 500 characters." +msgid "ProjectSettings|Maximum %{maxLength} characters." msgstr "" msgid "ProjectSettings|Merge checks" @@ -28602,6 +28605,9 @@ msgstr "" msgid "ProjectSettings|The default target project for merge requests created in this fork project." msgstr "" +msgid "ProjectSettings|The default template will be applied on save." +msgstr "" + msgid "ProjectSettings|These checks must pass before merge requests can be merged." msgstr "" diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index ad63bf8b760..f24e5a42e95 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -870,6 +870,85 @@ RSpec.describe Project, factory_default: :keep do end end + describe '#merge_commit_template_or_default' do + let_it_be(:project) { create(:project) } + + it 'returns default merge commit template' do + expect(project.merge_commit_template_or_default).to eq(Project::DEFAULT_MERGE_COMMIT_TEMPLATE) + end + + context 'when merge commit template is set and not nil' do + before do + project.merge_commit_template = '%{description}' + end + + it 'returns current value' do + expect(project.merge_commit_template_or_default).to eq('%{description}') + end + end + end + + describe '#merge_commit_template_or_default=' do + let_it_be(:project) { create(:project) } + + it 'sets template to nil when set to default value' do + project.merge_commit_template_or_default = Project::DEFAULT_MERGE_COMMIT_TEMPLATE + expect(project.merge_commit_template).to be_nil + end + + it 'sets template to nil when set to default value but with CRLF line endings' do + project.merge_commit_template_or_default = "Merge branch '%{source_branch}' into '%{target_branch}'\r\n\r\n%{title}\r\n\r\n%{issues}\r\n\r\nSee merge request %{reference}" + expect(project.merge_commit_template).to be_nil + end + + it 'allows changing template' do + project.merge_commit_template_or_default = '%{description}' + expect(project.merge_commit_template).to eq('%{description}') + end + + it 'allows setting template to nil' do + project.merge_commit_template_or_default = nil + expect(project.merge_commit_template).to be_nil + end + end + + describe '#squash_commit_template_or_default' do + let_it_be(:project) { create(:project) } + + it 'returns default squash commit template' do + expect(project.squash_commit_template_or_default).to eq(Project::DEFAULT_SQUASH_COMMIT_TEMPLATE) + end + + context 'when squash commit template is set and not nil' do + before do + project.squash_commit_template = '%{description}' + end + + it 'returns current value' do + expect(project.squash_commit_template_or_default).to eq('%{description}') + end + end + end + + describe '#squash_commit_template_or_default=' do + let_it_be(:project) { create(:project) } + + it 'sets template to nil when set to default value' do + project.squash_commit_template_or_default = Project::DEFAULT_SQUASH_COMMIT_TEMPLATE + expect(project.squash_commit_template).to be_nil + end + + it 'allows changing template' do + project.squash_commit_template_or_default = '%{description}' + expect(project.squash_commit_template).to eq('%{description}') + end + + it 'allows setting template to nil' do + project.squash_commit_template_or_default = nil + expect(project.squash_commit_template).to be_nil + end + end + describe 'reference methods' do # TODO update when we have multiple owners of a project # https://gitlab.com/gitlab-org/gitlab/-/issues/350605 diff --git a/spec/views/projects/edit.html.haml_spec.rb b/spec/views/projects/edit.html.haml_spec.rb index e39ea9e74c5..a85ddf7a005 100644 --- a/spec/views/projects/edit.html.haml_spec.rb +++ b/spec/views/projects/edit.html.haml_spec.rb @@ -45,10 +45,10 @@ RSpec.describe 'projects/edit' do end context 'merge commit template' do - it 'displays a placeholder if none is set' do + it 'displays default template if none is set' do render - expect(rendered).to have_field('project[merge_commit_template]', placeholder: <<~MSG.rstrip) + expect(rendered).to have_field('project[merge_commit_template_or_default]', with: <<~MSG.rstrip) Merge branch '%{source_branch}' into '%{target_branch}' %{title} @@ -64,15 +64,15 @@ RSpec.describe 'projects/edit' do render - expect(rendered).to have_field('project[merge_commit_template]', with: '%{title}') + expect(rendered).to have_field('project[merge_commit_template_or_default]', with: '%{title}') end end context 'squash template' do - it 'displays a placeholder if none is set' do + it 'displays default template if none is set' do render - expect(rendered).to have_field('project[squash_commit_template]', placeholder: '%{title}') + expect(rendered).to have_field('project[squash_commit_template_or_default]', with: '%{title}') end it 'displays the user entered value' do @@ -80,7 +80,7 @@ RSpec.describe 'projects/edit' do render - expect(rendered).to have_field('project[squash_commit_template]', with: '%{first_multiline_commit}') + expect(rendered).to have_field('project[squash_commit_template_or_default]', with: '%{first_multiline_commit}') end end