Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1769b59b9f
commit
1e0d9b7134
|
@ -232,8 +232,6 @@ Rails/SaveBang:
|
||||||
- 'spec/initializers/fog_google_https_private_urls_spec.rb'
|
- 'spec/initializers/fog_google_https_private_urls_spec.rb'
|
||||||
- 'spec/lib/after_commit_queue_spec.rb'
|
- 'spec/lib/after_commit_queue_spec.rb'
|
||||||
- 'spec/lib/backup/manager_spec.rb'
|
- 'spec/lib/backup/manager_spec.rb'
|
||||||
- 'spec/lib/banzai/reference_parser/external_issue_parser_spec.rb'
|
|
||||||
- 'spec/lib/banzai/reference_redactor_spec.rb'
|
|
||||||
- 'spec/lib/gitlab/alerting/alert_spec.rb'
|
- 'spec/lib/gitlab/alerting/alert_spec.rb'
|
||||||
- 'spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb'
|
- 'spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb'
|
||||||
- 'spec/lib/gitlab/auth/ldap/user_spec.rb'
|
- 'spec/lib/gitlab/auth/ldap/user_spec.rb'
|
||||||
|
|
|
@ -640,11 +640,6 @@ Rails/WhereEquals:
|
||||||
Rails/WhereExists:
|
Rails/WhereExists:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
# Offense count: 21
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Rails/WhereNot:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 8
|
# Offense count: 8
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Security/YAMLLoad:
|
Security/YAMLLoad:
|
||||||
|
|
|
@ -131,7 +131,7 @@ module Ci
|
||||||
def by_yaml_errors(items)
|
def by_yaml_errors(items)
|
||||||
case Gitlab::Utils.to_boolean(params[:yaml_errors])
|
case Gitlab::Utils.to_boolean(params[:yaml_errors])
|
||||||
when true
|
when true
|
||||||
items.where("yaml_errors IS NOT NULL")
|
items.where.not(yaml_errors: nil)
|
||||||
when false
|
when false
|
||||||
items.where("yaml_errors IS NULL")
|
items.where("yaml_errors IS NULL")
|
||||||
else
|
else
|
||||||
|
|
|
@ -4,29 +4,29 @@ module ServicesHelper
|
||||||
def service_event_description(event)
|
def service_event_description(event)
|
||||||
case event
|
case event
|
||||||
when "push", "push_events"
|
when "push", "push_events"
|
||||||
s_("ProjectService|Event will be triggered by a push to the repository")
|
s_("ProjectService|Event triggered when someone pushes to the repository.")
|
||||||
when "tag_push", "tag_push_events"
|
when "tag_push", "tag_push_events"
|
||||||
s_("ProjectService|Event will be triggered when a new tag is pushed to the repository")
|
s_("ProjectService|Event triggered when a new tag is pushed to the repository.")
|
||||||
when "note", "note_events"
|
when "note", "note_events"
|
||||||
s_("ProjectService|Event will be triggered when someone adds a comment")
|
s_("ProjectService|Event triggered when someone adds a comment.")
|
||||||
when "confidential_note", "confidential_note_events"
|
when "confidential_note", "confidential_note_events"
|
||||||
s_("ProjectService|Event will be triggered when someone adds a comment on a confidential issue")
|
s_("ProjectService|Event triggered when someone adds a comment on a confidential issue.")
|
||||||
when "issue", "issue_events"
|
when "issue", "issue_events"
|
||||||
s_("ProjectService|Event will be triggered when an issue is created/updated/closed")
|
s_("ProjectService|Event triggered when an issue is created, updated, or closed.")
|
||||||
when "confidential_issue", "confidential_issue_events"
|
when "confidential_issue", "confidential_issue_events"
|
||||||
s_("ProjectService|Event will be triggered when a confidential issue is created/updated/closed")
|
s_("ProjectService|Event triggered when a confidential issue is created, updated, or closed.")
|
||||||
when "merge_request", "merge_request_events"
|
when "merge_request", "merge_request_events"
|
||||||
s_("ProjectService|Event will be triggered when a merge request is created/updated/merged")
|
s_("ProjectService|Event triggered when a merge request is created, updated, or merged.")
|
||||||
when "pipeline", "pipeline_events"
|
when "pipeline", "pipeline_events"
|
||||||
s_("ProjectService|Event will be triggered when a pipeline status changes")
|
s_("ProjectService|Event triggered when a pipeline status changes.")
|
||||||
when "wiki_page", "wiki_page_events"
|
when "wiki_page", "wiki_page_events"
|
||||||
s_("ProjectService|Event will be triggered when a wiki page is created/updated")
|
s_("ProjectService|Event triggered when a wiki page is created or updated.")
|
||||||
when "commit", "commit_events"
|
when "commit", "commit_events"
|
||||||
s_("ProjectService|Event will be triggered when a commit is created/updated")
|
s_("ProjectService|Event triggered when a commit is created or updated.")
|
||||||
when "deployment"
|
when "deployment"
|
||||||
s_("ProjectService|Event will be triggered when a deployment starts or finishes")
|
s_("ProjectService|Event triggered when a deployment starts or finishes.")
|
||||||
when "alert"
|
when "alert"
|
||||||
s_("ProjectService|Event will be triggered when a new, unique alert is recorded")
|
s_("ProjectService|Event triggered when a new, unique alert is recorded.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -93,8 +93,7 @@ module Ci
|
||||||
validates :ref, presence: true
|
validates :ref, presence: true
|
||||||
|
|
||||||
scope :not_interruptible, -> do
|
scope :not_interruptible, -> do
|
||||||
joins(:metadata).where('ci_builds_metadata.id NOT IN (?)',
|
joins(:metadata).where.not('ci_builds_metadata.id' => Ci::BuildMetadata.scoped_build.with_interruptible.select(:id))
|
||||||
Ci::BuildMetadata.scoped_build.with_interruptible.select(:id))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope :unstarted, -> { where(runner_id: nil) }
|
scope :unstarted, -> { where(runner_id: nil) }
|
||||||
|
|
|
@ -44,7 +44,7 @@ module Ci
|
||||||
next if position.present?
|
next if position.present?
|
||||||
|
|
||||||
self.position = statuses.select(:stage_idx)
|
self.position = statuses.select(:stage_idx)
|
||||||
.where('stage_idx IS NOT NULL')
|
.where.not(stage_idx: nil)
|
||||||
.group(:stage_idx)
|
.group(:stage_idx)
|
||||||
.order('COUNT(*) DESC')
|
.order('COUNT(*) DESC')
|
||||||
.first&.stage_idx.to_i
|
.first&.stage_idx.to_i
|
||||||
|
|
|
@ -16,7 +16,7 @@ module Clusters
|
||||||
|
|
||||||
model
|
model
|
||||||
.unscoped
|
.unscoped
|
||||||
.where('clusters.id IS NOT NULL')
|
.where.not('clusters.id' => nil)
|
||||||
.with
|
.with
|
||||||
.recursive(cte.to_arel)
|
.recursive(cte.to_arel)
|
||||||
.from(cte_alias)
|
.from(cte_alias)
|
||||||
|
|
|
@ -56,6 +56,7 @@ class CommitStatus < ApplicationRecord
|
||||||
scope :by_name, -> (name) { where(name: name) }
|
scope :by_name, -> (name) { where(name: name) }
|
||||||
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
|
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
|
||||||
scope :eager_load_pipeline, -> { eager_load(:pipeline, project: { namespace: :route }) }
|
scope :eager_load_pipeline, -> { eager_load(:pipeline, project: { namespace: :route }) }
|
||||||
|
scope :with_pipeline, -> { joins(:pipeline) }
|
||||||
|
|
||||||
scope :for_project_paths, -> (paths) do
|
scope :for_project_paths, -> (paths) do
|
||||||
where(project: Project.where_full_path_in(Array(paths)))
|
where(project: Project.where_full_path_in(Array(paths)))
|
||||||
|
@ -180,6 +181,7 @@ class CommitStatus < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
after_transition any => :failed do |commit_status|
|
after_transition any => :failed do |commit_status|
|
||||||
|
next if Feature.enabled?(:async_add_build_failure_todo, commit_status.project, default_enabled: :yaml)
|
||||||
next unless commit_status.project
|
next unless commit_status.project
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ServiceClass
|
# rubocop: disable CodeReuse/ServiceClass
|
||||||
|
|
|
@ -86,7 +86,7 @@ class Environment < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
scope :for_project, -> (project) { where(project_id: project) }
|
scope :for_project, -> (project) { where(project_id: project) }
|
||||||
scope :for_tier, -> (tier) { where(tier: tier).where('tier IS NOT NULL') }
|
scope :for_tier, -> (tier) { where(tier: tier).where.not(tier: nil) }
|
||||||
scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
|
scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
|
||||||
scope :unfoldered, -> { where(environment_type: nil) }
|
scope :unfoldered, -> { where(environment_type: nil) }
|
||||||
scope :with_rank, -> do
|
scope :with_rank, -> do
|
||||||
|
|
|
@ -519,7 +519,7 @@ class Project < ApplicationRecord
|
||||||
scope :with_packages, -> { joins(:packages) }
|
scope :with_packages, -> { joins(:packages) }
|
||||||
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
|
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
|
||||||
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
|
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
|
||||||
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
|
scope :joined, ->(user) { where.not(namespace_id: user.namespace_id) }
|
||||||
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
|
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
|
||||||
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
|
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
|
||||||
scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) }
|
scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) }
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class MicrosoftTeamsService < ChatNotificationService
|
class MicrosoftTeamsService < ChatNotificationService
|
||||||
def title
|
def title
|
||||||
'Microsoft Teams Notification'
|
'Microsoft Teams notifications'
|
||||||
end
|
end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
|
@ -14,13 +14,7 @@ class MicrosoftTeamsService < ChatNotificationService
|
||||||
end
|
end
|
||||||
|
|
||||||
def help
|
def help
|
||||||
'This service sends notifications about projects events to Microsoft Teams channels.<br />
|
'<p>Use this service to send notifications about events in GitLab projects to your Microsoft Teams channels. <a href="https://docs.gitlab.com/ee/user/project/integrations/microsoft_teams.html">How do I configure this integration?</a></p>'
|
||||||
To set up this service:
|
|
||||||
<ol>
|
|
||||||
<li><a href="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors/connectors-using#setting-up-a-custom-incoming-webhook">Setup a custom Incoming Webhook using Office 365 Connectors For Microsoft Teams</a>.</li>
|
|
||||||
<li>Paste the <strong>Webhook URL</strong> into the field below.</li>
|
|
||||||
<li>Select events below to enable notifications.</li>
|
|
||||||
</ol>'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def webhook_placeholder
|
def webhook_placeholder
|
||||||
|
@ -40,8 +34,8 @@ class MicrosoftTeamsService < ChatNotificationService
|
||||||
|
|
||||||
def default_fields
|
def default_fields
|
||||||
[
|
[
|
||||||
{ type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
|
{ type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" },
|
||||||
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
|
{ type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'If selected, successful pipelines do not trigger a notification event.' },
|
||||||
{ type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
|
{ type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -353,12 +353,7 @@ class User < ApplicationRecord
|
||||||
# this state transition object in order to do a rollback.
|
# this state transition object in order to do a rollback.
|
||||||
# For this reason the tradeoff is to disable this cop.
|
# For this reason the tradeoff is to disable this cop.
|
||||||
after_transition any => :blocked do |user|
|
after_transition any => :blocked do |user|
|
||||||
if Feature.enabled?(:abort_user_pipelines_on_block, user)
|
Ci::AbortPipelinesService.new.execute(user.pipelines)
|
||||||
Ci::AbortPipelinesService.new.execute(user.pipelines)
|
|
||||||
else
|
|
||||||
Ci::CancelUserPipelinesService.new.execute(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
Ci::DisableUserPipelineSchedulesService.new.execute(user)
|
Ci::DisableUserPipelineSchedulesService.new.execute(user)
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ServiceClass
|
# rubocop: enable CodeReuse/ServiceClass
|
||||||
|
@ -1041,7 +1036,7 @@ class User < ApplicationRecord
|
||||||
[
|
[
|
||||||
Project.where(namespace: namespace),
|
Project.where(namespace: namespace),
|
||||||
Project.joins(:project_authorizations)
|
Project.joins(:project_authorizations)
|
||||||
.where("projects.namespace_id <> ?", namespace.id)
|
.where.not('projects.namespace_id' => namespace.id)
|
||||||
.where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER })
|
.where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER })
|
||||||
],
|
],
|
||||||
remove_duplicates: false
|
remove_duplicates: false
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Ci
|
|
||||||
class CancelUserPipelinesService
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
|
||||||
# This is a bug with CodeReuse/ActiveRecord cop
|
|
||||||
# https://gitlab.com/gitlab-org/gitlab/issues/32332
|
|
||||||
def execute(user)
|
|
||||||
# TODO: fix N+1 queries https://gitlab.com/gitlab-org/gitlab/-/issues/300685
|
|
||||||
user.pipelines.cancelable.find_each(&:cancel_running)
|
|
||||||
|
|
||||||
ServiceResponse.success(message: 'Pipeline canceled')
|
|
||||||
rescue ActiveRecord::StaleObjectError
|
|
||||||
ServiceResponse.error(message: 'Error canceling pipeline')
|
|
||||||
end
|
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -21,7 +21,7 @@ module Ci
|
||||||
|
|
||||||
Gitlab::OptimisticLocking.retry_lock(new_build, name: 'retry_build', &:enqueue)
|
Gitlab::OptimisticLocking.retry_lock(new_build, name: 'retry_build', &:enqueue)
|
||||||
|
|
||||||
MergeRequests::AddTodoWhenBuildFailsService
|
::MergeRequests::AddTodoWhenBuildFailsService
|
||||||
.new(project, current_user)
|
.new(project, current_user)
|
||||||
.close(new_build)
|
.close(new_build)
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,7 +28,7 @@ module Ci
|
||||||
|
|
||||||
pipeline.reset_ancestor_bridges!
|
pipeline.reset_ancestor_bridges!
|
||||||
|
|
||||||
MergeRequests::AddTodoWhenBuildFailsService
|
::MergeRequests::AddTodoWhenBuildFailsService
|
||||||
.new(project, current_user)
|
.new(project, current_user)
|
||||||
.close_all(pipeline)
|
.close_all(pipeline)
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ module MergeRequests
|
||||||
|
|
||||||
def pipeline_merge_requests(pipeline)
|
def pipeline_merge_requests(pipeline)
|
||||||
pipeline.all_merge_requests.opened.each do |merge_request|
|
pipeline.all_merge_requests.opened.each do |merge_request|
|
||||||
next unless pipeline == merge_request.head_pipeline
|
next unless pipeline.id == merge_request.head_pipeline_id
|
||||||
|
|
||||||
yield merge_request
|
yield merge_request
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,7 +27,7 @@ module Namespaces
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
raise NotImplementedError, "Track #{track} not defined" unless TRACKS.key?(track)
|
raise ArgumentError, "Track #{track} not defined" unless TRACKS.key?(track)
|
||||||
|
|
||||||
groups_for_track.each_batch do |groups|
|
groups_for_track.each_batch do |groups|
|
||||||
groups.each do |group|
|
groups.each do |group|
|
||||||
|
|
|
@ -13,7 +13,7 @@ module Todos
|
||||||
|
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def without_authorized(items)
|
def without_authorized(items)
|
||||||
items.where('todos.user_id NOT IN (?)', authorized_users)
|
items.where.not('todos.user_id' => authorized_users)
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ module Todos
|
||||||
items = Todo.where(project_id: project_id)
|
items = Todo.where(project_id: project_id)
|
||||||
items = items.where(user_id: user_id) if user_id
|
items = items.where(user_id: user_id) if user_id
|
||||||
|
|
||||||
items.where('user_id NOT IN (?)', authorized_users)
|
items.where.not(user_id: authorized_users)
|
||||||
.where(target_type: target_types)
|
.where(target_type: target_types)
|
||||||
.delete_all
|
.delete_all
|
||||||
end
|
end
|
||||||
|
|
|
@ -1235,6 +1235,14 @@
|
||||||
:weight: 3
|
:weight: 3
|
||||||
:idempotent:
|
:idempotent:
|
||||||
:tags: []
|
:tags: []
|
||||||
|
- :name: pipeline_default:ci_merge_requests_add_todo_when_build_fails
|
||||||
|
:feature_category: :continuous_integration
|
||||||
|
:has_external_dependencies:
|
||||||
|
:urgency: :low
|
||||||
|
:resource_boundary: :unknown
|
||||||
|
:weight: 3
|
||||||
|
:idempotent: true
|
||||||
|
:tags: []
|
||||||
- :name: pipeline_default:ci_pipeline_bridge_status
|
- :name: pipeline_default:ci_pipeline_bridge_status
|
||||||
:feature_category: :continuous_integration
|
:feature_category: :continuous_integration
|
||||||
:has_external_dependencies:
|
:has_external_dependencies:
|
||||||
|
|
|
@ -37,6 +37,10 @@ class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
ExpirePipelineCacheWorker.perform_async(build.pipeline_id)
|
ExpirePipelineCacheWorker.perform_async(build.pipeline_id)
|
||||||
ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
|
ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
|
||||||
|
|
||||||
|
if build.failed? && Feature.enabled?(:async_add_build_failure_todo, build.project, default_enabled: :yaml)
|
||||||
|
::Ci::MergeRequests::AddTodoWhenBuildFailsWorker.perform_async(build.id)
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# We want to delay sending a build trace to object storage operation to
|
# We want to delay sending a build trace to object storage operation to
|
||||||
# validate that this fixes a race condition between this and flushing live
|
# validate that this fixes a race condition between this and flushing live
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module Ci
|
||||||
|
module MergeRequests
|
||||||
|
class AddTodoWhenBuildFailsWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
include PipelineQueue
|
||||||
|
|
||||||
|
urgency :low
|
||||||
|
idempotent!
|
||||||
|
|
||||||
|
def perform(job_id)
|
||||||
|
job = ::CommitStatus.with_pipeline.find_by_id(job_id)
|
||||||
|
project = job&.project
|
||||||
|
|
||||||
|
return unless job && project
|
||||||
|
|
||||||
|
::MergeRequests::AddTodoWhenBuildFailsService.new(job.project, nil).execute(job)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Bulk-abort user pipelines on block
|
||||||
|
merge_request: 57801
|
||||||
|
author:
|
||||||
|
type: performance
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Backfill traversal_ids for gitlab-org .com
|
||||||
|
merge_request: 57075
|
||||||
|
author:
|
||||||
|
type: performance
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix Rails/SaveBang Rubocop offenses for banzai modules
|
||||||
|
merge_request: 58108
|
||||||
|
author: Huzaifa Iftikhar @huzaifaiftikhar
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Resolves rubocop offenses Rails/WhereNot
|
||||||
|
merge_request: 58062
|
||||||
|
author: Shubham Kumar (@imskr)
|
||||||
|
type: fixed
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
name: abort_user_pipelines_on_block
|
name: async_add_build_failure_todo
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56126
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57490/diffs
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/324045
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326726
|
||||||
milestone: '13.10'
|
milestone: '13.11'
|
||||||
type: development
|
type: development
|
||||||
group: group::memory
|
group: group::continuous integration
|
||||||
default_enabled: false
|
default_enabled: false
|
|
@ -5,6 +5,10 @@
|
||||||
"key_path": {
|
"key_path": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"name": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"pattern": "^([a-z]+_)*[a-z]+$"
|
||||||
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,7 +17,7 @@ class BackfillDeploymentClustersFromDeployments < ActiveRecord::Migration[6.0]
|
||||||
class Deployment < ActiveRecord::Base
|
class Deployment < ActiveRecord::Base
|
||||||
include EachBatch
|
include EachBatch
|
||||||
|
|
||||||
default_scope { where('cluster_id IS NOT NULL') } # rubocop:disable Cop/DefaultScope
|
default_scope { where.not(cluster_id: nil) } # rubocop:disable Cop/DefaultScope
|
||||||
|
|
||||||
self.table_name = 'deployments'
|
self.table_name = 'deployments'
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,11 +18,11 @@ class CreateMissingVulnerabilitiesIssueLinks < ActiveRecord::Migration[6.0]
|
||||||
disable_ddl_transaction!
|
disable_ddl_transaction!
|
||||||
|
|
||||||
def up
|
def up
|
||||||
VulnerabilitiesFeedback.where('issue_id IS NOT NULL').each_batch do |relation|
|
VulnerabilitiesFeedback.where.not(issue_id: nil).each_batch do |relation|
|
||||||
timestamp = Time.now
|
timestamp = Time.now
|
||||||
issue_links = relation
|
issue_links = relation
|
||||||
.joins("JOIN vulnerability_occurrences vo ON vo.project_id = vulnerability_feedback.project_id AND vo.report_type = vulnerability_feedback.category AND encode(vo.project_fingerprint, 'hex') = vulnerability_feedback.project_fingerprint")
|
.joins("JOIN vulnerability_occurrences vo ON vo.project_id = vulnerability_feedback.project_id AND vo.report_type = vulnerability_feedback.category AND encode(vo.project_fingerprint, 'hex') = vulnerability_feedback.project_fingerprint")
|
||||||
.where('vo.vulnerability_id IS NOT NULL')
|
.where.not('vo.vulnerability_id' => nil)
|
||||||
.pluck(:vulnerability_id, :issue_id)
|
.pluck(:vulnerability_id, :issue_id)
|
||||||
.map do |v_id, i_id|
|
.map do |v_id, i_id|
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class SetTraversalIdsForGitlabOrgGroupCom < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def up
|
||||||
|
return unless Gitlab.com?
|
||||||
|
|
||||||
|
# namespace ID 9970 is gitlab-org on .com
|
||||||
|
with_lock_retries do
|
||||||
|
execute(<<~SQL)
|
||||||
|
UPDATE
|
||||||
|
namespaces
|
||||||
|
SET
|
||||||
|
traversal_ids = cte.traversal_ids
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
WITH RECURSIVE cte(id, traversal_ids, cycle) AS (
|
||||||
|
VALUES
|
||||||
|
(9970, ARRAY[9970], false)
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
n.id,
|
||||||
|
cte.traversal_ids || n.id,
|
||||||
|
n.id = ANY(cte.traversal_ids)
|
||||||
|
FROM
|
||||||
|
namespaces n,
|
||||||
|
cte
|
||||||
|
WHERE
|
||||||
|
n.parent_id = cte.id
|
||||||
|
AND NOT cycle
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
traversal_ids
|
||||||
|
FROM
|
||||||
|
cte FOR
|
||||||
|
UPDATE
|
||||||
|
) as cte
|
||||||
|
WHERE
|
||||||
|
namespaces.id = cte.id
|
||||||
|
AND namespaces.traversal_ids <> cte.traversal_ids
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
return unless Gitlab.com?
|
||||||
|
|
||||||
|
# namespace ID 9970 is gitlab-org on .com
|
||||||
|
with_lock_retries do
|
||||||
|
execute(<<~SQL)
|
||||||
|
UPDATE
|
||||||
|
namespaces
|
||||||
|
SET
|
||||||
|
traversal_ids = '{}'
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
WITH RECURSIVE cte(id, traversal_ids, cycle) AS (
|
||||||
|
VALUES
|
||||||
|
(9970, ARRAY[9970], false)
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
n.id,
|
||||||
|
cte.traversal_ids || n.id,
|
||||||
|
n.id = ANY(cte.traversal_ids)
|
||||||
|
FROM
|
||||||
|
namespaces n,
|
||||||
|
cte
|
||||||
|
WHERE
|
||||||
|
n.parent_id = cte.id
|
||||||
|
AND NOT cycle
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
traversal_ids
|
||||||
|
FROM
|
||||||
|
cte FOR
|
||||||
|
UPDATE
|
||||||
|
) as cte
|
||||||
|
WHERE
|
||||||
|
namespaces.id = cte.id
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
2387c8a5516aaf8bcf44c9bad45bfc9844d68d2c03330f67773ce046b21a7a6c
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB |
|
@ -6,49 +6,55 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
||||||
|
|
||||||
# Microsoft Teams service **(FREE)**
|
# Microsoft Teams service **(FREE)**
|
||||||
|
|
||||||
## On Microsoft Teams
|
You can integrate Microsoft Teams with GitLab, and display notifications about GitLab projects
|
||||||
|
in Microsoft Teams. To integrate the services, you must:
|
||||||
|
|
||||||
To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft
|
1. [Configure Microsoft Teams](#configure-microsoft-teams) to enable a webhook
|
||||||
Teams by following the steps below:
|
to listen for changes.
|
||||||
|
1. [Configure your GitLab project](#configure-your-gitlab-project) to push notifications
|
||||||
|
to the Microsoft Teams webhook.
|
||||||
|
|
||||||
1. Search for "incoming webhook" on the search bar in Microsoft Teams and select the
|
## Configure Microsoft Teams
|
||||||
**Incoming Webhook** item.
|
|
||||||
|
To configure Microsoft Teams to listen for notifications from GitLab:
|
||||||
|
|
||||||
|
1. In Microsoft Teams, search for "incoming webhook" in the search bar, and select the
|
||||||
|
**Incoming Webhook** item:
|
||||||
|
|
||||||
![Select Incoming Webhook](img/microsoft_teams_select_incoming_webhook.png)
|
![Select Incoming Webhook](img/microsoft_teams_select_incoming_webhook.png)
|
||||||
|
|
||||||
1. Click the **Add to a team** button.
|
1. Select **Add to a team**.
|
||||||
1. Select the team and channel you want to add the integration to.
|
1. Select the team and channel you want to add the integration to.
|
||||||
1. Add a name for the webhook. The name is displayed next to every message that
|
1. Add a name for the webhook. The name is displayed next to every message that
|
||||||
comes in through the webhook.
|
comes in through the webhook.
|
||||||
1. Copy the webhook URL for the next steps.
|
1. Copy the webhook URL, as you need it to configure GitLab.
|
||||||
|
|
||||||
Learn more about
|
## Configure your GitLab project
|
||||||
[setting up an incoming webhook on Microsoft Teams](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#setting-up-a-custom-incoming-webhook).
|
|
||||||
|
|
||||||
## On GitLab
|
After you configure Microsoft Teams to receive notifications, you must configure
|
||||||
|
GitLab to send the notifications:
|
||||||
|
|
||||||
After you set up Microsoft Teams, it's time to set up GitLab.
|
1. Sign in to GitLab as a user with [Administrator](../../permissions.md) and go
|
||||||
|
to your project's page.
|
||||||
|
1. Go to **Settings > Integrations** and select **Microsoft Teams Notification**.
|
||||||
|
1. Select **Active** to enable the integration.
|
||||||
|
1. Select the check box next to each **Trigger** to enable:
|
||||||
|
- Push
|
||||||
|
- Issue
|
||||||
|
- Confidential issue
|
||||||
|
- Merge request
|
||||||
|
- Note
|
||||||
|
- Confidential note
|
||||||
|
- Tag push
|
||||||
|
- Pipeline - If you enable this trigger, you can also select **Notify only broken pipelines** to be notified only about failed pipelines.
|
||||||
|
- Wiki page
|
||||||
|
1. In **Webhook**, paste the URL you copied when you
|
||||||
|
[configured Microsoft Teams](#configure-microsoft-teams).
|
||||||
|
1. (Optional) If you enabled the pipeline trigger, you can select the
|
||||||
|
**Notify only broken pipelines** check box to push notifications only when pipelines break.
|
||||||
|
1. Select the branches you want to send notifications for.
|
||||||
|
1. Click **Save changes**.
|
||||||
|
|
||||||
Navigate to the [Integrations page](overview.md#accessing-integrations)
|
## Resources
|
||||||
and select the **Microsoft Teams Notification** service to configure it.
|
|
||||||
There, you see a checkbox with the following events that can be triggered:
|
|
||||||
|
|
||||||
- Push
|
- [Setting up an incoming webhook on Microsoft Teams](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#setting-up-a-custom-incoming-webhook).
|
||||||
- Issue
|
|
||||||
- Confidential issue
|
|
||||||
- Merge request
|
|
||||||
- Note
|
|
||||||
- Tag push
|
|
||||||
- Pipeline
|
|
||||||
- Wiki page
|
|
||||||
|
|
||||||
At the end fill in your Microsoft Teams details:
|
|
||||||
|
|
||||||
| Field | Description |
|
|
||||||
| ----- | ----------- |
|
|
||||||
| **Webhook** | The incoming webhook URL which you have to set up on Microsoft Teams. |
|
|
||||||
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
|
|
||||||
|
|
||||||
After you are all done, click **Save changes** for the changes to take effect.
|
|
||||||
|
|
||||||
![Microsoft Teams configuration](img/microsoft_teams_configuration.png)
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Gitlab
|
||||||
MergeRequest
|
MergeRequest
|
||||||
.where(merge_request_assignees_not_exists_clause)
|
.where(merge_request_assignees_not_exists_clause)
|
||||||
.where(id: from_id..to_id)
|
.where(id: from_id..to_id)
|
||||||
.where('assignee_id IS NOT NULL')
|
.where.not(assignee_id: nil)
|
||||||
.select(:id, :assignee_id)
|
.select(:id, :assignee_id)
|
||||||
.to_sql
|
.to_sql
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ module Gitlab
|
||||||
joins(:user)
|
joins(:user)
|
||||||
.merge(UserModel.active)
|
.merge(UserModel.active)
|
||||||
.where(id: (start_id..stop_id))
|
.where(id: (start_id..stop_id))
|
||||||
.where('emails.confirmed_at IS NOT NULL')
|
.where.not('emails.confirmed_at' => nil)
|
||||||
.where('emails.confirmed_at = users.confirmed_at')
|
.where('emails.confirmed_at = users.confirmed_at')
|
||||||
.where('emails.email <> users.email')
|
.where('emails.email <> users.email')
|
||||||
.where('NOT EXISTS (SELECT 1 FROM user_synced_attributes_metadata WHERE user_id=users.id AND email_synced IS true)')
|
.where('NOT EXISTS (SELECT 1 FROM user_synced_attributes_metadata WHERE user_id=users.id AND email_synced IS true)')
|
||||||
|
|
|
@ -76,7 +76,7 @@ module Gitlab
|
||||||
def project_uploads_except_avatar(avatar_path)
|
def project_uploads_except_avatar(avatar_path)
|
||||||
return @project.uploads unless avatar_path
|
return @project.uploads unless avatar_path
|
||||||
|
|
||||||
@project.uploads.where("path != ?", avatar_path)
|
@project.uploads.where.not(path: avatar_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def download_and_copy(upload)
|
def download_and_copy(upload)
|
||||||
|
|
|
@ -24110,40 +24110,40 @@ msgstr ""
|
||||||
msgid "ProjectService|Enter new password"
|
msgid "ProjectService|Enter new password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered by a push to the repository"
|
msgid "ProjectService|Event triggered when a commit is created or updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when a commit is created/updated"
|
msgid "ProjectService|Event triggered when a confidential issue is created, updated, or closed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when a confidential issue is created/updated/closed"
|
msgid "ProjectService|Event triggered when a deployment starts or finishes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when a deployment starts or finishes"
|
msgid "ProjectService|Event triggered when a merge request is created, updated, or merged."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when a merge request is created/updated/merged"
|
msgid "ProjectService|Event triggered when a new tag is pushed to the repository."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when a new tag is pushed to the repository"
|
msgid "ProjectService|Event triggered when a new, unique alert is recorded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when a new, unique alert is recorded"
|
msgid "ProjectService|Event triggered when a pipeline status changes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when a pipeline status changes"
|
msgid "ProjectService|Event triggered when a wiki page is created or updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when a wiki page is created/updated"
|
msgid "ProjectService|Event triggered when an issue is created, updated, or closed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when an issue is created/updated/closed"
|
msgid "ProjectService|Event triggered when someone adds a comment on a confidential issue."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when someone adds a comment"
|
msgid "ProjectService|Event triggered when someone adds a comment."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Event will be triggered when someone adds a comment on a confidential issue"
|
msgid "ProjectService|Event triggered when someone pushes to the repository."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ProjectService|Issue URL"
|
msgid "ProjectService|Issue URL"
|
||||||
|
|
|
@ -21,7 +21,7 @@ RSpec.describe Banzai::ReferenceParser::ExternalIssueParser do
|
||||||
|
|
||||||
levels.each do |level|
|
levels.each do |level|
|
||||||
it "creates reference when the feature is #{level}" do
|
it "creates reference when the feature is #{level}" do
|
||||||
project.project_feature.update(issues_access_level: level)
|
project.project_feature.update!(issues_access_level: level)
|
||||||
|
|
||||||
visible_nodes = subject.nodes_visible_to_user(user, [link])
|
visible_nodes = subject.nodes_visible_to_user(user, [link])
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ RSpec.describe Banzai::ReferenceRedactor do
|
||||||
let(:redactor) { described_class.new(Banzai::RenderContext.new(project, user)) }
|
let(:redactor) { described_class.new(Banzai::RenderContext.new(project, user)) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.update(pending_delete: true)
|
project.update!(pending_delete: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'redacts an issue attached' do
|
it 'redacts an issue attached' do
|
||||||
|
|
|
@ -15,7 +15,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::StageEvents::CodeStageStart do
|
||||||
|
|
||||||
other_merge_request = create(:merge_request, source_project: project, source_branch: 'a', target_branch: 'master')
|
other_merge_request = create(:merge_request, source_project: project, source_branch: 'a', target_branch: 'master')
|
||||||
|
|
||||||
records = subject.apply_query_customization(MergeRequest.all).where('merge_requests_closing_issues.issue_id IS NOT NULL')
|
records = subject.apply_query_customization(MergeRequest.all).where.not('merge_requests_closing_issues.issue_id' => nil)
|
||||||
expect(records).to eq([merge_request])
|
expect(records).to eq([merge_request])
|
||||||
expect(records).not_to include(other_merge_request)
|
expect(records).not_to include(other_merge_request)
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,7 +16,8 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
|
||||||
time_frame: 'none',
|
time_frame: 'none',
|
||||||
data_source: 'database',
|
data_source: 'database',
|
||||||
distribution: %w(ee ce),
|
distribution: %w(ee ce),
|
||||||
tier: %w(free starter premium ultimate bronze silver gold)
|
tier: %w(free starter premium ultimate bronze silver gold),
|
||||||
|
name: 'count_boards'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
|
||||||
:distribution | nil
|
:distribution | nil
|
||||||
:distribution | 'test'
|
:distribution | 'test'
|
||||||
:tier | %w(test ee)
|
:tier | %w(test ee)
|
||||||
|
:name | 'count_<adjective_describing>_boards'
|
||||||
end
|
end
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
|
|
|
@ -25,7 +25,7 @@ RSpec.describe CleanUpNoteableIdForNotesOnCommits do
|
||||||
end
|
end
|
||||||
|
|
||||||
def dirty_notes_on_commits
|
def dirty_notes_on_commits
|
||||||
notes.where(noteable_type: 'Commit').where('noteable_id IS NOT NULL')
|
notes.where(noteable_type: 'Commit').where.not(noteable_id: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def other_notes
|
def other_notes
|
||||||
|
|
|
@ -15,6 +15,6 @@ RSpec.describe MigrateBotTypeToUserType, :migration do
|
||||||
|
|
||||||
migrate!
|
migrate!
|
||||||
|
|
||||||
expect(users.where('user_type IS NOT NULL').map(&:user_type)).to match_array([1, 2, 3])
|
expect(users.where.not(user_type: nil).map(&:user_type)).to match_array([1, 2, 3])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3582,10 +3582,10 @@ RSpec.describe Ci::Build do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'state transition when build fails' do
|
describe 'state transition when build fails' do
|
||||||
let(:service) { MergeRequests::AddTodoWhenBuildFailsService.new(project, user) }
|
let(:service) { ::MergeRequests::AddTodoWhenBuildFailsService.new(project, user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(MergeRequests::AddTodoWhenBuildFailsService).to receive(:new).and_return(service)
|
allow(::MergeRequests::AddTodoWhenBuildFailsService).to receive(:new).and_return(service)
|
||||||
allow(service).to receive(:close)
|
allow(service).to receive(:close)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -3670,15 +3670,42 @@ RSpec.describe Ci::Build do
|
||||||
subject.drop!
|
subject.drop!
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a todo' do
|
context 'when async_add_build_failure_todo flag enabled' do
|
||||||
project.add_developer(user)
|
it 'creates a todo async', :sidekiq_inline do
|
||||||
|
project.add_developer(user)
|
||||||
|
|
||||||
expect_next_instance_of(TodoService) do |todo_service|
|
expect_next_instance_of(TodoService) do |todo_service|
|
||||||
expect(todo_service)
|
expect(todo_service)
|
||||||
.to receive(:merge_request_build_failed).with(merge_request)
|
.to receive(:merge_request_build_failed).with(merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject.drop!
|
||||||
end
|
end
|
||||||
|
|
||||||
subject.drop!
|
it 'does not create a sync todo' do
|
||||||
|
project.add_developer(user)
|
||||||
|
|
||||||
|
expect(TodoService).not_to receive(:new)
|
||||||
|
|
||||||
|
subject.drop!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when async_add_build_failure_todo flag disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(async_add_build_failure_todo: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a todo sync' do
|
||||||
|
project.add_developer(user)
|
||||||
|
|
||||||
|
expect_next_instance_of(TodoService) do |todo_service|
|
||||||
|
expect(todo_service)
|
||||||
|
.to receive(:merge_request_build_failed).with(merge_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject.drop!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1790,28 +1790,14 @@ RSpec.describe User do
|
||||||
|
|
||||||
context 'when user has running CI pipelines' do
|
context 'when user has running CI pipelines' do
|
||||||
let(:service) { double }
|
let(:service) { double }
|
||||||
|
let(:pipelines) { build_list(:ci_pipeline, 3, :running) }
|
||||||
|
|
||||||
context 'with abort_user_pipelines_on_block feature enabled' do
|
it 'aborts all running pipelines and related jobs' do
|
||||||
let(:pipelines) { build_list(:ci_pipeline, 3, :running) }
|
expect(user).to receive(:pipelines).and_return(pipelines)
|
||||||
|
expect(Ci::AbortPipelinesService).to receive(:new).and_return(service)
|
||||||
|
expect(service).to receive(:execute).with(pipelines)
|
||||||
|
|
||||||
it 'aborts all running pipelines and related jobs' do
|
user.block
|
||||||
stub_feature_flags(abort_user_pipelines_on_block: true)
|
|
||||||
expect(user).to receive(:pipelines).and_return(pipelines)
|
|
||||||
expect(Ci::AbortPipelinesService).to receive(:new).and_return(service)
|
|
||||||
expect(service).to receive(:execute).with(pipelines)
|
|
||||||
|
|
||||||
user.block
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with abort_user_pipelines_on_block feature disabled' do
|
|
||||||
it 'cancels all running pipelines and related jobs' do
|
|
||||||
stub_feature_flags(abort_user_pipelines_on_block: false)
|
|
||||||
expect(Ci::CancelUserPipelinesService).to receive(:new).and_return(service)
|
|
||||||
expect(service).to receive(:execute).with(user)
|
|
||||||
|
|
||||||
user.block
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ RSpec.describe ServiceEventEntity do
|
||||||
let(:event) { 'push' }
|
let(:event) { 'push' }
|
||||||
|
|
||||||
it 'exposes correct attributes' do
|
it 'exposes correct attributes' do
|
||||||
expect(subject[:description]).to eq('Event will be triggered by a push to the repository')
|
expect(subject[:description]).to eq('Event triggered when someone pushes to the repository.')
|
||||||
expect(subject[:name]).to eq('push_events')
|
expect(subject[:name]).to eq('push_events')
|
||||||
expect(subject[:title]).to eq('push')
|
expect(subject[:title]).to eq('push')
|
||||||
expect(subject[:value]).to be(true)
|
expect(subject[:value]).to be(true)
|
||||||
|
@ -29,7 +29,7 @@ RSpec.describe ServiceEventEntity do
|
||||||
let(:event) { 'note' }
|
let(:event) { 'note' }
|
||||||
|
|
||||||
it 'exposes correct attributes' do
|
it 'exposes correct attributes' do
|
||||||
expect(subject[:description]).to eq('Event will be triggered when someone adds a comment')
|
expect(subject[:description]).to eq('Event triggered when someone adds a comment.')
|
||||||
expect(subject[:name]).to eq('note_events')
|
expect(subject[:name]).to eq('note_events')
|
||||||
expect(subject[:title]).to eq('note')
|
expect(subject[:title]).to eq('note')
|
||||||
expect(subject[:value]).to eq(false)
|
expect(subject[:value]).to eq(false)
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe Ci::CancelUserPipelinesService do
|
|
||||||
describe '#execute' do
|
|
||||||
let(:user) { create(:user) }
|
|
||||||
|
|
||||||
subject { described_class.new.execute(user) }
|
|
||||||
|
|
||||||
context 'when user has running CI pipelines' do
|
|
||||||
let(:pipeline) { create(:ci_pipeline, :running, user: user) }
|
|
||||||
let!(:build) { create(:ci_build, :running, pipeline: pipeline) }
|
|
||||||
|
|
||||||
it 'cancels all running pipelines and related jobs', :sidekiq_might_not_need_inline do
|
|
||||||
subject
|
|
||||||
|
|
||||||
expect(pipeline.reload).to be_canceled
|
|
||||||
expect(build.reload).to be_canceled
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when an error ocurrs' do
|
|
||||||
it 'raises a service level error' do
|
|
||||||
service = double(execute: ServiceResponse.error(message: 'Error canceling pipeline'))
|
|
||||||
allow(::Ci::CancelUserPipelinesService).to receive(:new).and_return(service)
|
|
||||||
|
|
||||||
result = subject
|
|
||||||
|
|
||||||
expect(result).to be_a(ServiceResponse)
|
|
||||||
expect(result).to be_error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -181,7 +181,7 @@ RSpec.describe Ci::RetryBuildService do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'resolves todos for old build that failed' do
|
it 'resolves todos for old build that failed' do
|
||||||
expect(MergeRequests::AddTodoWhenBuildFailsService)
|
expect(::MergeRequests::AddTodoWhenBuildFailsService)
|
||||||
.to receive_message_chain(:new, :close)
|
.to receive_message_chain(:new, :close)
|
||||||
|
|
||||||
service.execute(build)
|
service.execute(build)
|
||||||
|
|
|
@ -272,7 +272,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'closes all todos about failed jobs for pipeline' do
|
it 'closes all todos about failed jobs for pipeline' do
|
||||||
expect(MergeRequests::AddTodoWhenBuildFailsService)
|
expect(::MergeRequests::AddTodoWhenBuildFailsService)
|
||||||
.to receive_message_chain(:new, :close_all)
|
.to receive_message_chain(:new, :close_all)
|
||||||
|
|
||||||
service.execute(pipeline)
|
service.execute(pipeline)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe MergeRequests::AddTodoWhenBuildFailsService do
|
RSpec.describe ::MergeRequests::AddTodoWhenBuildFailsService do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
let(:project) { create(:project, :repository) }
|
let(:project) { create(:project, :repository) }
|
||||||
let(:sha) { '1234567890abcdef1234567890abcdef12345678' }
|
let(:sha) { '1234567890abcdef1234567890abcdef12345678' }
|
||||||
|
@ -24,8 +24,8 @@ RSpec.describe MergeRequests::AddTodoWhenBuildFailsService do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(MergeRequest)
|
allow_any_instance_of(MergeRequest)
|
||||||
.to receive(:head_pipeline)
|
.to receive(:head_pipeline_id)
|
||||||
.and_return(pipeline)
|
.and_return(pipeline.id)
|
||||||
|
|
||||||
allow(service).to receive(:todo_service).and_return(todo_service)
|
allow(service).to receive(:todo_service).and_return(todo_service)
|
||||||
end
|
end
|
||||||
|
|
|
@ -218,7 +218,7 @@ RSpec.describe Namespaces::InProductMarketingEmailsService, '#execute' do
|
||||||
stub_const("#{described_class}::TRACKS", { bar: :git_write })
|
stub_const("#{described_class}::TRACKS", { bar: :git_write })
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect { subject }.to raise_error(NotImplementedError, 'Track foo not defined') }
|
it { expect { subject }.to raise_error(ArgumentError, 'Track foo not defined') }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when group is a sub-group' do
|
context 'when group is a sub-group' do
|
||||||
|
|
|
@ -6,10 +6,8 @@ RSpec.describe BuildFinishedWorker do
|
||||||
subject { described_class.new.perform(build.id) }
|
subject { described_class.new.perform(build.id) }
|
||||||
|
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
let(:build) { create(:ci_build, :success, pipeline: create(:ci_pipeline)) }
|
|
||||||
|
|
||||||
context 'when build exists' do
|
context 'when build exists' do
|
||||||
let!(:build) { create(:ci_build) }
|
let_it_be(:build) { create(:ci_build, :success, pipeline: create(:ci_pipeline)) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
expect(Ci::Build).to receive(:find_by).with(id: build.id).and_return(build)
|
expect(Ci::Build).to receive(:find_by).with(id: build.id).and_return(build)
|
||||||
|
@ -30,6 +28,42 @@ RSpec.describe BuildFinishedWorker do
|
||||||
|
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when build is failed' do
|
||||||
|
before do
|
||||||
|
build.update!(status: :failed)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds a todo' do
|
||||||
|
expect(::Ci::MergeRequests::AddTodoWhenBuildFailsWorker).to receive(:perform_async)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when async_add_build_failure_todo disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(async_add_build_failure_todo: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not add a todo' do
|
||||||
|
expect(::Ci::MergeRequests::AddTodoWhenBuildFailsWorker).not_to receive(:perform_async)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when build has a chat' do
|
||||||
|
before do
|
||||||
|
build.pipeline.update!(source: :chat)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'schedules a ChatNotification job' do
|
||||||
|
expect(ChatNotificationWorker).to receive(:perform_async).with(build.id)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when build does not exist' do
|
context 'when build does not exist' do
|
||||||
|
@ -38,15 +72,5 @@ RSpec.describe BuildFinishedWorker do
|
||||||
.not_to raise_error
|
.not_to raise_error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when build has a chat' do
|
|
||||||
let(:build) { create(:ci_build, :success, pipeline: create(:ci_pipeline, source: :chat)) }
|
|
||||||
|
|
||||||
it 'schedules a ChatNotification job' do
|
|
||||||
expect(ChatNotificationWorker).to receive(:perform_async).with(build.id)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Ci::MergeRequests::AddTodoWhenBuildFailsWorker do
|
||||||
|
describe '#perform' do
|
||||||
|
let_it_be(:project) { create(:project) }
|
||||||
|
let_it_be(:pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline) }
|
||||||
|
let_it_be(:job) { create(:ci_build, project: project, pipeline: pipeline, status: :failed) }
|
||||||
|
|
||||||
|
let(:job_args) { job.id }
|
||||||
|
|
||||||
|
subject(:perform_twice) { perform_multiple(job_args, exec_times: 2) }
|
||||||
|
|
||||||
|
include_examples 'an idempotent worker' do
|
||||||
|
it 'executes todo service' do
|
||||||
|
service = double
|
||||||
|
expect(::MergeRequests::AddTodoWhenBuildFailsService).to receive(:new).with(project, nil).and_return(service).twice
|
||||||
|
expect(service).to receive(:execute).with(job).twice
|
||||||
|
|
||||||
|
perform_twice
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when job does not exist' do
|
||||||
|
let(:job_args) { 0 }
|
||||||
|
|
||||||
|
it 'returns nil' do
|
||||||
|
expect(described_class.new.perform(job_args)).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when project does not exist' do
|
||||||
|
before do
|
||||||
|
job.update!(project_id: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns nil' do
|
||||||
|
expect(described_class.new.perform(job_args)).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when pipeline does not exist' do
|
||||||
|
before do
|
||||||
|
job.update_attribute('pipeline_id', nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns nil' do
|
||||||
|
expect(described_class.new.perform(job_args)).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue