summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-01-13 12:08:04 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-01-13 12:08:04 +0000
commiteb30dd6e28f6fc9eb8021d205f6ed84511f001e2 (patch)
tree9557e4782c762f4d08f57c9e04991bf988695085 /app
parentc3ad57034cc1cbd6d0ad02de7ac57f6004440c83 (diff)
downloadgitlab-ce-eb30dd6e28f6fc9eb8021d205f6ed84511f001e2.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/create_cluster/gke_cluster/components/gke_network_dropdown.vue53
-rw-r--r--app/assets/javascripts/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue44
-rw-r--r--app/assets/javascripts/reports/components/report_section.vue20
-rw-r--r--app/controllers/projects_controller.rb1
-rw-r--r--app/finders/pipelines_finder.rb2
-rw-r--r--app/graphql/types/project_type.rb2
-rw-r--r--app/models/ci/bridge.rb4
-rw-r--r--app/models/ci/pipeline.rb25
-rw-r--r--app/models/ci/pipeline_enums.rb8
-rw-r--r--app/models/ci/sources/pipeline.rb2
-rw-r--r--app/serializers/pipeline_details_entity.rb2
-rw-r--r--app/serializers/pipeline_serializer.rb1
-rw-r--r--app/services/ci/create_pipeline_service.rb7
-rw-r--r--app/services/suggestions/apply_service.rb37
-rw-r--r--app/views/devise/sessions/two_factor.html.haml2
-rw-r--r--app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml6
-rw-r--r--app/views/projects/_merge_request_merge_suggestions_settings.html.haml17
-rw-r--r--app/views/projects/_merge_request_settings.html.haml2
-rw-r--r--app/views/projects/_merge_request_settings_description_text.html.haml2
19 files changed, 212 insertions, 25 deletions
diff --git a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_network_dropdown.vue b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_network_dropdown.vue
new file mode 100644
index 00000000000..12b6070a79a
--- /dev/null
+++ b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_network_dropdown.vue
@@ -0,0 +1,53 @@
+<script>
+import { createNamespacedHelpers, mapState, mapGetters, mapActions } from 'vuex';
+
+import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
+
+const { mapState: mapDropdownState } = createNamespacedHelpers('networks');
+const { mapActions: mapSubnetworkActions } = createNamespacedHelpers('subnetworks');
+
+export default {
+ components: {
+ ClusterFormDropdown,
+ },
+ props: {
+ fieldName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['selectedNetwork']),
+ ...mapDropdownState(['items', 'isLoadingItems', 'loadingItemsError']),
+ ...mapGetters(['hasZone', 'projectId', 'region']),
+ },
+ methods: {
+ ...mapActions(['setNetwork', 'setSubnetwork']),
+ ...mapSubnetworkActions({ fetchSubnetworks: 'fetchItems' }),
+ setNetworkAndFetchSubnetworks(network) {
+ const { projectId: project, region } = this;
+
+ this.setSubnetwork('');
+ this.setNetwork(network);
+ this.fetchSubnetworks({ project, region, network: network.selfLink });
+ },
+ },
+};
+</script>
+<template>
+ <cluster-form-dropdown
+ :field-name="fieldName"
+ :value="selectedNetwork"
+ :items="items"
+ :disabled="!hasZone"
+ :loading="isLoadingItems"
+ :has-errors="Boolean(loadingItemsError)"
+ :loading-text="s__('ClusterIntegration|Loading networks')"
+ :placeholder="s__('ClusterIntergation|Select a network')"
+ :search-field-placeholder="s__('ClusterIntegration|Search networks')"
+ :empty-text="s__('ClusterIntegration|No networks found')"
+ :error-message="s__('ClusterIntegration|Could not load networks')"
+ :disabled-text="s__('ClusterIntegration|Select a zone to choose a network')"
+ @input="setNetworkAndFetchSubnetworks"
+ />
+</template>
diff --git a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue
new file mode 100644
index 00000000000..ec7889e2907
--- /dev/null
+++ b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue
@@ -0,0 +1,44 @@
+<script>
+import { createNamespacedHelpers, mapState, mapGetters, mapActions } from 'vuex';
+
+import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
+
+const { mapState: mapDropdownState } = createNamespacedHelpers('subnetworks');
+
+export default {
+ components: {
+ ClusterFormDropdown,
+ },
+ props: {
+ fieldName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['selectedSubnetwork']),
+ ...mapDropdownState(['items', 'isLoadingItems', 'loadingItemsError']),
+ ...mapGetters(['hasNetwork']),
+ },
+ methods: {
+ ...mapActions(['setSubnetwork']),
+ },
+};
+</script>
+<template>
+ <cluster-form-dropdown
+ :field-name="fieldName"
+ :value="selectedSubnetwork"
+ :items="items"
+ :disabled="!hasNetwork"
+ :loading="isLoadingItems"
+ :has-errors="Boolean(loadingItemsError)"
+ :loading-text="s__('ClusterIntegration|Loading subnetworks')"
+ :placeholder="s__('ClusterIntergation|Select a subnetwork')"
+ :search-field-placeholder="s__('ClusterIntegration|Search subnetworks')"
+ :empty-text="s__('ClusterIntegration|No subnetworks found')"
+ :error-message="s__('ClusterIntegration|Could not load subnetworks')"
+ :disabled-text="s__('ClusterIntegration|Select a network to choose a subnetwork')"
+ @input="setSubnetwork"
+ />
+</template>
diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue
index 45c890769a0..20b0c52dbda 100644
--- a/app/assets/javascripts/reports/components/report_section.vue
+++ b/app/assets/javascripts/reports/components/report_section.vue
@@ -165,21 +165,23 @@ export default {
<template>
<section class="media-section">
<div class="media">
- <status-icon :status="statusIconName" :size="24" />
- <div class="media-body d-flex flex-align-self-center">
- <span class="js-code-text code-text">
- {{ headerText }}
- <slot :name="slotName"></slot>
-
- <popover v-if="hasPopover" :options="popoverOptions" class="prepend-left-5" />
- </span>
+ <status-icon :status="statusIconName" :size="24" class="align-self-center" />
+ <div class="media-body d-flex flex-align-self-center align-items-center">
+ <div class="js-code-text code-text">
+ <div>
+ {{ headerText }}
+ <slot :name="slotName"></slot>
+ <popover v-if="hasPopover" :options="popoverOptions" class="prepend-left-5" />
+ </div>
+ <slot name="subHeading"></slot>
+ </div>
<slot name="actionButtons"></slot>
<button
v-if="isCollapsible"
type="button"
- class="js-collapse-btn btn float-right btn-sm align-self-start qa-expand-report-button"
+ class="js-collapse-btn btn float-right btn-sm align-self-center qa-expand-report-button"
@click="toggleCollapsed"
>
{{ collapseText }}
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index dd392bd39a8..1d9f81aaa23 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -387,6 +387,7 @@ class ProjectsController < Projects::ApplicationController
:merge_method,
:initialize_with_readme,
:autoclose_referenced_issues,
+ :suggestion_commit_message,
project_feature_attributes: %i[
builds_access_level
diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb
index 5a0d53d9683..48da44123f6 100644
--- a/app/finders/pipelines_finder.rb
+++ b/app/finders/pipelines_finder.rb
@@ -17,7 +17,7 @@ class PipelinesFinder
return Ci::Pipeline.none
end
- items = pipelines
+ items = pipelines.no_child
items = by_scope(items)
items = by_status(items)
items = by_ref(items)
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 0bd2b0c81d9..31cde7b6d48 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -104,6 +104,8 @@ module Types
description: 'Indicates if `Delete source branch` option should be enabled by default for all new merge requests of the project'
field :autoclose_referenced_issues, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if issues referenced by merge requests and commits within the default branch are closed automatically'
+ field :suggestion_commit_message, GraphQL::STRING_TYPE, null: true,
+ description: 'The commit message used to apply merge request suggestions'
field :namespace, Types::NamespaceType, null: true,
description: 'Namespace of the project'
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 804e01bfab0..e6d41dd2779 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -53,6 +53,10 @@ module Ci
def to_partial_path
'projects/generic_commit_statuses/generic_commit_status'
end
+
+ def yaml_for_downstream
+ nil
+ end
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 7a48fa8595b..5ad3a9b4431 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -60,7 +60,9 @@ module Ci
has_one :chat_data, class_name: 'Ci::PipelineChatData'
has_many :triggered_pipelines, through: :sourced_pipelines, source: :pipeline
+ has_many :child_pipelines, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :sourced_pipelines, source: :pipeline
has_one :triggered_by_pipeline, through: :source_pipeline, source: :source_pipeline
+ has_one :parent_pipeline, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :source_pipeline, source: :source_pipeline
has_one :source_job, through: :source_pipeline, source: :source_job
has_one :pipeline_config, class_name: 'Ci::PipelineConfig', inverse_of: :pipeline
@@ -212,6 +214,7 @@ module Ci
end
scope :internal, -> { where(source: internal_sources) }
+ scope :no_child, -> { where.not(source: :parent_pipeline) }
scope :ci_sources, -> { where(config_source: ::Ci::PipelineEnums.ci_config_sources_values) }
scope :for_user, -> (user) { where(user: user) }
scope :for_sha, -> (sha) { where(sha: sha) }
@@ -507,10 +510,6 @@ module Ci
builds.skipped.after_stage(stage_idx).find_each(&:process)
end
- def child?
- false
- end
-
def latest?
return false unless git_ref && commit.present?
@@ -693,6 +692,24 @@ module Ci
all_merge_requests.order(id: :desc)
end
+ # If pipeline is a child of another pipeline, include the parent
+ # and the siblings, otherwise return only itself.
+ def same_family_pipeline_ids
+ if (parent = parent_pipeline)
+ [parent.id] + parent.child_pipelines.pluck(:id)
+ else
+ [self.id]
+ end
+ end
+
+ def child?
+ parent_pipeline.present?
+ end
+
+ def parent?
+ child_pipelines.exists?
+ end
+
def detailed_status(current_user)
Gitlab::Ci::Status::Pipeline::Factory
.new(self, current_user)
diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb
index 3cd88807969..fde169d2f03 100644
--- a/app/models/ci/pipeline_enums.rb
+++ b/app/models/ci/pipeline_enums.rb
@@ -23,10 +23,13 @@ module Ci
schedule: 4,
api: 5,
external: 6,
+ # TODO: Rename `pipeline` to `cross_project_pipeline` in 13.0
+ # https://gitlab.com/gitlab-org/gitlab/issues/195991
pipeline: 7,
chat: 8,
merge_request_event: 10,
- external_pull_request_event: 11
+ external_pull_request_event: 11,
+ parent_pipeline: 12
}
end
@@ -38,7 +41,8 @@ module Ci
repository_source: 1,
auto_devops_source: 2,
remote_source: 4,
- external_project_source: 5
+ external_project_source: 5,
+ bridge_source: 6
}
end
diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb
index feaec27281c..d71e3b55b9a 100644
--- a/app/models/ci/sources/pipeline.rb
+++ b/app/models/ci/sources/pipeline.rb
@@ -18,6 +18,8 @@ module Ci
validates :source_project, presence: true
validates :source_job, presence: true
validates :source_pipeline, presence: true
+
+ scope :same_project, -> { where(arel_table[:source_project_id].eq(arel_table[:project_id])) }
end
end
end
diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb
index 71589ac8315..a4ab1d399bc 100644
--- a/app/serializers/pipeline_details_entity.rb
+++ b/app/serializers/pipeline_details_entity.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class PipelineDetailsEntity < PipelineEntity
+ expose :project, using: ProjectEntity
+
expose :flags do
expose :latest?, as: :latest
end
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index b25a1ea9209..be535a5d414 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -41,6 +41,7 @@ class PipelineSerializer < BaseSerializer
def preloaded_relations
[
:latest_statuses_ordered_by_stage,
+ :project,
:stages,
{
failed_builds: %i(project metadata)
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index ce3a9eb0772..2daf3a51235 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -23,7 +23,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Limit::JobActivity].freeze
# rubocop: disable Metrics/ParameterLists
- def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, external_pull_request: nil, **options, &block)
+ def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, external_pull_request: nil, bridge: nil, **options, &block)
@pipeline = Ci::Pipeline.new
command = Gitlab::Ci::Pipeline::Chain::Command.new(
@@ -46,6 +46,7 @@ module Ci
current_user: current_user,
push_options: params[:push_options] || {},
chat_data: params[:chat_data],
+ bridge: bridge,
**extra_options(options))
sequence = Gitlab::Ci::Pipeline::Chain::Sequence
@@ -104,14 +105,14 @@ module Ci
if Feature.enabled?(:ci_support_interruptible_pipelines, project, default_enabled: true)
project.ci_pipelines
.where(ref: pipeline.ref)
- .where.not(id: pipeline.id)
+ .where.not(id: pipeline.same_family_pipeline_ids)
.where.not(sha: project.commit(pipeline.ref).try(:id))
.alive_or_scheduled
.with_only_interruptible_builds
else
project.ci_pipelines
.where(ref: pipeline.ref)
- .where.not(id: pipeline.id)
+ .where.not(id: pipeline.same_family_pipeline_ids)
.where.not(sha: project.commit(pipeline.ref).try(:id))
.created_or_pending
end
diff --git a/app/services/suggestions/apply_service.rb b/app/services/suggestions/apply_service.rb
index 8ba50e22b09..a6485e42bdb 100644
--- a/app/services/suggestions/apply_service.rb
+++ b/app/services/suggestions/apply_service.rb
@@ -2,6 +2,24 @@
module Suggestions
class ApplyService < ::BaseService
+ DEFAULT_SUGGESTION_COMMIT_MESSAGE = 'Apply suggestion to %{file_path}'
+
+ PLACEHOLDERS = {
+ 'project_path' => ->(suggestion, user) { suggestion.project.path },
+ 'project_name' => ->(suggestion, user) { suggestion.project.name },
+ 'file_path' => ->(suggestion, user) { suggestion.file_path },
+ 'branch_name' => ->(suggestion, user) { suggestion.branch },
+ 'username' => ->(suggestion, user) { user.username },
+ 'user_full_name' => ->(suggestion, user) { user.name }
+ }.freeze
+
+ # This regex is built dynamically using the keys from the PLACEHOLDER struct.
+ # So, we can easily add new placeholder just by modifying the PLACEHOLDER hash.
+ # This regex will build the new PLACEHOLDER_REGEX with the new information
+ PLACEHOLDERS_REGEX = Regexp.union(PLACEHOLDERS.keys.map { |key| Regexp.new(Regexp.escape(key)) }).freeze
+
+ attr_reader :current_user
+
def initialize(current_user)
@current_user = current_user
end
@@ -22,7 +40,7 @@ module Suggestions
end
params = file_update_params(suggestion, diff_file)
- result = ::Files::UpdateService.new(suggestion.project, @current_user, params).execute
+ result = ::Files::UpdateService.new(suggestion.project, current_user, params).execute
if result[:status] == :success
suggestion.update(commit_id: result[:result], applied: true)
@@ -46,13 +64,14 @@ module Suggestions
def file_update_params(suggestion, diff_file)
blob = diff_file.new_blob
+ project = suggestion.project
file_path = suggestion.file_path
branch_name = suggestion.branch
file_content = new_file_content(suggestion, blob)
- commit_message = "Apply suggestion to #{file_path}"
+ commit_message = processed_suggestion_commit_message(suggestion)
file_last_commit =
- Gitlab::Git::Commit.last_for_path(suggestion.project.repository,
+ Gitlab::Git::Commit.last_for_path(project.repository,
blob.commit_id,
blob.path)
@@ -75,5 +94,17 @@ module Suggestions
content.join
end
+
+ def suggestion_commit_message(project)
+ project.suggestion_commit_message || DEFAULT_SUGGESTION_COMMIT_MESSAGE
+ end
+
+ def processed_suggestion_commit_message(suggestion)
+ message = suggestion_commit_message(suggestion.project)
+
+ Gitlab::StringPlaceholderReplacer.replace_string_placeholders(message, PLACEHOLDERS_REGEX) do |key|
+ PLACEHOLDERS[key].call(suggestion, current_user)
+ end
+ end
end
end
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index f49cdfbf8da..08675c16124 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -8,7 +8,7 @@
= f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
%div
= f.label 'Two-Factor Authentication code', name: :otp_attempt
- = f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.'
+ = f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.', inputmode: 'numeric', pattern: '[0-9]*'
%p.form-text.text-muted.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
.prepend-top-20
= f.submit "Verify code", class: "btn btn-success"
diff --git a/app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml b/app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml
index 5d163d03c73..e3142ff96a1 100644
--- a/app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml
+++ b/app/views/devise/shared/_experimental_separate_sign_up_flow_box.html.haml
@@ -5,7 +5,8 @@
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f|
.devise-errors.mt-0
= render "devise/shared/error_messages", resource: resource
- = invisible_captcha
+ - if Feature.enabled?(:invisible_captcha)
+ = invisible_captcha
.username.form-group
= f.label :username, class: 'label-bold'
= f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
@@ -27,5 +28,8 @@
- accept_terms_label = _("I accept the %{terms_link}") % { terms_link: terms_link }
= accept_terms_label.html_safe
= render_if_exists 'devise/shared/email_opted_in', f: f
+ %div
+ - if show_recaptcha_sign_up?
+ = recaptcha_tags
.submit-container.mt-3
= f.submit _("Register"), class: "btn-register btn btn-block btn-success mb-0 p-2", data: { qa_selector: 'new_user_register_button' }
diff --git a/app/views/projects/_merge_request_merge_suggestions_settings.html.haml b/app/views/projects/_merge_request_merge_suggestions_settings.html.haml
new file mode 100644
index 00000000000..06bb9056e61
--- /dev/null
+++ b/app/views/projects/_merge_request_merge_suggestions_settings.html.haml
@@ -0,0 +1,17 @@
+- form = local_assigns.fetch(:form)
+
+.form-group
+ %b= s_('ProjectSettings|Merge suggestions')
+ %p.text-secondary
+ = s_('ProjectSettings|The commit message used to apply merge request suggestions')
+ = link_to icon('question-circle'),
+ help_page_path('user/discussions/index.md',
+ anchor: 'configure-the-commit-message-for-applied-suggestions'),
+ target: '_blank'
+ .mb-2
+ = form.text_field :suggestion_commit_message, class: 'form-control mb-2', placeholder: Suggestions::ApplyService::DEFAULT_SUGGESTION_COMMIT_MESSAGE
+ %p.form-text.text-muted
+ = s_('ProjectSettings|The variables GitLab supports:')
+ - Suggestions::ApplyService::PLACEHOLDERS.keys.each do |placeholder|
+ %code
+ = "%{#{placeholder}}".html_safe
diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml
index f2ba38387a3..dc3a3fcc647 100644
--- a/app/views/projects/_merge_request_settings.html.haml
+++ b/app/views/projects/_merge_request_settings.html.haml
@@ -5,3 +5,5 @@
= render 'projects/merge_request_merge_options_settings', project: @project, form: form
= render 'projects/merge_request_merge_checks_settings', project: @project, form: form
+
+= render 'projects/merge_request_merge_suggestions_settings', project: @project, form: form
diff --git a/app/views/projects/_merge_request_settings_description_text.html.haml b/app/views/projects/_merge_request_settings_description_text.html.haml
index 42964c900b3..dc9dc92675d 100644
--- a/app/views/projects/_merge_request_settings_description_text.html.haml
+++ b/app/views/projects/_merge_request_settings_description_text.html.haml
@@ -1 +1 @@
-%p= s_('ProjectSettings|Choose your merge method, merge options, and merge checks.')
+%p= s_('ProjectSettings|Choose your merge method, merge options, merge checks, and merge suggestions.')