Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-29 00:08:52 +00:00
parent 71d6b9014b
commit 21b8ed2e35
24 changed files with 257 additions and 61 deletions

View File

@ -116,6 +116,7 @@ module Projects
def project_params_attributes
[
:issue_branch_template,
:default_branch,
:autoclose_referenced_issues
]

View File

@ -39,6 +39,7 @@ class Issue < ApplicationRecord
DueNextMonthAndPreviousTwoWeeks = DueDateStruct.new('Due Next Month And Previous Two Weeks', 'next_month_and_previous_two_weeks').freeze
SORTING_PREFERENCE_FIELD = :issues_sort
MAX_BRANCH_TEMPLATE = 255
# Types of issues that should be displayed on issue lists across the app
# for example, project issues list, group issues list, and issues dashboard.
@ -394,10 +395,21 @@ class Issue < ApplicationRecord
)
end
def self.to_branch_name(*args)
branch_name = args.map(&:to_s).each_with_index.map do |arg, i|
arg.parameterize(preserve_case: i == 0).presence
end.compact.join('-')
def self.to_branch_name(id, title, project: nil)
params = {
'id' => id.to_s.parameterize(preserve_case: true),
'title' => title.to_s.parameterize
}
template = project&.issue_branch_template
branch_name =
if template.present?
Gitlab::StringPlaceholderReplacer.replace_string_placeholders(template, /(#{params.keys.join('|')})/) do |arg|
params[arg]
end
else
params.values.select(&:present?).join('-')
end
if branch_name.length > 100
truncated_string = branch_name[0, 100]
@ -475,7 +487,7 @@ class Issue < ApplicationRecord
if self.confidential?
"#{iid}-confidential-issue"
else
self.class.to_branch_name(iid, title)
self.class.to_branch_name(iid, title, project: project)
end
end

View File

@ -491,6 +491,7 @@ class Project < ApplicationRecord
to: :project_setting
delegate :merge_commit_template, :merge_commit_template=, to: :project_setting, allow_nil: true
delegate :squash_commit_template, :squash_commit_template=, to: :project_setting, allow_nil: true
delegate :issue_branch_template, :issue_branch_template=, to: :project_setting, allow_nil: true
delegate :log_jira_dvcs_integration_usage, :jira_dvcs_server_last_sync_at, :jira_dvcs_cloud_last_sync_at, to: :feature_usage

View File

@ -20,6 +20,7 @@ class ProjectSetting < ApplicationRecord
validates :merge_commit_template, length: { maximum: Project::MAX_COMMIT_TEMPLATE_LENGTH }
validates :squash_commit_template, length: { maximum: Project::MAX_COMMIT_TEMPLATE_LENGTH }
validates :issue_branch_template, length: { maximum: Issue::MAX_BRANCH_TEMPLATE }
validates :target_platforms, inclusion: { in: ALLOWED_TARGET_PLATFORMS }
validates :suggested_reviewers_enabled, inclusion: { in: [true, false] }

View File

@ -0,0 +1,14 @@
- if @project.project_feature.issues_access_level > 0
%fieldset#branch-names-settings
.form-group
= f.label :issue_branch_template, _('Branch name template'), class: 'label-bold'
%p= s_('ProjectSettings|Branches created from issues follow this pattern.')
.form-group
.gl-mb-2
= f.text_field :issue_branch_template, class: 'form-control gl-mb-2', placeholder: "%{id}-%{title}"
%p.form-text.text-muted
= s_('ProjectSettings|Leave empty to use default template.')
= sprintf(s_('ProjectSettings|Maximum %{maxLength} characters.'), { maxLength: Issue::MAX_BRANCH_TEMPLATE })
- branch_name_help_link = help_page_path('user/project/repository/web_editor.md', anchor: 'create-a-new-branch-from-an-issue')
= link_to _('What variables can I use?'), branch_name_help_link, target: "_blank"

View File

@ -13,4 +13,5 @@
= gitlab_ui_form_for @project, url: url, method: :put, html: { multipart: true, class: "issue-settings-form js-issue-settings-form" }, authenticity_token: true do |f|
%input{ name: 'update_section', type: 'hidden', value: 'js-issue-settings' }
= render 'projects/branch_defaults/default_branch_fields', f: f
= render 'projects/branch_defaults/branch_names_fields', f: f
= f.submit _('Save changes'), pajamas_button: true, data: { qa_selector: 'save_changes_button' }

View File

@ -1,4 +1,4 @@
.float-left.gl-md-ml-3.dropdown.gl-new-dropdown{ class: "gl-display-none! gl-md-display-flex!" }
.gl-md-ml-3.dropdown.gl-new-dropdown{ class: "gl-display-none! gl-md-display-flex!" }
#js-check-out-modal{ data: how_merge_modal_data(@merge_request) }
= button_tag type: 'button', class: "btn dropdown-toggle btn-confirm gl-button gl-dropdown-toggle", data: { toggle: 'dropdown', qa_selector: 'mr_code_dropdown' } do
%span.gl-new-dropdown-button-text= _('Code')

View File

@ -25,9 +25,9 @@
- display_class = moved_mr_sidebar_enabled? ? 'gl-md-display-none!' : 'gl-sm-display-none!'
= render Pajamas::ButtonComponent.new(icon: "chevron-double-lg-left", button_options: { class: "btn-icon float-right gl-display-block gutter-toggle issuable-gutter-toggle js-sidebar-toggle #{display_class}" })
.detail-page-header-actions.gl-align-self-start.is-merge-request.js-issuable-actions
.detail-page-header-actions.gl-align-self-start.is-merge-request.js-issuable-actions.gl-display-flex
- if can_update_merge_request
= link_to _('Edit'), edit_project_merge_request_path(@project, @merge_request), class: "gl-display-none gl-md-display-block btn gl-button btn-default btn-grouped js-issuable-edit", data: { qa_selector: "edit_button" }
= link_to _('Edit'), edit_project_merge_request_path(@project, @merge_request), class: "gl-display-none gl-md-display-block btn gl-button btn-default js-issuable-edit", data: { qa_selector: "edit_button" }
- if @merge_request.source_project
= render 'projects/merge_requests/code_dropdown'

View File

@ -1,8 +0,0 @@
---
name: approval_rules_eligible_filter
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100192
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/376331
milestone: '15.5'
type: development
group: group::source code
default_enabled: false

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddIssueBranchTemplateToProjectSettings < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
def up
with_lock_retries do
add_column :project_settings, :issue_branch_template, :text, if_not_exists: true
end
add_text_limit :project_settings, :issue_branch_template, 255
end
def down
remove_column :project_settings, :issue_branch_template, if_exists: true
end
end

View File

@ -0,0 +1 @@
5e3fbb2c033f8512e5fd14b8ce8c6088866c596a2b769e115dcc1feb9ce9d041

View File

@ -20190,12 +20190,14 @@ CREATE TABLE project_settings (
target_platforms character varying[] DEFAULT '{}'::character varying[] NOT NULL,
enforce_auth_checks_on_uploads boolean DEFAULT true NOT NULL,
selective_code_owner_removals boolean DEFAULT false NOT NULL,
issue_branch_template text,
show_diff_preview_in_email boolean DEFAULT true NOT NULL,
jitsu_key text,
suggested_reviewers_enabled boolean DEFAULT false NOT NULL,
only_allow_merge_if_all_status_checks_passed boolean DEFAULT false NOT NULL,
CONSTRAINT check_2981f15877 CHECK ((char_length(jitsu_key) <= 100)),
CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)),
CONSTRAINT check_3ca5cbffe6 CHECK ((char_length(issue_branch_template) <= 255)),
CONSTRAINT check_b09644994b CHECK ((char_length(squash_commit_template) <= 500)),
CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)),
CONSTRAINT check_eaf7cfb6a7 CHECK ((char_length(merge_commit_template) <= 500))

View File

@ -233,48 +233,6 @@ user.max_member_access_for_group group.id
## Merge requests
### Close a merge request
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
MergeRequests::CloseService.new(project: p, current_user: u).execute(m)
```
### Delete a merge request
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
Issuable::DestroyService.new(project: m.project, current_user: u).execute(m)
```
### Rebase manually
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
MergeRequests::RebaseService.new(project: m.target_project, current_user: u).execute(m)
```
### Set a merge request as merged
Use when a merge request was accepted and the changes merged into the Git repository,
but the merge request still shows as open.
If the changes are not merged yet, this action causes the merge request to
incorrectly show `merged into <branch-name>`.
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
MergeRequests::PostMergeService.new(project: p, current_user: u).execute(m)
```
## CI
This content has been moved to [Troubleshooting CI/CD](../../ci/troubleshooting.md).

View File

@ -289,3 +289,76 @@ For a web developer writing a webpage for your company's website:
- [Commits](commits.md)
- [CI/CD pipelines](../../../ci/index.md)
- [Push options](../push_options.md) for merge requests
## Troubleshooting
### Rebase a merge request from the Rails console **(FREE SELF)**
In addition to the `/rebase` [quick action](../quick_actions.md#issues-merge-requests-and-epics),
users with access to the [Rails console](../../../administration/operations/rails_console.md)
can rebase a merge request from the Rails console. Replace `<username>`,
`<namespace/project>`, and `<iid>` with appropriate values:
WARNING:
Any command that changes data directly could be damaging if not run correctly,
or under the right conditions. We highly recommend running them in a test environment
with a backup of the instance ready to be restored, just in case.
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
MergeRequests::RebaseService.new(project: m.target_project, current_user: u).execute(m)
```
### Fix incorrect merge request status **(FREE SELF)**
If a merge request remains **Open** after its changes are merged,
users with access to the [Rails console](../../../administration/operations/rails_console.md)
can correct the merge request's status. Replace `<username>`, `<namespace/project>`,
and `<iid>` with appropriate values:
WARNING:
Any command that changes data directly could be damaging if not run correctly,
or under the right conditions. We highly recommend running them in a test environment
with a backup of the instance ready to be restored, just in case.
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
MergeRequests::PostMergeService.new(project: p, current_user: u).execute(m)
```
Running this command against a merge request with unmerged changes causes the
merge request to display an incorrect message: `merged into <branch-name>`.
### Close a merge request from the Rails console **(FREE SELF)**
If closing a merge request doesn't work through the UI or API, you may want to attempt to close it in a [Rails console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session):
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
MergeRequests::CloseService.new(project: p, current_user: u).execute(m)
```
### Delete a merge request from the Rails console **(FREE SELF)**
If deleting a merge request doesn't work through the UI or API, you may want to attempt to delete it in a [Rails console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session):
WARNING:
Any command that changes data directly could be damaging if not run correctly,
or under the right conditions. We highly recommend running them in a test environment
with a backup of the instance ready to be restored, just in case.
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
Issuable::DestroyService.new(project: m.project, current_user: u).execute(m)
```

View File

@ -133,6 +133,7 @@ module API
expose :suggestion_commit_message
expose :merge_commit_template
expose :squash_commit_template
expose :issue_branch_template
expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
}

View File

@ -65,6 +65,7 @@ module API
optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions'
optional :merge_commit_template, type: String, desc: 'Template used to create merge commit message'
optional :squash_commit_template, type: String, desc: 'Template used to create squash commit message'
optional :issue_branch_template, type: String, desc: 'Template used to create a branch from an issue'
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning'
optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled'
@ -174,6 +175,7 @@ module API
:suggestion_commit_message,
:merge_commit_template,
:squash_commit_template,
:issue_branch_template,
:repository_storage,
:packages_enabled,
:service_desk_enabled,

View File

@ -6831,6 +6831,9 @@ msgstr ""
msgid "Branch name"
msgstr ""
msgid "Branch name template"
msgstr ""
msgid "Branch not loaded - %{branchId}"
msgstr ""
@ -31796,6 +31799,9 @@ msgstr ""
msgid "ProjectSettings|Badges"
msgstr ""
msgid "ProjectSettings|Branches created from issues follow this pattern."
msgstr ""
msgid "ProjectSettings|Build, test, and deploy your changes."
msgstr ""
@ -45619,6 +45625,9 @@ msgstr ""
msgid "What templates can I create?"
msgstr ""
msgid "What variables can I use?"
msgstr ""
msgid "What will you use this group for?"
msgstr ""

View File

@ -38,7 +38,7 @@
"markdownlint:no-trailing-spaces:fix": "yarn run markdownlint:no-trailing-spaces --fix",
"postinstall": "node ./scripts/frontend/postinstall.js",
"storybook:install": "yarn --cwd ./storybook install",
"storybook:build": "yarn --cwd ./storybook build",
"storybook:build": "yarn --cwd ./storybook build --quiet",
"storybook:start": "./scripts/frontend/start_storybook.sh",
"swagger:validate": "swagger-cli validate",
"webpack": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.config.js",

View File

@ -168,5 +168,37 @@ RSpec.describe Projects::Settings::RepositoryController do
end
end
end
context 'when updating branch names template from issues' do
let(:branch_name_template) { 'feat/GL-%{id}-%{title}' }
let(:request_params) { base_params.merge({ project: project_params_attributes }) }
subject { put :update, params: request_params }
context('with a good request') do
let(:project_params_attributes) { { issue_branch_template: branch_name_template } }
it "updates issue_branch_template and redirect to project_settings_repository_path" do
subject
expect(response).to redirect_to project_settings_repository_path(project)
expect(controller).to set_flash[:notice].to("Project settings were successfully updated.")
expect(project.reload.issue_branch_template).to eq(branch_name_template)
end
end
context('with a bad input') do
let(:project_params_attributes) { { issue_branch_template: 'a' * 260 } }
it "updates issue_branch_template and redirect to project_settings_repository_path" do
subject
expect(response).to redirect_to project_settings_repository_path(project)
expect(controller).to set_flash[:alert].to("Project setting issue branch template is too long (maximum is 255 characters)")
expect(project.reload.issue_branch_template).to eq(nil)
end
end
end
end
end

View File

@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Project settings > repositories > Branch names', :js do
let_it_be(:project) { create(:project, :public) }
let(:user) { create(:user) }
before do
project.add_maintainer(user)
sign_in(user)
end
context 'when Issues are initially disabled' do
let(:project_feature) { project.project_feature }
before do
project_feature.update!(issues_access_level: ProjectFeature::DISABLED)
visit project_settings_repository_path(project)
end
it 'do not render the Branch names settings' do
expect(page).not_to have_content('Branch name template')
end
end
context 'when Issues are initially enabled' do
before do
visit project_settings_repository_path(project)
end
it 'shows the Branch names settings' do
expect(page).to have_content('Branch name template')
value = "feature-%{id}"
within('section#branch-defaults-settings') do
fill_in 'project[issue_branch_template]', with: value
click_on('Save changes')
end
expect(project.reload.issue_branch_template).to eq(value)
expect(page).to have_content('Branch name template')
end
end
end

View File

@ -568,6 +568,7 @@ Project:
- suggestion_commit_message
- merge_commit_template
- squash_commit_template
- issue_branch_template
Author:
- name
ProjectFeature:

View File

@ -864,7 +864,7 @@ RSpec.describe Issue do
describe '.to_branch_name' do
it 'parameterizes arguments and joins with dashes' do
expect(described_class.to_branch_name(123, 'foo bar', '!@#$%', 'f!o@o#b$a%r^')).to eq('123-foo-bar-f-o-o-b-a-r')
expect(described_class.to_branch_name(123, 'foo bar!@#$%f!o@o#b$a%r^')).to eq('123-foo-bar-f-o-o-b-a-r')
end
it 'preserves the case in the first argument' do
@ -872,7 +872,7 @@ RSpec.describe Issue do
end
it 'truncates branch name to at most 100 characters' do
expect(described_class.to_branch_name('a' * 101)).to eq('a' * 100)
expect(described_class.to_branch_name('a' * 101, 'a')).to eq('a' * 100)
end
it 'truncates dangling parts of the branch name' do
@ -884,6 +884,13 @@ RSpec.describe Issue do
# 100 characters would've got us "999-lorem...lacus-custom-fri".
expect(branch_name).to eq('999-lorem-ipsum-dolor-sit-amet-consectetur-adipiscing-elit-mauris-sit-amet-ipsum-id-lacus-custom')
end
it 'takes issue branch template into account' do
project = create(:project)
project.project_setting.update!(issue_branch_template: 'feature-%{id}-%{title}')
expect(described_class.to_branch_name(123, 'issue title', project: project)).to eq('feature-123-issue-title')
end
end
describe '#to_branch_name' do

View File

@ -20,6 +20,7 @@ RSpec.describe ProjectSetting, type: :model do
describe 'validations' do
it { is_expected.not_to allow_value(nil).for(:target_platforms) }
it { is_expected.to allow_value([]).for(:target_platforms) }
it { is_expected.to validate_length_of(:issue_branch_template).is_at_most(255) }
it { is_expected.not_to allow_value(nil).for(:suggested_reviewers_enabled) }
it { is_expected.to allow_value(true).for(:suggested_reviewers_enabled) }

View File

@ -9,6 +9,12 @@ const IS_EE = require('../../config/helpers/is_ee_env');
const IS_JH = require('../../config/helpers/is_jh_env');
const gitlabWebpackConfig = require('../../config/webpack.config');
const ROOT_PATH = path.resolve(__dirname, '..', '..');
const EMPTY_VUE_COMPONENT_PATH = path.join(
ROOT_PATH,
'app/assets/javascripts/vue_shared/components/empty_component.js',
);
const buildIncludePaths = (nodeSassIncludePaths, previouslyResolvedPath) => {
const includePaths = [];
if (path.isAbsolute(previouslyResolvedPath)) {
@ -144,6 +150,22 @@ module.exports = function storybookWebpackConfig({ config }) {
// Silence webpack warnings about moment/pikaday not being able to resolve.
config.plugins.push(new webpack.IgnorePlugin(/moment/, /pikaday/));
if (!IS_EE) {
config.plugins.push(
new webpack.NormalModuleReplacementPlugin(/^ee_component\/(.*)\.vue/, (resource) => {
resource.request = EMPTY_VUE_COMPONENT_PATH;
}),
);
}
if (!IS_JH) {
config.plugins.push(
new webpack.NormalModuleReplacementPlugin(/^jh_component\/(.*)\.vue/, (resource) => {
resource.request = EMPTY_VUE_COMPONENT_PATH;
}),
);
}
const baseIntegrationTestHelpersPath = 'spec/frontend_integration/test_helpers';
// Add any missing aliases from the main GitLab webpack config