summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-04 21:07:54 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-04 21:07:54 +0000
commit2fd92f2dc784ade9cb4e1c33dd60cbfad7b86818 (patch)
tree7779f36689db97a46e0268a4aec1d49f283eb0c8 /app
parent42ca24aa5bbab7a2d43bc866d9bee9876941cea2 (diff)
downloadgitlab-ce-2fd92f2dc784ade9cb4e1c33dd60cbfad7b86818.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/error_tracking/components/error_details.vue25
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue6
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss14
-rw-r--r--app/controllers/concerns/uploads_actions.rb1
-rw-r--r--app/controllers/groups/group_links_controller.rb2
-rw-r--r--app/graphql/types/diff_refs_type.rb2
-rw-r--r--app/models/application_setting.rb30
-rw-r--r--app/models/badge.rb4
-rw-r--r--app/models/group.rb11
-rw-r--r--app/models/members/group_member.rb1
-rw-r--r--app/models/user_detail.rb2
-rw-r--r--app/presenters/ci/pipeline_presenter.rb6
-rw-r--r--app/services/auth/container_registry_authentication_service.rb18
-rw-r--r--app/services/groups/group_links/destroy_service.rb14
-rw-r--r--app/services/groups/group_links/update_service.rb29
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_service.rb19
-rw-r--r--app/services/projects/lfs_pointers/lfs_object_download_list_service.rb12
-rw-r--r--app/services/web_hook_service.rb8
-rw-r--r--app/uploaders/file_uploader.rb7
-rw-r--r--app/validators/addressable_url_validator.rb8
-rw-r--r--app/views/admin/application_settings/_grafana.html.haml2
-rw-r--r--app/views/clusters/clusters/_form.html.haml6
-rw-r--r--app/views/clusters/clusters/user/_form.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml2
-rw-r--r--app/views/shared/issuable/form/_branch_chooser.html.haml4
25 files changed, 184 insertions, 51 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue
index 093d993c3ad..43fa97e4095 100644
--- a/app/assets/javascripts/error_tracking/components/error_details.vue
+++ b/app/assets/javascripts/error_tracking/components/error_details.vue
@@ -108,16 +108,6 @@ export default {
'errorStatus',
]),
...mapGetters('details', ['stacktrace']),
- reported() {
- return sprintf(
- __('Reported %{timeAgo} by %{reportedBy}'),
- {
- reportedBy: `<strong class="error-details-meta-culprit">${this.error.culprit}</strong>`,
- timeAgo: this.timeFormatted(this.stacktraceData.date_received),
- },
- false,
- );
- },
firstReleaseLink() {
return `${this.error.externalBaseUrl}/releases/${this.error.firstReleaseShortVersion}`;
},
@@ -227,8 +217,19 @@ export default {
</gl-alert>
<div class="error-details-header d-flex py-2 justify-content-between">
- <div class="error-details-meta my-auto">
- <span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
+ <div
+ v-if="!loadingStacktrace && stacktrace"
+ class="error-details-meta my-auto"
+ data-qa-selector="reported_text"
+ >
+ <gl-sprintf :message="__('Reported %{timeAgo} by %{reportedBy}')">
+ <template #reportedBy>
+ <strong class="error-details-meta-culprit">{{ error.culprit }}</strong>
+ </template>
+ <template #timeAgo>
+ {{ timeFormatted(stacktraceData.date_received) }}
+ </template>
+ </gl-sprintf>
</div>
<div class="error-details-actions">
<div class="d-inline-flex bv-d-sm-down-none">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 57be97855e3..b1fb377e47a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -1,5 +1,6 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
+import { escape } from 'lodash';
import simplePoll from '../../../lib/utils/simple_poll';
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon.vue';
@@ -44,11 +45,10 @@ export default {
fastForwardMergeText() {
return sprintf(
__(
- `Fast-forward merge is not possible. Rebase the source branch onto %{startTag}${this.mr.targetBranch}%{endTag} to allow this merge request to be merged.`,
+ 'Fast-forward merge is not possible. Rebase the source branch onto %{targetBranch} to allow this merge request to be merged.',
),
{
- startTag: '<span class="label-branch">',
- endTag: '</span>',
+ targetBranch: `<span class="label-branch">${escape(this.mr.targetBranch)}</span>`,
},
false,
);
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 82bef91230e..8b0dd73c565 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -1090,6 +1090,20 @@ button.mini-pipeline-graph-dropdown-toggle {
}
}
+.codequality-report {
+ .media {
+ padding: $gl-padding;
+ }
+
+ .media-body {
+ flex-direction: row;
+ }
+
+ .report-block-container {
+ height: auto !important;
+ }
+}
+
.progress-bar.bg-primary {
background-color: $blue-500 !important;
}
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index 655575e0944..549a443b1a8 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -9,6 +9,7 @@ module UploadsActions
included do
prepend_before_action :set_request_format_from_path_extension
+ rescue_from FileUploader::InvalidSecret, with: :render_404
end
def create
diff --git a/app/controllers/groups/group_links_controller.rb b/app/controllers/groups/group_links_controller.rb
index d3360acd245..23daa29ac43 100644
--- a/app/controllers/groups/group_links_controller.rb
+++ b/app/controllers/groups/group_links_controller.rb
@@ -24,7 +24,7 @@ class Groups::GroupLinksController < Groups::ApplicationController
end
def update
- @group_link.update(group_link_params)
+ Groups::GroupLinks::UpdateService.new(@group_link).execute(group_link_params)
end
def destroy
diff --git a/app/graphql/types/diff_refs_type.rb b/app/graphql/types/diff_refs_type.rb
index 03d080d784b..4049a204f66 100644
--- a/app/graphql/types/diff_refs_type.rb
+++ b/app/graphql/types/diff_refs_type.rb
@@ -8,7 +8,7 @@ module Types
field :head_sha, GraphQL::STRING_TYPE, null: false,
description: 'SHA of the HEAD at the time the comment was made'
- field :base_sha, GraphQL::STRING_TYPE, null: false,
+ field :base_sha, GraphQL::STRING_TYPE, null: true,
description: 'Merge base of the branch the comment was made on'
field :start_sha, GraphQL::STRING_TYPE, null: false,
description: 'SHA of the branch being compared against'
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index f9d7e8a2b7a..481e1807a78 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -6,6 +6,9 @@ class ApplicationSetting < ApplicationRecord
include TokenAuthenticatable
include ChronicDurationAttribute
+ GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
+ 'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
+
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
add_authentication_token_field :health_check_access_token
add_authentication_token_field :static_objects_external_storage_auth_token
@@ -38,6 +41,14 @@ class ApplicationSetting < ApplicationRecord
chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds
+ validates :grafana_url,
+ system_hook_url: {
+ blocked_message: "is blocked: %{exception_message}. " + GRAFANA_URL_ERROR_MESSAGE
+ },
+ if: :grafana_url_absolute?
+
+ validate :validate_grafana_url
+
validates :uuid, presence: true
validates :outbound_local_requests_whitelist,
@@ -362,6 +373,19 @@ class ApplicationSetting < ApplicationRecord
end
after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') }
+ def validate_grafana_url
+ unless parsed_grafana_url
+ self.errors.add(
+ :grafana_url,
+ "must be a valid relative or absolute URL. #{GRAFANA_URL_ERROR_MESSAGE}"
+ )
+ end
+ end
+
+ def grafana_url_absolute?
+ parsed_grafana_url&.absolute?
+ end
+
def sourcegraph_url_is_com?
!!(sourcegraph_url =~ /\Ahttps:\/\/(www\.)?sourcegraph\.com/)
end
@@ -394,6 +418,12 @@ class ApplicationSetting < ApplicationRecord
rescue RegexpError
errors.add(:email_restrictions, _('is not a valid regular expression'))
end
+
+ private
+
+ def parsed_grafana_url
+ @parsed_grafana_url ||= Gitlab::Utils.parse_url(grafana_url)
+ end
end
ApplicationSetting.prepend_if_ee('EE::ApplicationSetting')
diff --git a/app/models/badge.rb b/app/models/badge.rb
index eb351425e66..3400d6d407d 100644
--- a/app/models/badge.rb
+++ b/app/models/badge.rb
@@ -32,7 +32,9 @@ class Badge < ApplicationRecord
end
def rendered_image_url(project = nil)
- build_rendered_url(image_url, project)
+ Gitlab::AssetProxy.proxy_url(
+ build_rendered_url(image_url, project)
+ )
end
private
diff --git a/app/models/group.rb b/app/models/group.rb
index d6a4af5af15..e9b3e3c3369 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -516,18 +516,29 @@ class Group < Namespace
group_group_links_query = GroupGroupLink.where(shared_group_id: self_and_ancestors_ids)
cte = Gitlab::SQL::CTE.new(:group_group_links_cte, group_group_links_query)
+ cte_alias = cte.table.alias(GroupGroupLink.table_name)
link = GroupGroupLink
.with(cte.to_arel)
+ .select(smallest_value_arel([cte_alias[:group_access], group_member_table[:access_level]],
+ 'group_access'))
.from([group_member_table, cte.alias_to(group_group_link_table)])
.where(group_member_table[:user_id].eq(user.id))
+ .where(group_member_table[:requested_at].eq(nil))
.where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id]))
+ .where(group_member_table[:source_type].eq('Namespace'))
.reorder(Arel::Nodes::Descending.new(group_group_link_table[:group_access]))
.first
link&.group_access
end
+ def smallest_value_arel(args, column_alias)
+ Arel::Nodes::As.new(
+ Arel::Nodes::NamedFunction.new('LEAST', args),
+ Arel::Nodes::SqlLiteral.new(column_alias))
+ end
+
def self.groups_including_descendants_by(group_ids)
Gitlab::ObjectHierarchy
.new(Group.where(id: group_ids))
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index bdff9e28df1..bc3be67bd32 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -66,6 +66,7 @@ class GroupMember < Member
def after_accept_invite
notification_service.accept_group_invite(self)
+ update_two_factor_requirement
super
end
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
index 1621f336111..5dc74421705 100644
--- a/app/models/user_detail.rb
+++ b/app/models/user_detail.rb
@@ -3,5 +3,5 @@
class UserDetail < ApplicationRecord
belongs_to :user
- validates :job_title, presence: true, length: { maximum: 200 }
+ validates :job_title, length: { maximum: 200 }
end
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index 37abefb5664..ce9a3346b4b 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -134,7 +134,11 @@ module Ci
def all_related_merge_requests
strong_memoize(:all_related_merge_requests) do
- pipeline.ref ? pipeline.all_merge_requests_by_recency.to_a : []
+ if pipeline.ref && can?(current_user, :read_merge_request, pipeline.project)
+ pipeline.all_merge_requests_by_recency.to_a
+ else
+ []
+ end
end
end
end
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 09a84950755..629c1cbdc5c 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -3,12 +3,24 @@
module Auth
class ContainerRegistryAuthenticationService < BaseService
AUDIENCE = 'container_registry'
+ REGISTRY_LOGIN_ABILITIES = [
+ :read_container_image,
+ :create_container_image,
+ :destroy_container_image,
+ :update_container_image,
+ :admin_container_image,
+ :build_read_container_image,
+ :build_create_container_image,
+ :build_destroy_container_image
+ ].freeze
def execute(authentication_abilities:)
@authentication_abilities = authentication_abilities
return error('UNAVAILABLE', status: 404, message: 'registry not enabled') unless registry.enabled
+ return error('DENIED', status: 403, message: 'access forbidden') unless has_registry_ability?
+
unless scopes.any? || current_user || project
return error('DENIED', status: 403, message: 'access forbidden')
end
@@ -197,5 +209,11 @@ module Auth
def has_authentication_ability?(capability)
@authentication_abilities.to_a.include?(capability)
end
+
+ def has_registry_ability?
+ @authentication_abilities.any? do |ability|
+ REGISTRY_LOGIN_ABILITIES.include?(ability)
+ end
+ end
end
end
diff --git a/app/services/groups/group_links/destroy_service.rb b/app/services/groups/group_links/destroy_service.rb
index 29aa8de4e68..6835b6c4637 100644
--- a/app/services/groups/group_links/destroy_service.rb
+++ b/app/services/groups/group_links/destroy_service.rb
@@ -6,19 +6,17 @@ module Groups
def execute(one_or_more_links)
links = Array(one_or_more_links)
- GroupGroupLink.transaction do
- GroupGroupLink.delete(links)
+ if GroupGroupLink.delete(links)
+ Gitlab::AppLogger.info(
+ "GroupGroupLinks with ids: #{links.map(&:id)} have been deleted.")
groups_to_refresh = links.map(&:shared_with_group)
groups_to_refresh.uniq.each do |group|
group.refresh_members_authorized_projects
end
-
- Gitlab::AppLogger.info("GroupGroupLinks with ids: #{links.map(&:id)} have been deleted.")
- rescue => ex
- Gitlab::AppLogger.error(ex)
-
- raise
+ else
+ Gitlab::AppLogger.info(
+ "Failed to delete GroupGroupLinks with ids: #{links.map(&:id)}.")
end
end
end
diff --git a/app/services/groups/group_links/update_service.rb b/app/services/groups/group_links/update_service.rb
new file mode 100644
index 00000000000..71b52cb616c
--- /dev/null
+++ b/app/services/groups/group_links/update_service.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Groups
+ module GroupLinks
+ class UpdateService < BaseService
+ def initialize(group_link, user = nil)
+ super(group_link.shared_group, user)
+
+ @group_link = group_link
+ end
+
+ def execute(group_link_params)
+ group_link.update!(group_link_params)
+
+ if requires_authorization_refresh?(group_link_params)
+ group_link.shared_with_group.refresh_members_authorized_projects
+ end
+ end
+
+ private
+
+ attr_accessor :group_link
+
+ def requires_authorization_refresh?(params)
+ params.include?(:group_access)
+ end
+ end
+ end
+end
diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb
index bd70012c76c..52c73bcff03 100644
--- a/app/services/projects/lfs_pointers/lfs_download_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_service.rb
@@ -16,17 +16,14 @@ module Projects
@lfs_download_object = lfs_download_object
end
- # rubocop: disable CodeReuse/ActiveRecord
def execute
return unless project&.lfs_enabled? && lfs_download_object
return error("LFS file with oid #{lfs_oid} has invalid attributes") unless lfs_download_object.valid?
- return if LfsObject.exists?(oid: lfs_oid)
wrap_download_errors do
download_lfs_file!
end
end
- # rubocop: enable CodeReuse/ActiveRecord
private
@@ -39,14 +36,24 @@ module Projects
def download_lfs_file!
with_tmp_file do |tmp_file|
download_and_save_file!(tmp_file)
- project.lfs_objects << LfsObject.new(oid: lfs_oid,
- size: lfs_size,
- file: tmp_file)
+
+ project.lfs_objects << find_or_create_lfs_object(tmp_file)
success
end
end
+ def find_or_create_lfs_object(tmp_file)
+ lfs_obj = LfsObject.safe_find_or_create_by!(
+ oid: lfs_oid,
+ size: lfs_size
+ )
+
+ lfs_obj.update!(file: tmp_file) unless lfs_obj.file.file
+
+ lfs_obj
+ end
+
def download_and_save_file!(file)
digester = Digest::SHA256.new
response = Gitlab::HTTP.get(lfs_sanitized_url, download_headers) do |fragment|
diff --git a/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb b/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb
index d6e6480bdad..75106297043 100644
--- a/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb
@@ -26,12 +26,12 @@ module Projects
return []
end
- # Getting all Lfs pointers already in the database and linking them to the project
- linked_oids = LfsLinkService.new(project).execute(lfs_pointers_in_repository.keys)
- # Retrieving those oids not present in the database which we need to download
- missing_oids = lfs_pointers_in_repository.except(*linked_oids)
- # Downloading the required information and gathering it inside a LfsDownloadObject for each oid
- LfsDownloadLinkListService.new(project, remote_uri: current_endpoint_uri).execute(missing_oids)
+ # Downloading the required information and gathering it inside an
+ # LfsDownloadObject for each oid
+ #
+ LfsDownloadLinkListService
+ .new(project, remote_uri: current_endpoint_uri)
+ .execute(lfs_pointers_in_repository)
rescue LfsDownloadLinkListService::DownloadLinksError => e
raise LfsObjectDownloadListError, "The LFS objects download list couldn't be imported. Error: #{e.message}"
end
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 514ba998d2c..178a321e20c 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -13,8 +13,14 @@ class WebHookService
end
end
+ GITLAB_EVENT_HEADER = 'X-Gitlab-Event'
+
attr_accessor :hook, :data, :hook_name, :request_options
+ def self.hook_to_event(hook_name)
+ hook_name.to_s.singularize.titleize
+ end
+
def initialize(hook, data, hook_name)
@hook = hook
@data = data
@@ -112,7 +118,7 @@ class WebHookService
@headers ||= begin
{
'Content-Type' => 'application/json',
- 'X-Gitlab-Event' => hook_name.singularize.titleize
+ GITLAB_EVENT_HEADER => self.class.hook_to_event(hook_name)
}.tap do |hash|
hash['X-Gitlab-Token'] = Gitlab::Utils.remove_line_breaks(hook.token) if hook.token.present?
end
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 0fc71d2e3f3..505b51c2006 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -16,6 +16,9 @@ class FileUploader < GitlabUploader
MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}.freeze
DYNAMIC_PATH_PATTERN = %r{.*(?<secret>\h{32})/(?<identifier>.*)}.freeze
+ VALID_SECRET_PATTERN = %r{\A\h{10,32}\z}.freeze
+
+ InvalidSecret = Class.new(StandardError)
after :remove, :prune_store_dir
@@ -153,6 +156,10 @@ class FileUploader < GitlabUploader
def secret
@secret ||= self.class.generate_secret
+
+ raise InvalidSecret unless @secret =~ VALID_SECRET_PATTERN
+
+ @secret
end
# return a new uploader with a file copy on another project
diff --git a/app/validators/addressable_url_validator.rb b/app/validators/addressable_url_validator.rb
index 300bd01ed22..99f503c3f06 100644
--- a/app/validators/addressable_url_validator.rb
+++ b/app/validators/addressable_url_validator.rb
@@ -23,7 +23,8 @@
# protect against Server-side Request Forgery (SSRF), or check for the right port.
#
# Configuration options:
-# * <tt>message</tt> - A custom error message (default is: "must be a valid URL").
+# * <tt>message</tt> - A custom error message, used when the URL is blank. (default is: "must be a valid URL").
+# * <tt>blocked_message</tt> - A custom error message, used when the URL is blocked. Default: +'is blocked: %{exception_message}'+.
# * <tt>schemes</tt> - Array of URI schemes. Default: +['http', 'https']+
# * <tt>allow_localhost</tt> - Allow urls pointing to +localhost+. Default: +true+
# * <tt>allow_local_network</tt> - Allow urls pointing to private network addresses. Default: +true+
@@ -59,7 +60,8 @@ class AddressableUrlValidator < ActiveModel::EachValidator
}.freeze
DEFAULT_OPTIONS = BLOCKER_VALIDATE_OPTIONS.merge({
- message: 'must be a valid URL'
+ message: 'must be a valid URL',
+ blocked_message: 'is blocked: %{exception_message}'
}).freeze
def initialize(options)
@@ -80,7 +82,7 @@ class AddressableUrlValidator < ActiveModel::EachValidator
Gitlab::UrlBlocker.validate!(value, blocker_args)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
- record.errors.add(attribute, "is blocked: #{e.message}")
+ record.errors.add(attribute, options.fetch(:blocked_message) % { exception_message: e.message })
end
private
diff --git a/app/views/admin/application_settings/_grafana.html.haml b/app/views/admin/application_settings/_grafana.html.haml
index b6e02bde895..700be7db54f 100644
--- a/app/views/admin/application_settings/_grafana.html.haml
+++ b/app/views/admin/application_settings/_grafana.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-grafana-settings'), html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-grafana-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/clusters/clusters/_form.html.haml b/app/views/clusters/clusters/_form.html.haml
index a85b005b2b4..40fc7d110fc 100644
--- a/app/views/clusters/clusters/_form.html.haml
+++ b/app/views/clusters/clusters/_form.html.haml
@@ -3,7 +3,7 @@
.form-group
%h5= s_('ClusterIntegration|Integration status')
%label.append-bottom-0.js-cluster-enable-toggle-area
- = render "shared/buttons/project_feature_toggle", is_checked: @cluster.enabled?, label: s_("ClusterIntegration|Toggle Kubernetes cluster"), disabled: !can?(current_user, :update_cluster, @cluster) do
+ = render "shared/buttons/project_feature_toggle", is_checked: @cluster.enabled?, label: s_("ClusterIntegration|Toggle Kubernetes cluster"), disabled: !can?(current_user, :update_cluster, @cluster), data: { qa_selector: 'integration_status_toggle' } do
= field.hidden_field :enabled, { class: 'js-project-feature-toggle-input'}
.form-text.text-muted= s_('ClusterIntegration|Enable or disable GitLab\'s connection to your Kubernetes cluster.')
@@ -22,7 +22,7 @@
.form-group
%h5= s_('ClusterIntegration|Base domain')
- = field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus qa-base-domain'
+ = field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus', data: { qa_selector: 'base_domain_field' }
.form-text.text-muted
- auto_devops_url = help_page_path('topics/autodevops/index')
- auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }
@@ -37,4 +37,4 @@
- if can?(current_user, :update_cluster, @cluster)
.form-group
- = field.submit _('Save changes'), class: 'btn btn-success qa-save-domain'
+ = field.submit _('Save changes'), class: 'btn btn-success', data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml
index 39b6d74d9f9..ce226d29113 100644
--- a/app/views/clusters/clusters/user/_form.html.haml
+++ b/app/views/clusters/clusters/user/_form.html.haml
@@ -54,4 +54,4 @@
= render('clusters/clusters/namespace', platform_field: platform_kubernetes_field)
.form-group
- = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
+ = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success', data: { qa_selector: 'add_kubernetes_cluster_button' }
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 9f70124ba0d..78cd3f62dec 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -83,7 +83,7 @@
= _('Requests Profiles')
- if Gitlab::CurrentSettings.current_application_settings.grafana_enabled?
= nav_link do
- = link_to Gitlab::CurrentSettings.current_application_settings.grafana_url, target: '_blank', title: _('Metrics Dashboard') do
+ = link_to Gitlab::CurrentSettings.current_application_settings.grafana_url, target: '_blank', title: _('Metrics Dashboard'), rel: 'noopener noreferrer' do
%span
= _('Metrics Dashboard')
= render_if_exists 'layouts/nav/ee/admin/new_monitoring_sidebar'
diff --git a/app/views/shared/issuable/form/_branch_chooser.html.haml b/app/views/shared/issuable/form/_branch_chooser.html.haml
index 29ac17c43b9..8d9e5ddf065 100644
--- a/app/views/shared/issuable/form/_branch_chooser.html.haml
+++ b/app/views/shared/issuable/form/_branch_chooser.html.haml
@@ -8,7 +8,9 @@
.form-group.row.d-flex.gl-pl-3.gl-pr-3.branch-selector
.align-self-center
- %span= s_('From %{source_title} into').html_safe % { source_title: "<code>#{source_title}</code>".html_safe }
+ %span
+ = _('From <code>%{source_title}</code> into').html_safe % { source_title: source_title }
+
- if issuable.new_record?
%code= target_title
&nbsp;