Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3a5bee7879
commit
eac94e5cd6
|
@ -35,6 +35,11 @@ export default {
|
||||||
retryButtonCategory() {
|
retryButtonCategory() {
|
||||||
return this.job.status && this.job.recoverable ? 'primary' : 'secondary';
|
return this.job.status && this.job.recoverable ? 'primary' : 'secondary';
|
||||||
},
|
},
|
||||||
|
buttonTitle() {
|
||||||
|
return this.job.status && this.job.status.text === 'passed'
|
||||||
|
? this.$options.i18n.runAgainJobButtonLabel
|
||||||
|
: this.$options.i18n.retryJobButtonLabel;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['toggleSidebar']),
|
...mapActions(['toggleSidebar']),
|
||||||
|
@ -66,8 +71,8 @@ export default {
|
||||||
<job-sidebar-retry-button
|
<job-sidebar-retry-button
|
||||||
v-if="job.retry_path"
|
v-if="job.retry_path"
|
||||||
v-gl-tooltip.left
|
v-gl-tooltip.left
|
||||||
:title="$options.i18n.retryJobButtonLabel"
|
:title="buttonTitle"
|
||||||
:aria-label="$options.i18n.retryJobButtonLabel"
|
:aria-label="buttonTitle"
|
||||||
:category="retryButtonCategory"
|
:category="retryButtonCategory"
|
||||||
:href="job.retry_path"
|
:href="job.retry_path"
|
||||||
:modal-id="$options.forwardDeploymentFailureModalId"
|
:modal-id="$options.forwardDeploymentFailureModalId"
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const JOB_SIDEBAR_COPY = {
|
||||||
retry: __('Retry'),
|
retry: __('Retry'),
|
||||||
retryJobButtonLabel: s__('Job|Retry'),
|
retryJobButtonLabel: s__('Job|Retry'),
|
||||||
toggleSidebar: __('Toggle Sidebar'),
|
toggleSidebar: __('Toggle Sidebar'),
|
||||||
|
runAgainJobButtonLabel: s__('Job|Run again'),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const JOB_RETRY_FORWARD_DEPLOYMENT_MODAL = {
|
export const JOB_RETRY_FORWARD_DEPLOYMENT_MODAL = {
|
||||||
|
|
|
@ -49,17 +49,22 @@ export default {
|
||||||
:class="{ 'gl-border-t gl-py-3 gl-pl-7': level === 2 }"
|
:class="{ 'gl-border-t gl-py-3 gl-pl-7': level === 2 }"
|
||||||
>
|
>
|
||||||
<status-icon v-if="statusIconName" :level="2" :name="widgetName" :icon-name="statusIconName" />
|
<status-icon v-if="statusIconName" :level="2" :name="widgetName" :icon-name="statusIconName" />
|
||||||
<div>
|
<div class="gl-w-full">
|
||||||
<slot name="header">
|
<div class="gl-display-flex">
|
||||||
<div v-if="header" class="gl-mb-2">
|
<slot name="header">
|
||||||
<strong v-safe-html="generatedHeader" class="gl-display-block"></strong
|
<div v-if="header" class="gl-mb-2">
|
||||||
><span
|
<strong v-safe-html="generatedHeader" class="gl-display-block"></strong
|
||||||
v-if="generatedSubheader"
|
><span
|
||||||
v-safe-html="generatedSubheader"
|
v-if="generatedSubheader"
|
||||||
class="gl-display-block"
|
v-safe-html="generatedSubheader"
|
||||||
></span>
|
class="gl-display-block"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
<div v-if="$scopedSlots['header-actions']" class="gl-ml-auto">
|
||||||
|
<slot name="header-actions"></slot>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</div>
|
||||||
<div class="gl-display-flex gl-align-items-baseline gl-w-full">
|
<div class="gl-display-flex gl-align-items-baseline gl-w-full">
|
||||||
<slot name="body"></slot>
|
<slot name="body"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
|
||||||
diff_view: diff_view,
|
diff_view: diff_view,
|
||||||
merge_ref_head_diff: render_merge_ref_head_diff?,
|
merge_ref_head_diff: render_merge_ref_head_diff?,
|
||||||
pagination_data: diffs.pagination_data,
|
pagination_data: diffs.pagination_data,
|
||||||
allow_tree_conflicts: display_merge_conflicts_in_diff?
|
merge_conflicts_in_diff: display_merge_conflicts_in_diff?
|
||||||
}
|
}
|
||||||
|
|
||||||
# NOTE: Any variables that would affect the resulting json needs to be added to the cache_context to avoid stale cache issues.
|
# NOTE: Any variables that would affect the resulting json needs to be added to the cache_context to avoid stale cache issues.
|
||||||
|
@ -57,7 +57,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
|
||||||
params[:page],
|
params[:page],
|
||||||
params[:per_page],
|
params[:per_page],
|
||||||
options[:merge_ref_head_diff],
|
options[:merge_ref_head_diff],
|
||||||
options[:allow_tree_conflicts]
|
options[:merge_conflicts_in_diff]
|
||||||
]
|
]
|
||||||
|
|
||||||
if Feature.enabled?(:check_etags_diffs_batch_before_write_cache, merge_request.project) && !stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs])
|
if Feature.enabled?(:check_etags_diffs_batch_before_write_cache, merge_request.project) && !stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs])
|
||||||
|
@ -81,7 +81,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
|
||||||
options = additional_attributes.merge(
|
options = additional_attributes.merge(
|
||||||
only_context_commits: show_only_context_commits?,
|
only_context_commits: show_only_context_commits?,
|
||||||
merge_ref_head_diff: render_merge_ref_head_diff?,
|
merge_ref_head_diff: render_merge_ref_head_diff?,
|
||||||
allow_tree_conflicts: display_merge_conflicts_in_diff?
|
merge_conflicts_in_diff: display_merge_conflicts_in_diff?
|
||||||
)
|
)
|
||||||
|
|
||||||
render json: DiffsMetadataSerializer.new(project: @merge_request.project, current_user: current_user)
|
render json: DiffsMetadataSerializer.new(project: @merge_request.project, current_user: current_user)
|
||||||
|
@ -110,7 +110,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
|
||||||
options = additional_attributes.merge(
|
options = additional_attributes.merge(
|
||||||
diff_view: "inline",
|
diff_view: "inline",
|
||||||
merge_ref_head_diff: render_merge_ref_head_diff?,
|
merge_ref_head_diff: render_merge_ref_head_diff?,
|
||||||
allow_tree_conflicts: display_merge_conflicts_in_diff?
|
merge_conflicts_in_diff: display_merge_conflicts_in_diff?
|
||||||
)
|
)
|
||||||
|
|
||||||
options[:context_commits] = @merge_request.recent_context_commits
|
options[:context_commits] = @merge_request.recent_context_commits
|
||||||
|
|
|
@ -227,7 +227,6 @@ module DiffHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def conflicts(allow_tree_conflicts: false)
|
def conflicts(allow_tree_conflicts: false)
|
||||||
return unless options[:merge_ref_head_diff]
|
|
||||||
return unless merge_request.cannot_be_merged?
|
return unless merge_request.cannot_be_merged?
|
||||||
|
|
||||||
conflicts_service = MergeRequests::Conflicts::ListService.new(merge_request, allow_tree_conflicts: allow_tree_conflicts) # rubocop:disable CodeReuse/ServiceClass
|
conflicts_service = MergeRequests::Conflicts::ListService.new(merge_request, allow_tree_conflicts: allow_tree_conflicts) # rubocop:disable CodeReuse/ServiceClass
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Integrations
|
||||||
|
class BaseSlackNotification < BaseChatNotification
|
||||||
|
SUPPORTED_EVENTS_FOR_USAGE_LOG = %w[
|
||||||
|
push issue confidential_issue merge_request note confidential_note tag_push wiki_page deployment
|
||||||
|
].freeze
|
||||||
|
|
||||||
|
prop_accessor EVENT_CHANNEL['alert']
|
||||||
|
|
||||||
|
override :default_channel_placeholder
|
||||||
|
def default_channel_placeholder
|
||||||
|
_('#general, #development')
|
||||||
|
end
|
||||||
|
|
||||||
|
override :get_message
|
||||||
|
def get_message(object_kind, data)
|
||||||
|
return Integrations::ChatMessage::AlertMessage.new(data) if object_kind == 'alert'
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
override :supported_events
|
||||||
|
def supported_events
|
||||||
|
additional = ['alert']
|
||||||
|
|
||||||
|
super + additional
|
||||||
|
end
|
||||||
|
|
||||||
|
override :configurable_channels?
|
||||||
|
def configurable_channels?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
override :log_usage
|
||||||
|
def log_usage(event, user_id)
|
||||||
|
return unless user_id
|
||||||
|
|
||||||
|
return unless SUPPORTED_EVENTS_FOR_USAGE_LOG.include?(event)
|
||||||
|
|
||||||
|
key = "i_ecosystem_slack_service_#{event}_notification"
|
||||||
|
|
||||||
|
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(key, values: user_id)
|
||||||
|
|
||||||
|
return unless Feature.enabled?(:route_hll_to_snowplow_phase2)
|
||||||
|
|
||||||
|
optional_arguments = {
|
||||||
|
project: project,
|
||||||
|
namespace: group || project&.namespace
|
||||||
|
}.compact
|
||||||
|
|
||||||
|
Gitlab::Tracking.event(
|
||||||
|
self.class.name,
|
||||||
|
Integration::SNOWPLOW_EVENT_ACTION,
|
||||||
|
label: Integration::SNOWPLOW_EVENT_LABEL,
|
||||||
|
property: key,
|
||||||
|
user: User.find(user_id),
|
||||||
|
**optional_arguments
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,17 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Integrations
|
module Integrations
|
||||||
class Slack < BaseChatNotification
|
class Slack < BaseSlackNotification
|
||||||
include SlackMattermostNotifier
|
include SlackMattermostNotifier
|
||||||
|
|
||||||
SUPPORTED_EVENTS_FOR_USAGE_LOG = %w[
|
|
||||||
push issue confidential_issue merge_request note confidential_note
|
|
||||||
tag_push wiki_page deployment
|
|
||||||
].freeze
|
|
||||||
SNOWPLOW_EVENT_CATEGORY = self.name
|
|
||||||
|
|
||||||
prop_accessor EVENT_CHANNEL['alert']
|
|
||||||
|
|
||||||
def title
|
def title
|
||||||
'Slack notifications'
|
'Slack notifications'
|
||||||
end
|
end
|
||||||
|
@ -24,57 +16,9 @@ module Integrations
|
||||||
'slack'
|
'slack'
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_channel_placeholder
|
override :webhook_placeholder
|
||||||
_('#general, #development')
|
|
||||||
end
|
|
||||||
|
|
||||||
def webhook_placeholder
|
def webhook_placeholder
|
||||||
'https://hooks.slack.com/services/…'
|
'https://hooks.slack.com/services/…'
|
||||||
end
|
end
|
||||||
|
|
||||||
def supported_events
|
|
||||||
additional = []
|
|
||||||
additional << 'alert'
|
|
||||||
|
|
||||||
super + additional
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_message(object_kind, data)
|
|
||||||
return Integrations::ChatMessage::AlertMessage.new(data) if object_kind == 'alert'
|
|
||||||
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
override :log_usage
|
|
||||||
def log_usage(event, user_id)
|
|
||||||
return unless user_id
|
|
||||||
|
|
||||||
return unless SUPPORTED_EVENTS_FOR_USAGE_LOG.include?(event)
|
|
||||||
|
|
||||||
key = "i_ecosystem_slack_service_#{event}_notification"
|
|
||||||
|
|
||||||
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(key, values: user_id)
|
|
||||||
|
|
||||||
return unless Feature.enabled?(:route_hll_to_snowplow_phase2)
|
|
||||||
|
|
||||||
optional_arguments = {
|
|
||||||
project: project,
|
|
||||||
namespace: group || project&.namespace
|
|
||||||
}.compact
|
|
||||||
|
|
||||||
Gitlab::Tracking.event(
|
|
||||||
SNOWPLOW_EVENT_CATEGORY,
|
|
||||||
Integration::SNOWPLOW_EVENT_ACTION,
|
|
||||||
label: Integration::SNOWPLOW_EVENT_LABEL,
|
|
||||||
property: key,
|
|
||||||
user: User.find(user_id),
|
|
||||||
**optional_arguments
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
override :configurable_channels?
|
|
||||||
def configurable_channels?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1134,7 +1134,7 @@ class MergeRequest < ApplicationRecord
|
||||||
# rubocop: enable CodeReuse/ServiceClass
|
# rubocop: enable CodeReuse/ServiceClass
|
||||||
|
|
||||||
def diffable_merge_ref?
|
def diffable_merge_ref?
|
||||||
open? && merge_head_diff.present? && (Feature.enabled?(:display_merge_conflicts_in_diff, project) || can_be_merged?)
|
open? && merge_head_diff.present? && can_be_merged?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns boolean indicating the merge_status should be rechecked in order to
|
# Returns boolean indicating the merge_status should be rechecked in order to
|
||||||
|
|
|
@ -55,17 +55,9 @@ class DiffFileEntity < DiffFileBaseEntity
|
||||||
end
|
end
|
||||||
|
|
||||||
# Used for inline diffs
|
# Used for inline diffs
|
||||||
expose :highlighted_diff_lines, using: DiffLineEntity, if: -> (diff_file, options) { inline_diff_view?(options) && diff_file.text? } do |diff_file|
|
expose :diff_lines_for_serializer, as: :highlighted_diff_lines, using: DiffLineEntity, if: -> (diff_file, options) { inline_diff_view?(options) && diff_file.text? }
|
||||||
highlighted_diff_lines_for(diff_file, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
expose :is_fully_expanded do |diff_file|
|
expose :fully_expanded?, as: :is_fully_expanded
|
||||||
if conflict_file(options, diff_file)
|
|
||||||
false
|
|
||||||
else
|
|
||||||
diff_file.fully_expanded?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Used for parallel diffs
|
# Used for parallel diffs
|
||||||
expose :parallel_diff_lines, using: DiffLineParallelEntity, if: -> (diff_file, options) { parallel_diff_view?(options) && diff_file.text? }
|
expose :parallel_diff_lines, using: DiffLineParallelEntity, if: -> (diff_file, options) { parallel_diff_view?(options) && diff_file.text? }
|
||||||
|
@ -88,15 +80,6 @@ class DiffFileEntity < DiffFileBaseEntity
|
||||||
# If nothing is present, inline will be the default.
|
# If nothing is present, inline will be the default.
|
||||||
options.fetch(:diff_view, :inline).to_sym
|
options.fetch(:diff_view, :inline).to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
def highlighted_diff_lines_for(diff_file, options)
|
|
||||||
file = conflict_file(options, diff_file) || diff_file
|
|
||||||
|
|
||||||
file.diff_lines_for_serializer
|
|
||||||
rescue Gitlab::Git::Conflict::Parser::UnmergeableFile
|
|
||||||
# Fallback to diff_file as it means that conflict lines can't be parsed due to limit
|
|
||||||
diff_file.diff_lines_for_serializer
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
DiffFileEntity.prepend_mod
|
DiffFileEntity.prepend_mod
|
||||||
|
|
|
@ -74,7 +74,7 @@ class DiffsEntity < Grape::Entity
|
||||||
options.merge(
|
options.merge(
|
||||||
submodule_links: submodule_links,
|
submodule_links: submodule_links,
|
||||||
code_navigation_path: code_navigation_path(diffs),
|
code_navigation_path: code_navigation_path(diffs),
|
||||||
conflicts: conflicts(allow_tree_conflicts: options[:allow_tree_conflicts])
|
conflicts: (conflicts(allow_tree_conflicts: true) if options[:merge_conflicts_in_diff])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ class DiffsMetadataEntity < DiffsEntity
|
||||||
DiffFileMetadataEntity.represent(
|
DiffFileMetadataEntity.represent(
|
||||||
diffs.raw_diff_files(sorted: true),
|
diffs.raw_diff_files(sorted: true),
|
||||||
options.merge(
|
options.merge(
|
||||||
conflicts: conflicts(allow_tree_conflicts: options[:allow_tree_conflicts])
|
conflicts: (conflicts(allow_tree_conflicts: true) if options[:merge_conflicts_in_diff])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ class PaginatedDiffEntity < Grape::Entity
|
||||||
options.merge(
|
options.merge(
|
||||||
submodule_links: submodule_links,
|
submodule_links: submodule_links,
|
||||||
code_navigation_path: code_navigation_path(diffs),
|
code_navigation_path: code_navigation_path(diffs),
|
||||||
conflicts: conflicts(allow_tree_conflicts: options[:allow_tree_conflicts])
|
conflicts: (conflicts(allow_tree_conflicts: true) if options[:merge_conflicts_in_diff])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -156,8 +156,7 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_to_ref
|
def merge_to_ref
|
||||||
params = { allow_conflicts: Feature.enabled?(:display_merge_conflicts_in_diff, project) }
|
result = MergeRequests::MergeToRefService.new(project: project, current_user: merge_request.author, params: {}).execute(merge_request)
|
||||||
result = MergeRequests::MergeToRefService.new(project: project, current_user: merge_request.author, params: params).execute(merge_request)
|
|
||||||
|
|
||||||
result[:status] == :success
|
result[:status] == :success
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,19 +8,15 @@
|
||||||
- grouped_emojis = awardable.grouped_awards(with_thumbs: inline)
|
- grouped_emojis = awardable.grouped_awards(with_thumbs: inline)
|
||||||
.awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } }
|
.awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } }
|
||||||
- awards_sort(grouped_emojis).each do |emoji, awards|
|
- awards_sort(grouped_emojis).each do |emoji, awards|
|
||||||
%button.gl-button.btn.btn-default.award-control.js-emoji-btn.has-tooltip{ type: "button",
|
= render Pajamas::ButtonComponent.new(button_options: { class: "#{award_state_class(awardable, awards, current_user)} award-control js-emoji-btn", title: award_user_list(awards, current_user), data: { toggle: 'tooltip', container: 'body' } }) do
|
||||||
class: [award_state_class(awardable, awards, current_user)],
|
|
||||||
data: { title: award_user_list(awards, current_user) } }
|
|
||||||
= emoji_icon(emoji)
|
= emoji_icon(emoji)
|
||||||
%span.award-control-text.js-counter
|
%span.award-control-text.js-counter
|
||||||
= awards.count
|
= awards.count
|
||||||
|
|
||||||
- if can?(current_user, :award_emoji, awardable)
|
- if can?(current_user, :award_emoji, awardable)
|
||||||
.award-menu-holder.js-award-holder
|
.award-menu-holder.js-award-holder
|
||||||
%button.gl-button.btn.btn-default.award-control.has-tooltip.js-add-award{ type: 'button',
|
= render Pajamas::ButtonComponent.new(button_text_classes: 'gl-display-flex', button_options: { title: _('Add reaction'), class: 'js-add-award award-control has-tooltip', 'aria-label': _('Add reaction') }) do
|
||||||
'aria-label': _('Add reaction'),
|
= sprite_icon('slight-smile', css_class: 'award-control-icon-neutral gl-icon gl-mr-0!')
|
||||||
data: { title: _('Add reaction') } }
|
= sprite_icon('smiley', css_class: 'award-control-icon-positive gl-icon')
|
||||||
%span{ class: "award-control-icon award-control-icon-neutral gl-icon" }= sprite_icon('slight-smile')
|
= sprite_icon('smile', css_class: 'award-control-icon-super-positive gl-icon')
|
||||||
%span{ class: "award-control-icon award-control-icon-positive gl-icon" }= sprite_icon('smiley')
|
|
||||||
%span{ class: "award-control-icon award-control-icon-super-positive gl-icon" }= sprite_icon('smile')
|
|
||||||
= yield
|
= yield
|
||||||
|
|
|
@ -10,45 +10,32 @@
|
||||||
.import-buttons
|
.import-buttons
|
||||||
- if gitlab_project_import_enabled?
|
- if gitlab_project_import_enabled?
|
||||||
.import_gitlab_project.has-tooltip{ data: { container: 'body', qa_selector: 'gitlab_import_button' } }
|
.import_gitlab_project.has-tooltip{ data: { container: 'body', qa_selector: 'gitlab_import_button' } }
|
||||||
= link_to '#', class: 'gl-button btn-default btn btn_import_gitlab_project js-import-project-btn', data: { href: new_import_gitlab_project_path, platform: 'gitlab_export', **tracking_attrs_data(track_label, 'click_button', 'gitlab_export') } do
|
= render Pajamas::ButtonComponent.new(href: '#', icon: 'tanuki', button_options: { class: 'btn_import_gitlab_project js-import-project-btn', data: { href: new_import_gitlab_project_path, platform: 'gitlab_export', **tracking_attrs_data(track_label, 'click_button', 'gitlab_export') } }) do
|
||||||
.gl-button-icon
|
= _('GitLab export')
|
||||||
= sprite_icon('tanuki')
|
|
||||||
= _("GitLab export")
|
- if gitlab_import_enabled?
|
||||||
|
%div
|
||||||
|
= render Pajamas::ButtonComponent.new(href: status_import_gitlab_path(namespace_id: namespace_id), icon: 'tanuki', button_options: { class: "import_gitlab js-import-project-btn #{'js-how-to-import-link' unless gitlab_import_configured?}", data: { modal_title: _("Import projects from GitLab.com"), modal_message: import_from_gitlab_message, platform: 'gitlab_com', **tracking_attrs_data(track_label, 'click_button', 'gitlab_com') } }) do
|
||||||
|
GitLab.com
|
||||||
|
|
||||||
- if github_import_enabled?
|
- if github_import_enabled?
|
||||||
%div
|
%div
|
||||||
= link_to new_import_github_path(namespace_id: namespace_id), class: 'gl-button btn-default btn js-import-github js-import-project-btn', data: { platform: 'github', **tracking_attrs_data(track_label, 'click_button', 'github') } do
|
= render Pajamas::ButtonComponent.new(href: new_import_github_path(namespace_id: namespace_id), icon: 'github', button_options: { class: 'js-import-github js-import-project-btn', data: { platform: 'github', **tracking_attrs_data(track_label, 'click_button', 'github') } }) do
|
||||||
.gl-button-icon
|
|
||||||
= sprite_icon('github')
|
|
||||||
GitHub
|
GitHub
|
||||||
|
|
||||||
- if bitbucket_import_enabled?
|
- if bitbucket_import_enabled?
|
||||||
%div
|
%div
|
||||||
= link_to status_import_bitbucket_path(namespace_id: namespace_id), class: "gl-button btn-default btn import_bitbucket js-import-project-btn #{'js-how-to-import-link' unless bitbucket_import_configured?}",
|
= render Pajamas::ButtonComponent.new(href: status_import_bitbucket_path(namespace_id: namespace_id), icon: 'bitbucket', button_options: { class: "import_bitbucket js-import-project-btn #{'js-how-to-import-link' unless bitbucket_import_configured?}", data: { modal_title: _("Import projects from Bitbucket"), modal_message: import_from_bitbucket_message, platform: 'bitbucket_cloud', **tracking_attrs_data(track_label, 'click_button', 'bitbucket_cloud') } }) do
|
||||||
data: { modal_title: _("Import projects from Bitbucket"), modal_message: import_from_bitbucket_message, platform: 'bitbucket_cloud', **tracking_attrs_data(track_label, 'click_button', 'bitbucket_cloud') } do
|
|
||||||
.gl-button-icon
|
|
||||||
= sprite_icon('bitbucket')
|
|
||||||
Bitbucket Cloud
|
Bitbucket Cloud
|
||||||
|
|
||||||
- if bitbucket_server_import_enabled?
|
- if bitbucket_server_import_enabled?
|
||||||
%div
|
%div
|
||||||
= link_to status_import_bitbucket_server_path(namespace_id: namespace_id), class: "gl-button btn-default btn import_bitbucket js-import-project-btn", data: { platform: 'bitbucket_server', **tracking_attrs_data(track_label, 'click_button', 'bitbucket_server') } do
|
= render Pajamas::ButtonComponent.new(href: status_import_bitbucket_server_path(namespace_id: namespace_id), icon: 'bitbucket', button_options: { class: 'import_bitbucket js-import-project-btn', data: { platform: 'bitbucket_server', **tracking_attrs_data(track_label, 'click_button', 'bitbucket_server') } }) do
|
||||||
.gl-button-icon
|
|
||||||
= sprite_icon('bitbucket')
|
|
||||||
Bitbucket Server
|
Bitbucket Server
|
||||||
%div
|
|
||||||
- if gitlab_import_enabled?
|
|
||||||
%div
|
|
||||||
= link_to status_import_gitlab_path(namespace_id: namespace_id), class: "gl-button btn-default btn import_gitlab js-import-project-btn #{'js-how-to-import-link' unless gitlab_import_configured?}",
|
|
||||||
data: { modal_title: _("Import projects from GitLab.com"), modal_message: import_from_gitlab_message, platform: 'gitlab_com', **tracking_attrs_data(track_label, 'click_button', 'gitlab_com') } do
|
|
||||||
.gl-button-icon
|
|
||||||
= sprite_icon('tanuki')
|
|
||||||
= _("GitLab.com")
|
|
||||||
|
|
||||||
- if fogbugz_import_enabled?
|
- if fogbugz_import_enabled?
|
||||||
%div
|
%div
|
||||||
= link_to new_import_fogbugz_path(namespace_id: namespace_id), class: 'gl-button btn-default btn import_fogbugz js-import-project-btn', data: { platform: 'fogbugz', **tracking_attrs_data(track_label, 'click_button', 'fogbugz') } do
|
= render Pajamas::ButtonComponent.new(href: new_import_fogbugz_path(namespace_id: namespace_id), icon: 'bug', button_options: { class: 'import_fogbugz js-import-project-btn', data: { platform: 'fogbugz', **tracking_attrs_data(track_label, 'click_button', 'fogbugz') } }) do
|
||||||
.gl-button-icon
|
|
||||||
= sprite_icon('bug')
|
|
||||||
FogBugz
|
FogBugz
|
||||||
|
|
||||||
- if gitea_import_enabled?
|
- if gitea_import_enabled?
|
||||||
|
@ -60,24 +47,18 @@
|
||||||
|
|
||||||
- if git_import_enabled?
|
- if git_import_enabled?
|
||||||
%div
|
%div
|
||||||
%button.gl-button.btn-default.btn.btn-svg.js-toggle-button.js-import-git-toggle-button.js-import-project-btn{ type: "button", data: { platform: 'repo_url', toggle_open_class: 'active', **tracking_attrs_data(track_label, 'click_button', 'repo_url') } }
|
= render Pajamas::ButtonComponent.new(icon: 'link', button_options: { class: 'js-toggle-button js-import-git-toggle-button js-import-project-btn', data: { platform: 'repo_url', toggle_open_class: 'active', **tracking_attrs_data(track_label, 'click_button', 'repo_url') } }) do
|
||||||
.gl-button-icon
|
|
||||||
= sprite_icon('link', css_class: 'gl-icon')
|
|
||||||
= _('Repository by URL')
|
= _('Repository by URL')
|
||||||
|
|
||||||
- if manifest_import_enabled?
|
- if manifest_import_enabled?
|
||||||
%div
|
%div
|
||||||
= link_to new_import_manifest_path(namespace_id: namespace_id), class: 'gl-button btn-default btn import_manifest js-import-project-btn', data: { platform: 'manifest_file', **tracking_attrs_data(track_label, 'click_button', 'manifest_file') } do
|
= render Pajamas::ButtonComponent.new(href: new_import_manifest_path(namespace_id: namespace_id), icon: 'doc-text', button_options: { class: 'import_manifest js-import-project-btn', data: { platform: 'manifest_file', **tracking_attrs_data(track_label, 'click_button', 'manifest_file') } }) do
|
||||||
.gl-button-icon
|
= _('Manifest file')
|
||||||
= sprite_icon('doc-text')
|
|
||||||
Manifest file
|
|
||||||
|
|
||||||
- if phabricator_import_enabled?
|
- if phabricator_import_enabled?
|
||||||
%div
|
%div
|
||||||
= link_to new_import_phabricator_path(namespace_id: namespace_id), class: 'gl-button btn-default btn import_phabricator js-import-project-btn', data: { platform: 'phabricator', track_label: "#{track_label}", track_action: "click_button", track_property: "phabricator" } do
|
= render Pajamas::ButtonComponent.new(href: new_import_phabricator_path(namespace_id: namespace_id), icon: 'issues', button_options: { class: 'import_phabricator js-import-project-btn', data: { platform: 'phabricator', track_label: "#{track_label}", track_action: "click_button", track_property: "phabricator" } }) do
|
||||||
.gl-button-icon
|
= _('Phabricator tasks')
|
||||||
= custom_icon('issues')
|
|
||||||
= _("Phabricator Tasks")
|
|
||||||
|
|
||||||
|
|
||||||
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
|
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
|
||||||
|
|
|
@ -4,7 +4,7 @@ module Namespaces
|
||||||
class RootStatisticsWorker
|
class RootStatisticsWorker
|
||||||
include ApplicationWorker
|
include ApplicationWorker
|
||||||
|
|
||||||
data_consistency :always
|
data_consistency :sticky, feature_flag: :root_statistics_worker_read_replica
|
||||||
|
|
||||||
sidekiq_options retry: 3
|
sidekiq_options retry: 3
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,6 @@
|
||||||
- planning_analytics
|
- planning_analytics
|
||||||
- pods
|
- pods
|
||||||
- portfolio_management
|
- portfolio_management
|
||||||
- privacy_control_center
|
|
||||||
- product_analytics
|
- product_analytics
|
||||||
- projects
|
- projects
|
||||||
- pubsec_services
|
- pubsec_services
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: root_statistics_worker_read_replica
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102516
|
||||||
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/379678
|
||||||
|
milestone: '15.6'
|
||||||
|
type: development
|
||||||
|
group: group::utilization
|
||||||
|
default_enabled: false
|
|
@ -11434,6 +11434,7 @@ CREATE TABLE application_settings (
|
||||||
inactive_projects_min_size_mb integer DEFAULT 0 NOT NULL,
|
inactive_projects_min_size_mb integer DEFAULT 0 NOT NULL,
|
||||||
inactive_projects_send_warning_email_after_months integer DEFAULT 1 NOT NULL,
|
inactive_projects_send_warning_email_after_months integer DEFAULT 1 NOT NULL,
|
||||||
delayed_group_deletion boolean DEFAULT true NOT NULL,
|
delayed_group_deletion boolean DEFAULT true NOT NULL,
|
||||||
|
maven_package_requests_forwarding boolean DEFAULT true NOT NULL,
|
||||||
arkose_labs_namespace text DEFAULT 'client'::text NOT NULL,
|
arkose_labs_namespace text DEFAULT 'client'::text NOT NULL,
|
||||||
max_export_size integer DEFAULT 0,
|
max_export_size integer DEFAULT 0,
|
||||||
encrypted_slack_app_signing_secret bytea,
|
encrypted_slack_app_signing_secret bytea,
|
||||||
|
@ -11472,18 +11473,17 @@ CREATE TABLE application_settings (
|
||||||
cube_api_base_url text,
|
cube_api_base_url text,
|
||||||
encrypted_cube_api_key bytea,
|
encrypted_cube_api_key bytea,
|
||||||
encrypted_cube_api_key_iv bytea,
|
encrypted_cube_api_key_iv bytea,
|
||||||
maven_package_requests_forwarding boolean DEFAULT true NOT NULL,
|
|
||||||
dashboard_limit_enabled boolean DEFAULT false NOT NULL,
|
|
||||||
dashboard_limit integer DEFAULT 0 NOT NULL,
|
|
||||||
dashboard_notification_limit integer DEFAULT 0 NOT NULL,
|
|
||||||
dashboard_enforcement_limit integer DEFAULT 0 NOT NULL,
|
|
||||||
dashboard_limit_new_namespace_creation_enforcement_date date,
|
|
||||||
jitsu_host text,
|
jitsu_host text,
|
||||||
jitsu_project_xid text,
|
jitsu_project_xid text,
|
||||||
clickhouse_connection_string text,
|
clickhouse_connection_string text,
|
||||||
jitsu_administrator_email text,
|
jitsu_administrator_email text,
|
||||||
encrypted_jitsu_administrator_password bytea,
|
encrypted_jitsu_administrator_password bytea,
|
||||||
encrypted_jitsu_administrator_password_iv bytea,
|
encrypted_jitsu_administrator_password_iv bytea,
|
||||||
|
dashboard_limit_enabled boolean DEFAULT false NOT NULL,
|
||||||
|
dashboard_limit integer DEFAULT 0 NOT NULL,
|
||||||
|
dashboard_notification_limit integer DEFAULT 0 NOT NULL,
|
||||||
|
dashboard_enforcement_limit integer DEFAULT 0 NOT NULL,
|
||||||
|
dashboard_limit_new_namespace_creation_enforcement_date date,
|
||||||
can_create_group boolean DEFAULT true NOT NULL,
|
can_create_group boolean DEFAULT true NOT NULL,
|
||||||
lock_maven_package_requests_forwarding boolean DEFAULT false NOT NULL,
|
lock_maven_package_requests_forwarding boolean DEFAULT false NOT NULL,
|
||||||
lock_pypi_package_requests_forwarding boolean DEFAULT false NOT NULL,
|
lock_pypi_package_requests_forwarding boolean DEFAULT false NOT NULL,
|
||||||
|
@ -20192,8 +20192,8 @@ CREATE TABLE project_settings (
|
||||||
selective_code_owner_removals boolean DEFAULT false NOT NULL,
|
selective_code_owner_removals boolean DEFAULT false NOT NULL,
|
||||||
issue_branch_template text,
|
issue_branch_template text,
|
||||||
show_diff_preview_in_email boolean DEFAULT true NOT NULL,
|
show_diff_preview_in_email boolean DEFAULT true NOT NULL,
|
||||||
suggested_reviewers_enabled boolean DEFAULT false NOT NULL,
|
|
||||||
jitsu_key text,
|
jitsu_key text,
|
||||||
|
suggested_reviewers_enabled boolean DEFAULT false NOT NULL,
|
||||||
only_allow_merge_if_all_status_checks_passed boolean DEFAULT false NOT NULL,
|
only_allow_merge_if_all_status_checks_passed boolean DEFAULT false NOT NULL,
|
||||||
mirror_branch_regex text,
|
mirror_branch_regex text,
|
||||||
CONSTRAINT check_2981f15877 CHECK ((char_length(jitsu_key) <= 100)),
|
CONSTRAINT check_2981f15877 CHECK ((char_length(jitsu_key) <= 100)),
|
||||||
|
@ -28246,6 +28246,10 @@ CREATE UNIQUE INDEX p_ci_builds_metadata_build_id_partition_id_idx ON ONLY p_ci_
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_ci_builds_metadata_on_build_id_partition_id_unique ON ci_builds_metadata USING btree (build_id, partition_id);
|
CREATE UNIQUE INDEX index_ci_builds_metadata_on_build_id_partition_id_unique ON ci_builds_metadata USING btree (build_id, partition_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX p_ci_builds_metadata_id_partition_id_idx ON ONLY p_ci_builds_metadata USING btree (id, partition_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX index_ci_builds_metadata_on_id_partition_id_unique ON ci_builds_metadata USING btree (id, partition_id);
|
||||||
|
|
||||||
CREATE INDEX p_ci_builds_metadata_project_id_idx ON ONLY p_ci_builds_metadata USING btree (project_id);
|
CREATE INDEX p_ci_builds_metadata_project_id_idx ON ONLY p_ci_builds_metadata USING btree (project_id);
|
||||||
|
|
||||||
CREATE INDEX index_ci_builds_metadata_on_project_id ON ci_builds_metadata USING btree (project_id);
|
CREATE INDEX index_ci_builds_metadata_on_project_id ON ci_builds_metadata USING btree (project_id);
|
||||||
|
@ -32484,6 +32488,8 @@ ALTER INDEX p_ci_builds_metadata_build_id_id_idx ATTACH PARTITION index_ci_build
|
||||||
|
|
||||||
ALTER INDEX p_ci_builds_metadata_build_id_partition_id_idx ATTACH PARTITION index_ci_builds_metadata_on_build_id_partition_id_unique;
|
ALTER INDEX p_ci_builds_metadata_build_id_partition_id_idx ATTACH PARTITION index_ci_builds_metadata_on_build_id_partition_id_unique;
|
||||||
|
|
||||||
|
ALTER INDEX p_ci_builds_metadata_id_partition_id_idx ATTACH PARTITION index_ci_builds_metadata_on_id_partition_id_unique;
|
||||||
|
|
||||||
ALTER INDEX p_ci_builds_metadata_project_id_idx ATTACH PARTITION index_ci_builds_metadata_on_project_id;
|
ALTER INDEX p_ci_builds_metadata_project_id_idx ATTACH PARTITION index_ci_builds_metadata_on_project_id;
|
||||||
|
|
||||||
CREATE TRIGGER chat_names_loose_fk_trigger AFTER DELETE ON chat_names REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
|
CREATE TRIGGER chat_names_loose_fk_trigger AFTER DELETE ON chat_names REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
|
||||||
|
|
|
@ -2835,6 +2835,13 @@ deploystacks: [vultr, data]
|
||||||
deploystacks: [vultr, processing]
|
deploystacks: [vultr, processing]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Additional details**:
|
||||||
|
|
||||||
|
- `parallel:matrix` jobs add the variable values to the job names to differentiate
|
||||||
|
the jobs from each other, but [large values can cause names to exceed limits](https://gitlab.com/gitlab-org/gitlab/-/issues/362262):
|
||||||
|
- Job names must be [255 characters or fewer](../jobs/index.md#job-name-limitations).
|
||||||
|
- When using [`needs`](#needs), job names must be 128 characters or fewer.
|
||||||
|
|
||||||
**Related topics**:
|
**Related topics**:
|
||||||
|
|
||||||
- [Run a one-dimensional matrix of parallel jobs](../jobs/job_control.md#run-a-one-dimensional-matrix-of-parallel-jobs).
|
- [Run a one-dimensional matrix of parallel jobs](../jobs/job_control.md#run-a-one-dimensional-matrix-of-parallel-jobs).
|
||||||
|
|
|
@ -18236,9 +18236,6 @@ msgstr ""
|
||||||
msgid "GitLab will create a branch in your fork and start a merge request."
|
msgid "GitLab will create a branch in your fork and start a merge request."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "GitLab.com"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "GitLab.com (SaaS)"
|
msgid "GitLab.com (SaaS)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -23390,6 +23387,9 @@ msgstr ""
|
||||||
msgid "Job|Retry"
|
msgid "Job|Retry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Job|Run again"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Job|Running"
|
msgid "Job|Running"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -24744,6 +24744,9 @@ msgstr ""
|
||||||
msgid "Manifest"
|
msgid "Manifest"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Manifest file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Manifest file import"
|
msgid "Manifest file import"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -29450,7 +29453,7 @@ msgstr ""
|
||||||
msgid "Phabricator Server URL"
|
msgid "Phabricator Server URL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Phabricator Tasks"
|
msgid "Phabricator tasks"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
|
@ -47661,12 +47664,27 @@ msgstr ""
|
||||||
msgid "ciReport|Dependency scanning"
|
msgid "ciReport|Dependency scanning"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ciReport|Detects known vulnerabilities in your source code's dependencies."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ciReport|Detects known vulnerabilities in your source code."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ciReport|Detects known vulnerabilities in your web application."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ciReport|Detects secrets and credentials vulnerabilities in your source code."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ciReport|Download patch to resolve"
|
msgid "ciReport|Download patch to resolve"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ciReport|Download the patch to apply it manually"
|
msgid "ciReport|Download the patch to apply it manually"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ciReport|Dynamic Application Security Testing (DAST)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
|
msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -47786,6 +47804,9 @@ msgstr ""
|
||||||
msgid "ciReport|Solution"
|
msgid "ciReport|Solution"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ciReport|Static Application Security Testing (SAST)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
|
msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ RSpec.describe Projects::MergeRequests::DiffsController do
|
||||||
commit: nil,
|
commit: nil,
|
||||||
latest_diff: true,
|
latest_diff: true,
|
||||||
only_context_commits: false,
|
only_context_commits: false,
|
||||||
allow_tree_conflicts: true,
|
merge_conflicts_in_diff: true,
|
||||||
merge_ref_head_diff: false
|
merge_ref_head_diff: false
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -281,7 +281,7 @@ RSpec.describe Projects::MergeRequests::DiffsController do
|
||||||
commit: nil,
|
commit: nil,
|
||||||
latest_diff: true,
|
latest_diff: true,
|
||||||
only_context_commits: false,
|
only_context_commits: false,
|
||||||
allow_tree_conflicts: true,
|
merge_conflicts_in_diff: true,
|
||||||
merge_ref_head_diff: nil
|
merge_ref_head_diff: nil
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -303,7 +303,7 @@ RSpec.describe Projects::MergeRequests::DiffsController do
|
||||||
commit: merge_request.diff_head_commit,
|
commit: merge_request.diff_head_commit,
|
||||||
latest_diff: nil,
|
latest_diff: nil,
|
||||||
only_context_commits: false,
|
only_context_commits: false,
|
||||||
allow_tree_conflicts: true,
|
merge_conflicts_in_diff: true,
|
||||||
merge_ref_head_diff: nil
|
merge_ref_head_diff: nil
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -329,7 +329,7 @@ RSpec.describe Projects::MergeRequests::DiffsController do
|
||||||
commit: nil,
|
commit: nil,
|
||||||
latest_diff: true,
|
latest_diff: true,
|
||||||
only_context_commits: false,
|
only_context_commits: false,
|
||||||
allow_tree_conflicts: false,
|
merge_conflicts_in_diff: false,
|
||||||
merge_ref_head_diff: nil
|
merge_ref_head_diff: nil
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -488,7 +488,7 @@ RSpec.describe Projects::MergeRequests::DiffsController do
|
||||||
commit: nil,
|
commit: nil,
|
||||||
diff_view: :inline,
|
diff_view: :inline,
|
||||||
merge_ref_head_diff: nil,
|
merge_ref_head_diff: nil,
|
||||||
allow_tree_conflicts: true,
|
merge_conflicts_in_diff: true,
|
||||||
pagination_data: {
|
pagination_data: {
|
||||||
total_pages: nil
|
total_pages: nil
|
||||||
}.merge(pagination_data)
|
}.merge(pagination_data)
|
||||||
|
@ -616,7 +616,7 @@ RSpec.describe Projects::MergeRequests::DiffsController do
|
||||||
|
|
||||||
it_behaves_like 'serializes diffs with expected arguments' do
|
it_behaves_like 'serializes diffs with expected arguments' do
|
||||||
let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
|
let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
|
||||||
let(:expected_options) { collection_arguments(total_pages: 20).merge(allow_tree_conflicts: false) }
|
let(:expected_options) { collection_arguments(total_pages: 20).merge(merge_conflicts_in_diff: false) }
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'successful request'
|
it_behaves_like 'successful request'
|
||||||
|
|
|
@ -726,12 +726,7 @@ RSpec.describe 'Pipeline', :js do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
schedule.owner.block!
|
schedule.owner.block!
|
||||||
|
PipelineScheduleWorker.new.perform
|
||||||
begin
|
|
||||||
PipelineScheduleWorker.new.perform
|
|
||||||
rescue Ci::CreatePipelineService::CreateError
|
|
||||||
# Do nothing, assert view code after the Pipeline failed to create.
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays the PipelineSchedule in an inactive state' do
|
it 'displays the PipelineSchedule in an inactive state' do
|
||||||
|
|
|
@ -851,12 +851,7 @@ RSpec.describe 'Pipeline', :js do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
schedule.owner.block!
|
schedule.owner.block!
|
||||||
|
PipelineScheduleWorker.new.perform
|
||||||
begin
|
|
||||||
PipelineScheduleWorker.new.perform
|
|
||||||
rescue Ci::CreatePipelineService::CreateError
|
|
||||||
# Do nothing, assert view code after the Pipeline failed to create.
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays the PipelineSchedule in an inactive state' do
|
it 'displays the PipelineSchedule in an inactive state' do
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||||
import JobRetryButton from '~/jobs/components/job/sidebar/job_sidebar_retry_button.vue';
|
import JobRetryButton from '~/jobs/components/job/sidebar/job_sidebar_retry_button.vue';
|
||||||
import LegacySidebarHeader from '~/jobs/components/job/sidebar/legacy_sidebar_header.vue';
|
import LegacySidebarHeader from '~/jobs/components/job/sidebar/legacy_sidebar_header.vue';
|
||||||
import createStore from '~/jobs/store';
|
import createStore from '~/jobs/store';
|
||||||
import job from '../../mock_data';
|
import job, { failedJobStatus } from '../../mock_data';
|
||||||
|
|
||||||
describe('Legacy Sidebar Header', () => {
|
describe('Legacy Sidebar Header', () => {
|
||||||
let store;
|
let store;
|
||||||
|
@ -67,6 +67,12 @@ describe('Legacy Sidebar Header', () => {
|
||||||
it('should render the retry button', () => {
|
it('should render the retry button', () => {
|
||||||
expect(findRetryButton().props('href')).toBe(job.retry_path);
|
expect(findRetryButton().props('href')).toBe(job.retry_path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should have a different label when the job status is passed', () => {
|
||||||
|
expect(findRetryButton().attributes('title')).toBe(
|
||||||
|
LegacySidebarHeader.i18n.runAgainJobButtonLabel,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when there is no retry path', () => {
|
describe('when there is no retry path', () => {
|
||||||
|
@ -88,4 +94,16 @@ describe('Legacy Sidebar Header', () => {
|
||||||
expect(findCancelButton().attributes('href')).toBe(job.cancel_path);
|
expect(findCancelButton().attributes('href')).toBe(job.cancel_path);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when the job is failed', () => {
|
||||||
|
describe('retry button', () => {
|
||||||
|
it('should have a different label when the job status is failed', () => {
|
||||||
|
createWrapper({ job: { ...job, status: failedJobStatus } });
|
||||||
|
|
||||||
|
expect(findRetryButton().attributes('title')).toBe(
|
||||||
|
LegacySidebarHeader.i18n.retryJobButtonLabel,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1086,6 +1086,29 @@ export default {
|
||||||
has_trace: true,
|
has_trace: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const failedJobStatus = {
|
||||||
|
icon: 'status_warning',
|
||||||
|
text: 'failed',
|
||||||
|
label: 'failed (allowed to fail)',
|
||||||
|
group: 'failed-with-warnings',
|
||||||
|
tooltip: 'failed - (unknown failure) (allowed to fail)',
|
||||||
|
has_details: true,
|
||||||
|
details_path: '/gitlab-org/gitlab-shell/-/jobs/454',
|
||||||
|
illustration: {
|
||||||
|
image: 'illustrations/skipped-job_empty.svg',
|
||||||
|
size: 'svg-430',
|
||||||
|
title: 'This job does not have a trace.',
|
||||||
|
},
|
||||||
|
favicon:
|
||||||
|
'/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
|
||||||
|
action: {
|
||||||
|
icon: 'retry',
|
||||||
|
title: 'Retry',
|
||||||
|
path: '/gitlab-org/gitlab-shell/-/jobs/454/retry',
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const jobsInStage = {
|
export const jobsInStage = {
|
||||||
name: 'build',
|
name: 'build',
|
||||||
title: 'build: running',
|
title: 'build: running',
|
||||||
|
|
|
@ -36,12 +36,14 @@ describe('~/vue_merge_request_widget/components/widget/widget_content_row.vue',
|
||||||
},
|
},
|
||||||
slots: {
|
slots: {
|
||||||
header: '<span>this is a header</span>',
|
header: '<span>this is a header</span>',
|
||||||
|
'header-actions': '<span>this is a header action</span>',
|
||||||
body: '<span>this is a body</span>',
|
body: '<span>this is a body</span>',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(wrapper.findByText('this is a body').exists()).toBe(true);
|
expect(wrapper.findByText('this is a body').exists()).toBe(true);
|
||||||
expect(wrapper.findByText('this is a header').exists()).toBe(true);
|
expect(wrapper.findByText('this is a header').exists()).toBe(true);
|
||||||
|
expect(wrapper.findByText('this is a header action').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -471,7 +471,6 @@ RSpec.describe DiffHelper do
|
||||||
|
|
||||||
describe '#conflicts' do
|
describe '#conflicts' do
|
||||||
let(:merge_request) { instance_double(MergeRequest, cannot_be_merged?: true) }
|
let(:merge_request) { instance_double(MergeRequest, cannot_be_merged?: true) }
|
||||||
let(:merge_ref_head_diff) { true }
|
|
||||||
let(:can_be_resolved_in_ui?) { true }
|
let(:can_be_resolved_in_ui?) { true }
|
||||||
let(:allow_tree_conflicts) { false }
|
let(:allow_tree_conflicts) { false }
|
||||||
let(:files) { [instance_double(Gitlab::Conflict::File, path: 'a')] }
|
let(:files) { [instance_double(Gitlab::Conflict::File, path: 'a')] }
|
||||||
|
@ -479,7 +478,6 @@ RSpec.describe DiffHelper do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(helper).to receive(:merge_request).and_return(merge_request)
|
allow(helper).to receive(:merge_request).and_return(merge_request)
|
||||||
allow(helper).to receive(:options).and_return(merge_ref_head_diff: merge_ref_head_diff)
|
|
||||||
|
|
||||||
allow_next_instance_of(MergeRequests::Conflicts::ListService, merge_request, allow_tree_conflicts: allow_tree_conflicts) do |svc|
|
allow_next_instance_of(MergeRequests::Conflicts::ListService, merge_request, allow_tree_conflicts: allow_tree_conflicts) do |svc|
|
||||||
allow(svc).to receive(:can_be_resolved_in_ui?).and_return(can_be_resolved_in_ui?)
|
allow(svc).to receive(:can_be_resolved_in_ui?).and_return(can_be_resolved_in_ui?)
|
||||||
|
@ -496,14 +494,6 @@ RSpec.describe DiffHelper do
|
||||||
expect(helper.conflicts).to eq('a' => files.first)
|
expect(helper.conflicts).to eq('a' => files.first)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when merge_ref_head_diff option is false' do
|
|
||||||
let(:merge_ref_head_diff) { false }
|
|
||||||
|
|
||||||
it 'returns nil' do
|
|
||||||
expect(helper.conflicts).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when merge request can be merged' do
|
context 'when merge request can be merged' do
|
||||||
let(:merge_request) { instance_double(MergeRequest, cannot_be_merged?: false) }
|
let(:merge_request) { instance_double(MergeRequest, cannot_be_merged?: false) }
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,6 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Median do
|
||||||
merge_request2.metrics.update!(merged_at: Time.zone.now)
|
merge_request2.metrics.update!(merged_at: Time.zone.now)
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(subject).to be_within(0.5).of(7.5.minutes.seconds)
|
expect(subject).to be_within(5.seconds).of(7.5.minutes.seconds)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do
|
||||||
let(:default_branch) { 'master' }
|
let(:default_branch) { 'master' }
|
||||||
let(:pipeline_ref) { default_branch }
|
let(:pipeline_ref) { default_branch }
|
||||||
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
||||||
let(:pipeline) { service.execute!(:push).payload }
|
let(:pipeline) { service.execute(:push).payload }
|
||||||
let(:build_names) { pipeline.builds.pluck(:name) }
|
let(:build_names) { pipeline.builds.pluck(:name) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -62,7 +62,8 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do
|
||||||
|
|
||||||
context 'on master' do
|
context 'on master' do
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,7 +71,8 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do
|
||||||
let(:pipeline_ref) { 'feature' }
|
let(:pipeline_ref) { 'feature' }
|
||||||
|
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,7 +80,8 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do
|
||||||
let(:pipeline_ref) { 'v1.0.0' }
|
let(:pipeline_ref) { 'v1.0.0' }
|
||||||
|
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,10 +9,10 @@ RSpec.describe 'Jobs/SAST-IaC.gitlab-ci.yml' do
|
||||||
let_it_be(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let_it_be(:user) { project.first_owner }
|
let_it_be(:user) { project.first_owner }
|
||||||
|
|
||||||
let(:default_branch) { 'main' }
|
let(:default_branch) { "master" }
|
||||||
let(:pipeline_ref) { default_branch }
|
let(:pipeline_ref) { default_branch }
|
||||||
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
||||||
let(:pipeline) { service.execute!(:push).payload }
|
let(:pipeline) { service.execute(:push).payload }
|
||||||
let(:build_names) { pipeline.builds.pluck(:name) }
|
let(:build_names) { pipeline.builds.pluck(:name) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -49,7 +49,8 @@ RSpec.describe 'Jobs/SAST-IaC.gitlab-ci.yml' do
|
||||||
|
|
||||||
context 'on default branch' do
|
context 'on default branch' do
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -57,7 +58,8 @@ RSpec.describe 'Jobs/SAST-IaC.gitlab-ci.yml' do
|
||||||
let(:pipeline_ref) { 'feature' }
|
let(:pipeline_ref) { 'feature' }
|
||||||
|
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,10 +9,10 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do
|
||||||
let_it_be(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let_it_be(:user) { project.first_owner }
|
let_it_be(:user) { project.first_owner }
|
||||||
|
|
||||||
let(:default_branch) { 'main' }
|
let(:default_branch) { "master" }
|
||||||
let(:pipeline_ref) { default_branch }
|
let(:pipeline_ref) { default_branch }
|
||||||
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
||||||
let(:pipeline) { service.execute!(:push).payload }
|
let(:pipeline) { service.execute(:push).payload }
|
||||||
let(:build_names) { pipeline.builds.pluck(:name) }
|
let(:build_names) { pipeline.builds.pluck(:name) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -50,7 +50,8 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do
|
||||||
|
|
||||||
context 'on default branch' do
|
context 'on default branch' do
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -58,7 +59,8 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do
|
||||||
let(:pipeline_ref) { 'feature' }
|
let(:pipeline_ref) { 'feature' }
|
||||||
|
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do
|
||||||
let(:default_branch) { 'master' }
|
let(:default_branch) { 'master' }
|
||||||
let(:pipeline_ref) { default_branch }
|
let(:pipeline_ref) { default_branch }
|
||||||
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
||||||
let(:pipeline) { service.execute!(:push).payload }
|
let(:pipeline) { service.execute(:push).payload }
|
||||||
let(:build_names) { pipeline.builds.pluck(:name) }
|
let(:build_names) { pipeline.builds.pluck(:name) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -62,7 +62,8 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do
|
||||||
|
|
||||||
context 'on master' do
|
context 'on master' do
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,7 +71,8 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do
|
||||||
let(:pipeline_ref) { 'feature' }
|
let(:pipeline_ref) { 'feature' }
|
||||||
|
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,7 +80,8 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do
|
||||||
let(:pipeline_ref) { 'v1.0.0' }
|
let(:pipeline_ref) { 'v1.0.0' }
|
||||||
|
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@ RSpec.describe 'npm.gitlab-ci.yml' do
|
||||||
let(:pipeline_tag) { 'v1.2.1' }
|
let(:pipeline_tag) { 'v1.2.1' }
|
||||||
let(:pipeline_ref) { pipeline_branch }
|
let(:pipeline_ref) { pipeline_branch }
|
||||||
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref ) }
|
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref ) }
|
||||||
let(:pipeline) { service.execute!(:push).payload }
|
let(:pipeline) { service.execute(:push).payload }
|
||||||
let(:build_names) { pipeline.builds.pluck(:name) }
|
let(:build_names) { pipeline.builds.pluck(:name) }
|
||||||
|
|
||||||
def create_branch(name:)
|
def create_branch(name:)
|
||||||
|
@ -42,7 +42,8 @@ RSpec.describe 'npm.gitlab-ci.yml' do
|
||||||
|
|
||||||
shared_examples 'no pipeline created' do
|
shared_examples 'no pipeline created' do
|
||||||
it 'does not create a pipeline because the only job (publish) is not created' do
|
it 'does not create a pipeline because the only job (publish) is not created' do
|
||||||
expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.')
|
expect(build_names).to be_empty
|
||||||
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ RSpec.describe 'ThemeKit.gitlab-ci.yml' do
|
||||||
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
|
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
|
||||||
let(:user) { project.first_owner }
|
let(:user) { project.first_owner }
|
||||||
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
|
||||||
let(:pipeline) { service.execute!(:push).payload }
|
let(:pipeline) { service.execute(:push).payload }
|
||||||
let(:build_names) { pipeline.builds.pluck(:name) }
|
let(:build_names) { pipeline.builds.pluck(:name) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -51,9 +51,8 @@ RSpec.describe 'ThemeKit.gitlab-ci.yml' do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has no jobs' do
|
it 'has no jobs' do
|
||||||
expect { pipeline }.to raise_error(
|
expect(build_names).to be_empty
|
||||||
Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.'
|
expect(pipeline.errors.full_messages).to match_array(["No stages / jobs for this pipeline."])
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -382,5 +382,81 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
|
||||||
|
|
||||||
it_behaves_like 'migrating queues'
|
it_behaves_like 'migrating queues'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when multiple workers are in the same queue' do
|
||||||
|
before do
|
||||||
|
ExportCsvWorker.sidekiq_options(queue: 'email_receiver') # follows EmailReceiverWorker's queue
|
||||||
|
ExportCsvWorker.perform_async('fizz')
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
ExportCsvWorker.set_queue
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the queue exists in mappings' do
|
||||||
|
let(:mappings) do
|
||||||
|
{ 'EmailReceiverWorker' => 'email_receiver', 'AuthorizedProjectUpdate::ProjectRecalculateWorker' => 'default',
|
||||||
|
'ExportCsvWorker' => 'default' }
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:queues_included_pre_migrate) do
|
||||||
|
['email_receiver',
|
||||||
|
'authorized_project_update:authorized_project_update_project_recalculate']
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:queues_excluded_pre_migrate) { ['default'] }
|
||||||
|
let(:queues_excluded_post_migrate) do
|
||||||
|
['authorized_project_update:authorized_project_update_project_recalculate']
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:queues_included_post_migrate) { %w[default email_receiver] }
|
||||||
|
|
||||||
|
it_behaves_like 'migrating queues'
|
||||||
|
def post_migrate_checks
|
||||||
|
# jobs from email_receiver are not migrated at all
|
||||||
|
jobs = list_jobs('email_receiver')
|
||||||
|
expect(jobs.length).to eq(3)
|
||||||
|
sorted = jobs.sort_by { |job| [job["class"], job["args"]] }
|
||||||
|
expect(sorted[0]).to include('class' => 'EmailReceiverWorker', 'args' => ['bar'], 'queue' => 'email_receiver')
|
||||||
|
expect(sorted[1]).to include('class' => 'EmailReceiverWorker', 'args' => ['foo'], 'queue' => 'email_receiver')
|
||||||
|
expect(sorted[2]).to include('class' => 'ExportCsvWorker', 'args' => ['fizz'], 'queue' => 'email_receiver')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the queue doesnt exist in mappings' do
|
||||||
|
let(:mappings) do
|
||||||
|
{ 'EmailReceiverWorker' => 'default', 'AuthorizedProjectUpdate::ProjectRecalculateWorker' => 'default',
|
||||||
|
'ExportCsvWorker' => 'default' }
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:queues_included_pre_migrate) do
|
||||||
|
['email_receiver',
|
||||||
|
'authorized_project_update:authorized_project_update_project_recalculate']
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:queues_excluded_pre_migrate) { ['default'] }
|
||||||
|
let(:queues_excluded_post_migrate) do
|
||||||
|
['email_receiver', 'authorized_project_update:authorized_project_update_project_recalculate']
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:queues_included_post_migrate) { ['default'] }
|
||||||
|
|
||||||
|
it_behaves_like 'migrating queues'
|
||||||
|
def post_migrate_checks
|
||||||
|
# jobs from email_receiver are all migrated
|
||||||
|
jobs = list_jobs('email_receiver')
|
||||||
|
expect(jobs.length).to eq(0)
|
||||||
|
|
||||||
|
jobs = list_jobs('default')
|
||||||
|
expect(jobs.length).to eq(4)
|
||||||
|
sorted = jobs.sort_by { |job| [job["class"], job["args"]] }
|
||||||
|
expect(sorted[0]).to include('class' => 'AuthorizedProjectUpdate::ProjectRecalculateWorker',
|
||||||
|
'queue' => 'default')
|
||||||
|
expect(sorted[1]).to include('class' => 'EmailReceiverWorker', 'args' => ['bar'], 'queue' => 'default')
|
||||||
|
expect(sorted[2]).to include('class' => 'EmailReceiverWorker', 'args' => ['foo'], 'queue' => 'default')
|
||||||
|
expect(sorted[3]).to include('class' => 'ExportCsvWorker', 'args' => ['fizz'], 'queue' => 'default')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,142 +3,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Integrations::Slack do
|
RSpec.describe Integrations::Slack do
|
||||||
it_behaves_like Integrations::SlackMattermostNotifier, "Slack"
|
it_behaves_like Integrations::SlackMattermostNotifier, 'Slack'
|
||||||
|
it_behaves_like Integrations::BaseSlackNotification, factory: :integrations_slack
|
||||||
describe '#execute' do
|
|
||||||
let_it_be(:project) { create(:project, :repository, :wiki_repo) }
|
|
||||||
let_it_be(:slack_integration) { create(:integrations_slack, branches_to_be_notified: 'all', project: project) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
stub_request(:post, slack_integration.webhook)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'uses only known events', :aggregate_failures do
|
|
||||||
described_class::SUPPORTED_EVENTS_FOR_USAGE_LOG.each do |action|
|
|
||||||
expect(Gitlab::UsageDataCounters::HLLRedisCounter.known_event?("i_ecosystem_slack_service_#{action}_notification")).to be true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'hook data includes a user object' do
|
|
||||||
let_it_be(:user) { create_default(:user) }
|
|
||||||
|
|
||||||
shared_examples 'increases the usage data counter' do |event_name|
|
|
||||||
subject(:execute) { slack_integration.execute(data) }
|
|
||||||
|
|
||||||
it 'increases the usage data counter' do
|
|
||||||
expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(event_name, values: user.id).and_call_original
|
|
||||||
|
|
||||||
execute
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'Snowplow event tracking' do
|
|
||||||
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
|
|
||||||
let(:category) { 'Integrations::Slack' }
|
|
||||||
let(:action) { 'perform_integrations_action' }
|
|
||||||
let(:namespace) { project.namespace }
|
|
||||||
let(:label) { 'redis_hll_counters.ecosystem.ecosystem_total_unique_counts_monthly' }
|
|
||||||
let(:property) { event_name }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'event is not supported for usage log' do
|
|
||||||
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
|
|
||||||
|
|
||||||
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
|
|
||||||
|
|
||||||
it 'does not increase the usage data counter' do
|
|
||||||
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event).with('i_ecosystem_slack_service_pipeline_notification', values: user.id)
|
|
||||||
|
|
||||||
slack_integration.execute(data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'issue notification' do
|
|
||||||
let_it_be(:issue) { create(:issue, project: project) }
|
|
||||||
|
|
||||||
let(:data) { issue.to_hook_data(user) }
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_issue_notification'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'push notification' do
|
|
||||||
let(:data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_push_notification'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'deployment notification' do
|
|
||||||
let_it_be(:deployment) { create(:deployment, project: project, user: user) }
|
|
||||||
|
|
||||||
let(:data) { Gitlab::DataBuilder::Deployment.build(deployment, deployment.status, Time.current) }
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_deployment_notification'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'wiki_page notification' do
|
|
||||||
let(:wiki_page) { create(:wiki_page, wiki: project.wiki, project: project, message: 'user created page: Awesome wiki_page') }
|
|
||||||
|
|
||||||
let(:data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
|
|
||||||
|
|
||||||
before do
|
|
||||||
# Skip this method that is not relevant to this test to prevent having
|
|
||||||
# to update project which is frozen
|
|
||||||
allow(project.wiki).to receive(:after_wiki_activity)
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_wiki_page_notification'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'merge_request notification' do
|
|
||||||
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
|
|
||||||
|
|
||||||
let(:data) { merge_request.to_hook_data(user) }
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_merge_request_notification'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'note notification' do
|
|
||||||
let_it_be(:issue_note) { create(:note_on_issue, project: project, note: 'issue note') }
|
|
||||||
|
|
||||||
let(:data) { Gitlab::DataBuilder::Note.build(issue_note, user) }
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_note_notification'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'tag_push notification' do
|
|
||||||
let(:oldrev) { Gitlab::Git::BLANK_SHA }
|
|
||||||
let(:newrev) { '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b' } # gitlab-test: git rev-parse refs/tags/v1.1.0
|
|
||||||
let(:ref) { 'refs/tags/v1.1.0' }
|
|
||||||
let(:data) { Git::TagHooksService.new(project, user, change: { oldrev: oldrev, newrev: newrev, ref: ref }).send(:push_data) }
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_tag_push_notification'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'confidential note notification' do
|
|
||||||
let_it_be(:confidential_issue_note) { create(:note_on_issue, project: project, note: 'issue note', confidential: true) }
|
|
||||||
|
|
||||||
let(:data) { Gitlab::DataBuilder::Note.build(confidential_issue_note, user) }
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_confidential_note_notification'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'confidential issue notification' do
|
|
||||||
let_it_be(:issue) { create(:issue, project: project, confidential: true) }
|
|
||||||
|
|
||||||
let(:data) { issue.to_hook_data(user) }
|
|
||||||
|
|
||||||
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_confidential_issue_notification'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'hook data does not include a user' do
|
|
||||||
let(:data) { Gitlab::DataBuilder::Pipeline.build(create(:ci_pipeline, project: project)) }
|
|
||||||
|
|
||||||
it 'does not increase the usage data counter' do
|
|
||||||
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
|
|
||||||
|
|
||||||
slack_integration.execute(data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5138,17 +5138,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false' do
|
it 'returns false' do
|
||||||
expect(merge_request.diffable_merge_ref?).to eq(true)
|
expect(merge_request.diffable_merge_ref?).to eq(false)
|
||||||
end
|
|
||||||
|
|
||||||
context 'display_merge_conflicts_in_diff is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(display_merge_conflicts_in_diff: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false' do
|
|
||||||
expect(merge_request.diffable_merge_ref?).to eq(false)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,7 +35,7 @@ RSpec.describe 'Merge Requests Context Commit Diffs' do
|
||||||
commit: nil,
|
commit: nil,
|
||||||
diff_view: :inline,
|
diff_view: :inline,
|
||||||
merge_ref_head_diff: nil,
|
merge_ref_head_diff: nil,
|
||||||
allow_tree_conflicts: true,
|
merge_conflicts_in_diff: true,
|
||||||
pagination_data: {
|
pagination_data: {
|
||||||
total_pages: nil
|
total_pages: nil
|
||||||
}.merge(pagination_data)
|
}.merge(pagination_data)
|
||||||
|
|
|
@ -33,7 +33,7 @@ RSpec.describe 'Merge Requests Diffs' do
|
||||||
commit: nil,
|
commit: nil,
|
||||||
diff_view: :inline,
|
diff_view: :inline,
|
||||||
merge_ref_head_diff: nil,
|
merge_ref_head_diff: nil,
|
||||||
allow_tree_conflicts: true,
|
merge_conflicts_in_diff: true,
|
||||||
pagination_data: {
|
pagination_data: {
|
||||||
total_pages: nil
|
total_pages: nil
|
||||||
}.merge(pagination_data)
|
}.merge(pagination_data)
|
||||||
|
@ -128,7 +128,7 @@ RSpec.describe 'Merge Requests Diffs' do
|
||||||
|
|
||||||
context 'with disabled display_merge_conflicts_in_diff feature' do
|
context 'with disabled display_merge_conflicts_in_diff feature' do
|
||||||
let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
|
let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
|
||||||
let(:expected_options) { collection_arguments(total_pages: 20).merge(allow_tree_conflicts: false) }
|
let(:expected_options) { collection_arguments(total_pages: 20).merge(merge_conflicts_in_diff: false) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(display_merge_conflicts_in_diff: false)
|
stub_feature_flags(display_merge_conflicts_in_diff: false)
|
||||||
|
|
|
@ -80,47 +80,13 @@ RSpec.describe DiffFileEntity do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#is_fully_expanded' do
|
|
||||||
context 'file with a conflict' do
|
|
||||||
let(:options) { { conflicts: { diff_file.new_path => double(diff_lines_for_serializer: [], conflict_type: :both_modified) } } }
|
|
||||||
|
|
||||||
it 'returns false' do
|
|
||||||
expect(diff_file).not_to receive(:fully_expanded?)
|
|
||||||
expect(subject[:is_fully_expanded]).to eq(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#highlighted_diff_lines' do
|
describe '#highlighted_diff_lines' do
|
||||||
context 'file without a conflict' do
|
let(:options) { { conflicts: {} } }
|
||||||
let(:options) { { conflicts: {} } }
|
|
||||||
|
|
||||||
it 'calls diff_lines_for_serializer on diff_file' do
|
it 'calls diff_lines_for_serializer on diff_file' do
|
||||||
# #diff_lines_for_serializer gets called in #fully_expanded? as well so we expect twice
|
# #diff_lines_for_serializer gets called in #fully_expanded? as well so we expect twice
|
||||||
expect(diff_file).to receive(:diff_lines_for_serializer).twice.and_return([])
|
expect(diff_file).to receive(:diff_lines_for_serializer).twice.and_return([])
|
||||||
expect(subject[:highlighted_diff_lines]).to eq([])
|
expect(subject[:highlighted_diff_lines]).to eq([])
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'file with a conflict' do
|
|
||||||
let(:conflict_file) { instance_double(Gitlab::Conflict::File, conflict_type: :both_modified) }
|
|
||||||
let(:options) { { conflicts: { diff_file.new_path => conflict_file } } }
|
|
||||||
|
|
||||||
it 'calls diff_lines_for_serializer on matching conflict file' do
|
|
||||||
expect(conflict_file).to receive(:diff_lines_for_serializer).and_return([])
|
|
||||||
expect(subject[:highlighted_diff_lines]).to eq([])
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when Gitlab::Git::Conflict::Parser::UnmergeableFile gets raised' do
|
|
||||||
before do
|
|
||||||
allow(conflict_file).to receive(:diff_lines_for_serializer).and_raise(Gitlab::Git::Conflict::Parser::UnmergeableFile)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'falls back to diff_file diff_lines_for_serializer' do
|
|
||||||
expect(diff_file).to receive(:diff_lines_for_serializer).and_return([])
|
|
||||||
expect(subject[:highlighted_diff_lines]).to eq([])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,13 @@ RSpec.describe DiffsEntity do
|
||||||
|
|
||||||
let(:request) { EntityRequest.new(project: project, current_user: user) }
|
let(:request) { EntityRequest.new(project: project, current_user: user) }
|
||||||
let(:merge_request_diffs) { merge_request.merge_request_diffs }
|
let(:merge_request_diffs) { merge_request.merge_request_diffs }
|
||||||
let(:allow_tree_conflicts) { false }
|
let(:merge_conflicts_in_diff) { false }
|
||||||
let(:options) do
|
let(:options) do
|
||||||
{
|
{
|
||||||
request: request,
|
request: request,
|
||||||
merge_request: merge_request,
|
merge_request: merge_request,
|
||||||
merge_request_diffs: merge_request_diffs,
|
merge_request_diffs: merge_request_diffs,
|
||||||
allow_tree_conflicts: allow_tree_conflicts
|
merge_conflicts_in_diff: merge_conflicts_in_diff
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -87,60 +87,39 @@ RSpec.describe DiffsEntity do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are conflicts' do
|
describe 'diff_files' do
|
||||||
let(:diff_files) { merge_request_diffs.first.diffs.diff_files }
|
let(:diff_files) { merge_request_diffs.first.diffs.diff_files }
|
||||||
let(:diff_file_with_conflict) { diff_files.to_a.last }
|
|
||||||
let(:diff_file_without_conflict) { diff_files.to_a[-2] }
|
|
||||||
|
|
||||||
let(:resolvable_conflicts) { true }
|
it 'serializes diff files using DiffFileEntity' do
|
||||||
let(:conflict_file) { double(path: diff_file_with_conflict.new_path, conflict_type: :both_modified) }
|
expect(DiffFileEntity)
|
||||||
let(:conflicts) { double(conflicts: double(files: [conflict_file]), can_be_resolved_in_ui?: resolvable_conflicts) }
|
.to receive(:represent)
|
||||||
|
.with(
|
||||||
|
diff_files,
|
||||||
|
hash_including(options.merge(conflicts: nil))
|
||||||
|
)
|
||||||
|
|
||||||
let(:merge_ref_head_diff) { true }
|
subject[:diff_files]
|
||||||
let(:options) { super().merge(merge_ref_head_diff: merge_ref_head_diff) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(merge_request).to receive(:cannot_be_merged?).and_return(true)
|
|
||||||
allow(MergeRequests::Conflicts::ListService).to receive(:new).and_return(conflicts)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'conflicts are highlighted' do
|
context 'when merge_conflicts_in_diff is true' do
|
||||||
expect(conflict_file).to receive(:diff_lines_for_serializer)
|
let(:conflict_file) { double(path: diff_files.first.new_path, conflict_type: :both_modified) }
|
||||||
expect(diff_file_with_conflict).not_to receive(:diff_lines_for_serializer)
|
let(:conflicts) { double(conflicts: double(files: [conflict_file]), can_be_resolved_in_ui?: false) }
|
||||||
expect(diff_file_without_conflict).to receive(:diff_lines_for_serializer).twice # for highlighted_diff_lines and is_fully_expanded
|
let(:merge_conflicts_in_diff) { true }
|
||||||
|
|
||||||
subject
|
before do
|
||||||
end
|
allow(merge_request).to receive(:cannot_be_merged?).and_return(true)
|
||||||
|
allow(MergeRequests::Conflicts::ListService).to receive(:new).and_return(conflicts)
|
||||||
context 'merge ref head diff is not chosen to be displayed' do
|
|
||||||
let(:merge_ref_head_diff) { false }
|
|
||||||
|
|
||||||
it 'conflicts are not calculated' do
|
|
||||||
expect(MergeRequests::Conflicts::ListService).not_to receive(:new)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when conflicts cannot be resolved' do
|
|
||||||
let(:resolvable_conflicts) { false }
|
|
||||||
|
|
||||||
it 'conflicts are not highlighted' do
|
|
||||||
expect(conflict_file).not_to receive(:diff_lines_for_serializer)
|
|
||||||
expect(diff_file_with_conflict).to receive(:diff_lines_for_serializer).twice # for highlighted_diff_lines and is_fully_expanded
|
|
||||||
expect(diff_file_without_conflict).to receive(:diff_lines_for_serializer).twice # for highlighted_diff_lines and is_fully_expanded
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when allow_tree_conflicts is set to true' do
|
it 'serializes diff files with conflicts' do
|
||||||
let(:allow_tree_conflicts) { true }
|
expect(DiffFileEntity)
|
||||||
|
.to receive(:represent)
|
||||||
|
.with(
|
||||||
|
diff_files,
|
||||||
|
hash_including(options.merge(conflicts: { conflict_file.path => conflict_file }))
|
||||||
|
)
|
||||||
|
|
||||||
it 'conflicts are still highlighted' do
|
subject[:diff_files]
|
||||||
expect(conflict_file).to receive(:diff_lines_for_serializer)
|
|
||||||
expect(diff_file_with_conflict).not_to receive(:diff_lines_for_serializer)
|
|
||||||
expect(diff_file_without_conflict).to receive(:diff_lines_for_serializer).twice # for highlighted_diff_lines and is_fully_expanded
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ RSpec.describe DiffsMetadataEntity do
|
||||||
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
|
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
|
||||||
let(:merge_request_diffs) { merge_request.merge_request_diffs }
|
let(:merge_request_diffs) { merge_request.merge_request_diffs }
|
||||||
let(:merge_request_diff) { merge_request_diffs.last }
|
let(:merge_request_diff) { merge_request_diffs.last }
|
||||||
|
let(:merge_conflicts_in_diff) { false }
|
||||||
let(:options) { {} }
|
let(:options) { {} }
|
||||||
|
|
||||||
let(:entity) do
|
let(:entity) do
|
||||||
|
@ -17,7 +18,8 @@ RSpec.describe DiffsMetadataEntity do
|
||||||
options.merge(
|
options.merge(
|
||||||
request: request,
|
request: request,
|
||||||
merge_request: merge_request,
|
merge_request: merge_request,
|
||||||
merge_request_diffs: merge_request_diffs
|
merge_request_diffs: merge_request_diffs,
|
||||||
|
merge_conflicts_in_diff: merge_conflicts_in_diff
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -54,49 +56,36 @@ RSpec.describe DiffsMetadataEntity do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns diff files metadata' do
|
it 'serializes diff files metadata using DiffFileMetadataEntity' do
|
||||||
payload = DiffFileMetadataEntity.represent(raw_diff_files).as_json
|
expect(DiffFileMetadataEntity)
|
||||||
|
.to receive(:represent)
|
||||||
|
.with(
|
||||||
|
raw_diff_files,
|
||||||
|
hash_including(options.merge(conflicts: nil))
|
||||||
|
)
|
||||||
|
|
||||||
expect(subject[:diff_files]).to eq(payload)
|
subject[:diff_files]
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when merge_ref_head_diff and allow_tree_conflicts options are set' do
|
context 'when merge_conflicts_in_diff is true' do
|
||||||
let(:conflict_file) { double(path: raw_diff_files.first.new_path, conflict_type: :both_modified) }
|
let(:conflict_file) { double(path: raw_diff_files.first.new_path, conflict_type: :both_modified) }
|
||||||
let(:conflicts) { double(conflicts: double(files: [conflict_file]), can_be_resolved_in_ui?: false) }
|
let(:conflicts) { double(conflicts: double(files: [conflict_file]), can_be_resolved_in_ui?: false) }
|
||||||
|
let(:merge_conflicts_in_diff) { true }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(merge_request).to receive(:cannot_be_merged?).and_return(true)
|
allow(merge_request).to receive(:cannot_be_merged?).and_return(true)
|
||||||
allow(MergeRequests::Conflicts::ListService).to receive(:new).and_return(conflicts)
|
allow(MergeRequests::Conflicts::ListService).to receive(:new).and_return(conflicts)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when merge_ref_head_diff is true and allow_tree_conflicts is false' do
|
it 'serializes diff files with conflicts' do
|
||||||
let(:options) { { merge_ref_head_diff: true, allow_tree_conflicts: false } }
|
expect(DiffFileMetadataEntity)
|
||||||
|
.to receive(:represent)
|
||||||
|
.with(
|
||||||
|
raw_diff_files,
|
||||||
|
hash_including(options.merge(conflicts: { conflict_file.path => conflict_file }))
|
||||||
|
)
|
||||||
|
|
||||||
it 'returns diff files metadata without conflicts' do
|
subject[:diff_files]
|
||||||
payload = DiffFileMetadataEntity.represent(raw_diff_files).as_json
|
|
||||||
|
|
||||||
expect(subject[:diff_files]).to eq(payload)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when merge_ref_head_diff is false and allow_tree_conflicts is true' do
|
|
||||||
let(:options) { { merge_ref_head_diff: false, allow_tree_conflicts: true } }
|
|
||||||
|
|
||||||
it 'returns diff files metadata without conflicts' do
|
|
||||||
payload = DiffFileMetadataEntity.represent(raw_diff_files).as_json
|
|
||||||
|
|
||||||
expect(subject[:diff_files]).to eq(payload)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when merge_ref_head_diff and allow_tree_conflicts are true' do
|
|
||||||
let(:options) { { merge_ref_head_diff: true, allow_tree_conflicts: true } }
|
|
||||||
|
|
||||||
it 'returns diff files metadata with conflicts' do
|
|
||||||
payload = DiffFileMetadataEntity.represent(raw_diff_files, conflicts: { conflict_file.path => conflict_file }).as_json
|
|
||||||
|
|
||||||
expect(subject[:diff_files]).to eq(payload)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,13 +7,13 @@ RSpec.describe PaginatedDiffEntity do
|
||||||
let(:request) { double('request', current_user: user) }
|
let(:request) { double('request', current_user: user) }
|
||||||
let(:merge_request) { create(:merge_request) }
|
let(:merge_request) { create(:merge_request) }
|
||||||
let(:diff_batch) { merge_request.merge_request_diff.diffs_in_batch(2, 3, diff_options: nil) }
|
let(:diff_batch) { merge_request.merge_request_diff.diffs_in_batch(2, 3, diff_options: nil) }
|
||||||
let(:allow_tree_conflicts) { false }
|
let(:merge_conflicts_in_diff) { false }
|
||||||
let(:options) do
|
let(:options) do
|
||||||
{
|
{
|
||||||
request: request,
|
request: request,
|
||||||
merge_request: merge_request,
|
merge_request: merge_request,
|
||||||
pagination_data: diff_batch.pagination_data,
|
pagination_data: diff_batch.pagination_data,
|
||||||
allow_tree_conflicts: allow_tree_conflicts
|
merge_conflicts_in_diff: merge_conflicts_in_diff
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -29,61 +29,39 @@ RSpec.describe PaginatedDiffEntity do
|
||||||
expect(subject[:pagination]).to eq(total_pages: 20)
|
expect(subject[:pagination]).to eq(total_pages: 20)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are conflicts' do
|
describe 'diff_files' do
|
||||||
let(:diff_batch) { merge_request.merge_request_diff.diffs_in_batch(7, 3, diff_options: nil) }
|
let(:diff_files) { diff_batch.diff_files(sorted: true) }
|
||||||
let(:diff_files) { diff_batch.diff_files.to_a }
|
|
||||||
let(:diff_file_with_conflict) { diff_files.last }
|
|
||||||
let(:diff_file_without_conflict) { diff_files.first }
|
|
||||||
|
|
||||||
let(:resolvable_conflicts) { true }
|
it 'serializes diff files using DiffFileEntity' do
|
||||||
let(:conflict_file) { double(path: diff_file_with_conflict.new_path, conflict_type: :both_modified) }
|
expect(DiffFileEntity)
|
||||||
let(:conflicts) { double(conflicts: double(files: [conflict_file]), can_be_resolved_in_ui?: resolvable_conflicts) }
|
.to receive(:represent)
|
||||||
|
.with(
|
||||||
|
diff_files,
|
||||||
|
hash_including(options.merge(conflicts: nil))
|
||||||
|
)
|
||||||
|
|
||||||
let(:merge_ref_head_diff) { true }
|
subject[:diff_files]
|
||||||
let(:options) { super().merge(merge_ref_head_diff: merge_ref_head_diff) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(merge_request).to receive(:cannot_be_merged?).and_return(true)
|
|
||||||
allow(MergeRequests::Conflicts::ListService).to receive(:new).and_return(conflicts)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'conflicts are highlighted' do
|
context 'when merge_conflicts_in_diff is true' do
|
||||||
expect(conflict_file).to receive(:diff_lines_for_serializer)
|
let(:conflict_file) { double(path: diff_files.first.new_path, conflict_type: :both_modified) }
|
||||||
expect(diff_file_with_conflict).not_to receive(:diff_lines_for_serializer)
|
let(:conflicts) { double(conflicts: double(files: [conflict_file]), can_be_resolved_in_ui?: false) }
|
||||||
expect(diff_file_without_conflict).to receive(:diff_lines_for_serializer).twice # for highlighted_diff_lines and is_fully_expanded
|
let(:merge_conflicts_in_diff) { true }
|
||||||
|
|
||||||
subject
|
before do
|
||||||
end
|
allow(merge_request).to receive(:cannot_be_merged?).and_return(true)
|
||||||
|
allow(MergeRequests::Conflicts::ListService).to receive(:new).and_return(conflicts)
|
||||||
context 'merge ref head diff is not chosen to be displayed' do
|
|
||||||
let(:merge_ref_head_diff) { false }
|
|
||||||
|
|
||||||
it 'conflicts are not calculated' do
|
|
||||||
expect(MergeRequests::Conflicts::ListService).not_to receive(:new)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when conflicts cannot be resolved' do
|
|
||||||
let(:resolvable_conflicts) { false }
|
|
||||||
|
|
||||||
it 'conflicts are not highlighted' do
|
|
||||||
expect(conflict_file).not_to receive(:diff_lines_for_serializer)
|
|
||||||
expect(diff_file_with_conflict).to receive(:diff_lines_for_serializer).twice # for highlighted_diff_lines and is_fully_expanded
|
|
||||||
expect(diff_file_without_conflict).to receive(:diff_lines_for_serializer).twice # for highlighted_diff_lines and is_fully_expanded
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when allow_tree_conflicts is set to true' do
|
it 'serializes diff files with conflicts' do
|
||||||
let(:allow_tree_conflicts) { true }
|
expect(DiffFileEntity)
|
||||||
|
.to receive(:represent)
|
||||||
|
.with(
|
||||||
|
diff_files,
|
||||||
|
hash_including(options.merge(conflicts: { conflict_file.path => conflict_file }))
|
||||||
|
)
|
||||||
|
|
||||||
it 'conflicts are still highlighted' do
|
subject[:diff_files]
|
||||||
expect(conflict_file).to receive(:diff_lines_for_serializer)
|
|
||||||
expect(diff_file_with_conflict).not_to receive(:diff_lines_for_serializer)
|
|
||||||
expect(diff_file_without_conflict).to receive(:diff_lines_for_serializer).twice # for highlighted_diff_lines and is_fully_expanded
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -190,14 +190,6 @@ RSpec.describe MergeRequests::MergeabilityCheckService, :clean_gitlab_redis_shar
|
||||||
target_branch: 'conflict-start')
|
target_branch: 'conflict-start')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not change the merge ref HEAD' do
|
|
||||||
expect(merge_request.merge_ref_head).to be_nil
|
|
||||||
|
|
||||||
subject
|
|
||||||
|
|
||||||
expect(merge_request.reload.merge_ref_head).not_to be_nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns ServiceResponse.error and keeps merge status as cannot_be_merged' do
|
it 'returns ServiceResponse.error and keeps merge status as cannot_be_merged' do
|
||||||
result = subject
|
result = subject
|
||||||
|
|
||||||
|
@ -351,27 +343,5 @@ RSpec.describe MergeRequests::MergeabilityCheckService, :clean_gitlab_redis_shar
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'merge with conflicts' do
|
|
||||||
it 'calls MergeToRefService with true allow_conflicts param' do
|
|
||||||
expect(MergeRequests::MergeToRefService).to receive(:new)
|
|
||||||
.with(project: project, current_user: merge_request.author, params: { allow_conflicts: true }).and_call_original
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when display_merge_conflicts_in_diff is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(display_merge_conflicts_in_diff: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'calls MergeToRefService with false allow_conflicts param' do
|
|
||||||
expect(MergeRequests::MergeToRefService).to receive(:new)
|
|
||||||
.with(project: project, current_user: merge_request.author, params: { allow_conflicts: false }).and_call_original
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
|
||||||
|
describe '#execute' do
|
||||||
|
let_it_be(:project) { create(:project, :repository, :wiki_repo) }
|
||||||
|
let_it_be(:integration) { create(factory, branches_to_be_notified: 'all', project: project) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:post, integration.webhook)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses only known events', :aggregate_failures do
|
||||||
|
described_class::SUPPORTED_EVENTS_FOR_USAGE_LOG.each do |action|
|
||||||
|
expect(
|
||||||
|
Gitlab::UsageDataCounters::HLLRedisCounter.known_event?("i_ecosystem_slack_service_#{action}_notification")
|
||||||
|
).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when hook data includes a user object' do
|
||||||
|
let_it_be(:user) { create_default(:user) }
|
||||||
|
|
||||||
|
shared_examples 'increases the usage data counter' do |event_name|
|
||||||
|
subject(:execute) { integration.execute(data) }
|
||||||
|
|
||||||
|
it 'increases the usage data counter' do
|
||||||
|
expect(Gitlab::UsageDataCounters::HLLRedisCounter)
|
||||||
|
.to receive(:track_event).with(event_name, values: user.id).and_call_original
|
||||||
|
|
||||||
|
execute
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'Snowplow event tracking' do
|
||||||
|
let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
|
||||||
|
let(:category) { described_class.to_s }
|
||||||
|
let(:action) { 'perform_integrations_action' }
|
||||||
|
let(:namespace) { project.namespace }
|
||||||
|
let(:label) { 'redis_hll_counters.ecosystem.ecosystem_total_unique_counts_monthly' }
|
||||||
|
let(:property) { event_name }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when event is not supported for usage log' do
|
||||||
|
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
|
||||||
|
|
||||||
|
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
|
||||||
|
|
||||||
|
it 'does not increase the usage data counter' do
|
||||||
|
expect(Gitlab::UsageDataCounters::HLLRedisCounter)
|
||||||
|
.not_to receive(:track_event).with('i_ecosystem_slack_service_pipeline_notification', values: user.id)
|
||||||
|
|
||||||
|
integration.execute(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for issue notification' do
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
|
||||||
|
let(:data) { issue.to_hook_data(user) }
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_issue_notification'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for push notification' do
|
||||||
|
let(:data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_push_notification'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for deployment notification' do
|
||||||
|
let_it_be(:deployment) { create(:deployment, project: project, user: user) }
|
||||||
|
|
||||||
|
let(:data) { Gitlab::DataBuilder::Deployment.build(deployment, deployment.status, Time.current) }
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_deployment_notification'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for wiki_page notification' do
|
||||||
|
let_it_be(:wiki_page) do
|
||||||
|
create(:wiki_page, wiki: project.wiki, message: 'user created page: Awesome wiki_page')
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
# Skip this method that is not relevant to this test to prevent having
|
||||||
|
# to update project which is frozen
|
||||||
|
allow(project.wiki).to receive(:after_wiki_activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_wiki_page_notification'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for merge_request notification' do
|
||||||
|
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
|
||||||
|
|
||||||
|
let(:data) { merge_request.to_hook_data(user) }
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_merge_request_notification'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for note notification' do
|
||||||
|
let_it_be(:issue_note) { create(:note_on_issue, project: project, note: 'issue note') }
|
||||||
|
|
||||||
|
let(:data) { Gitlab::DataBuilder::Note.build(issue_note, user) }
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_note_notification'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for tag_push notification' do
|
||||||
|
let(:oldrev) { Gitlab::Git::BLANK_SHA }
|
||||||
|
let(:newrev) { '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b' } # gitlab-test: git rev-parse refs/tags/v1.1.0
|
||||||
|
let(:ref) { 'refs/tags/v1.1.0' }
|
||||||
|
let(:data) do
|
||||||
|
Git::TagHooksService.new(project, user, change: { oldrev: oldrev, newrev: newrev, ref: ref }).send(:push_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_tag_push_notification'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for confidential note notification' do
|
||||||
|
let_it_be(:confidential_issue_note) do
|
||||||
|
create(:note_on_issue, project: project, note: 'issue note', confidential: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:data) { Gitlab::DataBuilder::Note.build(confidential_issue_note, user) }
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_confidential_note_notification'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for confidential issue notification' do
|
||||||
|
let_it_be(:issue) { create(:issue, project: project, confidential: true) }
|
||||||
|
|
||||||
|
let(:data) { issue.to_hook_data(user) }
|
||||||
|
|
||||||
|
it_behaves_like 'increases the usage data counter', 'i_ecosystem_slack_service_confidential_issue_notification'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when hook data does not include a user' do
|
||||||
|
let(:data) { Gitlab::DataBuilder::Pipeline.build(create(:ci_pipeline, project: project)) }
|
||||||
|
|
||||||
|
it 'does not increase the usage data counter' do
|
||||||
|
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
|
||||||
|
|
||||||
|
integration.execute(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -90,6 +90,11 @@ RSpec.describe Namespaces::RootStatisticsWorker, '#perform' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'worker with data consistency',
|
||||||
|
described_class,
|
||||||
|
feature_flag: :root_statistics_worker_read_replica,
|
||||||
|
data_consistency: :sticky
|
||||||
|
|
||||||
it 'has the `until_executed` deduplicate strategy' do
|
it 'has the `until_executed` deduplicate strategy' do
|
||||||
expect(described_class.get_deduplicate_strategy).to eq(:until_executed)
|
expect(described_class.get_deduplicate_strategy).to eq(:until_executed)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue