diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 8e5b6a02b49..3569347dd43 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -232,8 +232,6 @@ Rails/SaveBang: - 'spec/initializers/fog_google_https_private_urls_spec.rb' - 'spec/lib/after_commit_queue_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/analytics/cycle_analytics/records_fetcher_spec.rb' - 'spec/lib/gitlab/auth/ldap/user_spec.rb' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f01dedb40dd..f06496d4519 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -640,11 +640,6 @@ Rails/WhereEquals: Rails/WhereExists: Enabled: false -# Offense count: 21 -# Cop supports --auto-correct. -Rails/WhereNot: - Enabled: false - # Offense count: 8 # Cop supports --auto-correct. Security/YAMLLoad: diff --git a/app/finders/ci/pipelines_finder.rb b/app/finders/ci/pipelines_finder.rb index a77faebb160..e509cf940b8 100644 --- a/app/finders/ci/pipelines_finder.rb +++ b/app/finders/ci/pipelines_finder.rb @@ -131,7 +131,7 @@ module Ci def by_yaml_errors(items) case Gitlab::Utils.to_boolean(params[:yaml_errors]) when true - items.where("yaml_errors IS NOT NULL") + items.where.not(yaml_errors: nil) when false items.where("yaml_errors IS NULL") else diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index 1bbfdb431de..666286b3016 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -4,29 +4,29 @@ module ServicesHelper def service_event_description(event) case event 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" - 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" - 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" - 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" - 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" - 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" - 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" - 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" - 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" - 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" - s_("ProjectService|Event will be triggered when a deployment starts or finishes") + s_("ProjectService|Event triggered when a deployment starts or finishes.") 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 diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index dce9168a9a4..1f7f5f6babe 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -93,8 +93,7 @@ module Ci validates :ref, presence: true scope :not_interruptible, -> do - joins(:metadata).where('ci_builds_metadata.id NOT IN (?)', - Ci::BuildMetadata.scoped_build.with_interruptible.select(:id)) + joins(:metadata).where.not('ci_builds_metadata.id' => Ci::BuildMetadata.scoped_build.with_interruptible.select(:id)) end scope :unstarted, -> { where(runner_id: nil) } diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index fe6889a6808..9dd75150ac7 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -44,7 +44,7 @@ module Ci next if position.present? self.position = statuses.select(:stage_idx) - .where('stage_idx IS NOT NULL') + .where.not(stage_idx: nil) .group(:stage_idx) .order('COUNT(*) DESC') .first&.stage_idx.to_i diff --git a/app/models/clusters/clusters_hierarchy.rb b/app/models/clusters/clusters_hierarchy.rb index c9c18d8c96a..125783e6ee1 100644 --- a/app/models/clusters/clusters_hierarchy.rb +++ b/app/models/clusters/clusters_hierarchy.rb @@ -16,7 +16,7 @@ module Clusters model .unscoped - .where('clusters.id IS NOT NULL') + .where.not('clusters.id' => nil) .with .recursive(cte.to_arel) .from(cte_alias) diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index e64380f5d91..fe262d48ac4 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -56,6 +56,7 @@ class CommitStatus < ApplicationRecord scope :by_name, -> (name) { where(name: name) } scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) } scope :eager_load_pipeline, -> { eager_load(:pipeline, project: { namespace: :route }) } + scope :with_pipeline, -> { joins(:pipeline) } scope :for_project_paths, -> (paths) do where(project: Project.where_full_path_in(Array(paths))) @@ -180,6 +181,7 @@ class CommitStatus < ApplicationRecord end 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 # rubocop: disable CodeReuse/ServiceClass diff --git a/app/models/environment.rb b/app/models/environment.rb index 96b44c9ac0a..4cc65f4e295 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -86,7 +86,7 @@ class Environment < ApplicationRecord end 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 :unfoldered, -> { where(environment_type: nil) } scope :with_rank, -> do diff --git a/app/models/project.rb b/app/models/project.rb index eb0382414de..2431b40a8be 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -519,7 +519,7 @@ class Project < ApplicationRecord scope :with_packages, -> { joins(:packages) } scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) } 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 :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)) } diff --git a/app/models/project_services/microsoft_teams_service.rb b/app/models/project_services/microsoft_teams_service.rb index e8e12a9a206..803c1255195 100644 --- a/app/models/project_services/microsoft_teams_service.rb +++ b/app/models/project_services/microsoft_teams_service.rb @@ -2,7 +2,7 @@ class MicrosoftTeamsService < ChatNotificationService def title - 'Microsoft Teams Notification' + 'Microsoft Teams notifications' end def description @@ -14,13 +14,7 @@ class MicrosoftTeamsService < ChatNotificationService end def help - 'This service sends notifications about projects events to Microsoft Teams channels.
- To set up this service: -
    -
  1. Setup a custom Incoming Webhook using Office 365 Connectors For Microsoft Teams.
  2. -
  3. Paste the Webhook URL into the field below.
  4. -
  5. Select events below to enable notifications.
  6. -
' + '

Use this service to send notifications about events in GitLab projects to your Microsoft Teams channels. How do I configure this integration?

' end def webhook_placeholder @@ -40,8 +34,8 @@ class MicrosoftTeamsService < ChatNotificationService def default_fields [ - { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" }, - { type: 'checkbox', name: 'notify_only_broken_pipelines' }, + { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" }, + { 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 } ] end diff --git a/app/models/user.rb b/app/models/user.rb index decd1117c6c..de7bb1ccf49 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -353,12 +353,7 @@ class User < ApplicationRecord # this state transition object in order to do a rollback. # For this reason the tradeoff is to disable this cop. after_transition any => :blocked do |user| - if Feature.enabled?(:abort_user_pipelines_on_block, user) - Ci::AbortPipelinesService.new.execute(user.pipelines) - else - Ci::CancelUserPipelinesService.new.execute(user) - end - + Ci::AbortPipelinesService.new.execute(user.pipelines) Ci::DisableUserPipelineSchedulesService.new.execute(user) end # rubocop: enable CodeReuse/ServiceClass @@ -1041,7 +1036,7 @@ class User < ApplicationRecord [ Project.where(namespace: namespace), 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 }) ], remove_duplicates: false diff --git a/app/services/ci/cancel_user_pipelines_service.rb b/app/services/ci/cancel_user_pipelines_service.rb deleted file mode 100644 index 3d3a8032e8e..00000000000 --- a/app/services/ci/cancel_user_pipelines_service.rb +++ /dev/null @@ -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 diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb index 9355b82b598..3d58f3d9e9a 100644 --- a/app/services/ci/retry_build_service.rb +++ b/app/services/ci/retry_build_service.rb @@ -21,7 +21,7 @@ module Ci Gitlab::OptimisticLocking.retry_lock(new_build, name: 'retry_build', &:enqueue) - MergeRequests::AddTodoWhenBuildFailsService + ::MergeRequests::AddTodoWhenBuildFailsService .new(project, current_user) .close(new_build) end diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb index 90ee7b9b3ba..bb8590a769c 100644 --- a/app/services/ci/retry_pipeline_service.rb +++ b/app/services/ci/retry_pipeline_service.rb @@ -28,7 +28,7 @@ module Ci pipeline.reset_ancestor_bridges! - MergeRequests::AddTodoWhenBuildFailsService + ::MergeRequests::AddTodoWhenBuildFailsService .new(project, current_user) .close_all(pipeline) diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 317cd11a69d..4cbb6b77c50 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -164,7 +164,7 @@ module MergeRequests def pipeline_merge_requests(pipeline) 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 end diff --git a/app/services/namespaces/in_product_marketing_emails_service.rb b/app/services/namespaces/in_product_marketing_emails_service.rb index aa29c8574ad..2b3c0c382a8 100644 --- a/app/services/namespaces/in_product_marketing_emails_service.rb +++ b/app/services/namespaces/in_product_marketing_emails_service.rb @@ -27,7 +27,7 @@ module Namespaces end 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.each do |group| diff --git a/app/services/todos/destroy/base_service.rb b/app/services/todos/destroy/base_service.rb index 7378f10e7c4..4e971246185 100644 --- a/app/services/todos/destroy/base_service.rb +++ b/app/services/todos/destroy/base_service.rb @@ -13,7 +13,7 @@ module Todos # rubocop: disable CodeReuse/ActiveRecord def without_authorized(items) - items.where('todos.user_id NOT IN (?)', authorized_users) + items.where.not('todos.user_id' => authorized_users) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/services/todos/destroy/private_features_service.rb b/app/services/todos/destroy/private_features_service.rb index bd49519d694..44c3ff231f8 100644 --- a/app/services/todos/destroy/private_features_service.rb +++ b/app/services/todos/destroy/private_features_service.rb @@ -36,7 +36,7 @@ module Todos items = Todo.where(project_id: project_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) .delete_all end diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 921def1eef8..a542c8e179d 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -1235,6 +1235,14 @@ :weight: 3 :idempotent: :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 :feature_category: :continuous_integration :has_external_dependencies: diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb index 3f99b30fdf7..c9cfb1da10b 100644 --- a/app/workers/build_finished_worker.rb +++ b/app/workers/build_finished_worker.rb @@ -37,6 +37,10 @@ class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker ExpirePipelineCacheWorker.perform_async(build.pipeline_id) 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 # validate that this fixes a race condition between this and flushing live diff --git a/app/workers/ci/merge_requests/add_todo_when_build_fails_worker.rb b/app/workers/ci/merge_requests/add_todo_when_build_fails_worker.rb new file mode 100644 index 00000000000..d5e097dc2b5 --- /dev/null +++ b/app/workers/ci/merge_requests/add_todo_when_build_fails_worker.rb @@ -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 diff --git a/changelogs/unreleased/324045-remove-feature-flag.yml b/changelogs/unreleased/324045-remove-feature-flag.yml new file mode 100644 index 00000000000..838d1923556 --- /dev/null +++ b/changelogs/unreleased/324045-remove-feature-flag.yml @@ -0,0 +1,5 @@ +--- +title: Bulk-abort user pipelines on block +merge_request: 57801 +author: +type: performance diff --git a/changelogs/unreleased/325509-set-namespaces-traversal_ids-for-production-gitlab-org-group.yml b/changelogs/unreleased/325509-set-namespaces-traversal_ids-for-production-gitlab-org-group.yml new file mode 100644 index 00000000000..fc438b5cc6b --- /dev/null +++ b/changelogs/unreleased/325509-set-namespaces-traversal_ids-for-production-gitlab-org-group.yml @@ -0,0 +1,5 @@ +--- +title: Backfill traversal_ids for gitlab-org .com +merge_request: 57075 +author: +type: performance diff --git a/changelogs/unreleased/issue-220040-fix-rails-savebang-banzai-module.yml b/changelogs/unreleased/issue-220040-fix-rails-savebang-banzai-module.yml new file mode 100644 index 00000000000..f3715817ea0 --- /dev/null +++ b/changelogs/unreleased/issue-220040-fix-rails-savebang-banzai-module.yml @@ -0,0 +1,5 @@ +--- +title: Fix Rails/SaveBang Rubocop offenses for banzai modules +merge_request: 58108 +author: Huzaifa Iftikhar @huzaifaiftikhar +type: fixed diff --git a/changelogs/unreleased/pl-rubocop-todo-where-not.yml b/changelogs/unreleased/pl-rubocop-todo-where-not.yml new file mode 100644 index 00000000000..da8c041ec10 --- /dev/null +++ b/changelogs/unreleased/pl-rubocop-todo-where-not.yml @@ -0,0 +1,5 @@ +--- +title: Resolves rubocop offenses Rails/WhereNot +merge_request: 58062 +author: Shubham Kumar (@imskr) +type: fixed diff --git a/config/feature_flags/development/abort_user_pipelines_on_block.yml b/config/feature_flags/development/async_add_build_failure_todo.yml similarity index 59% rename from config/feature_flags/development/abort_user_pipelines_on_block.yml rename to config/feature_flags/development/async_add_build_failure_todo.yml index b0e4fb30fc2..6bb1a84ed82 100644 --- a/config/feature_flags/development/abort_user_pipelines_on_block.yml +++ b/config/feature_flags/development/async_add_build_failure_todo.yml @@ -1,8 +1,8 @@ --- -name: abort_user_pipelines_on_block -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56126 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/324045 -milestone: '13.10' +name: async_add_build_failure_todo +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57490/diffs +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326726 +milestone: '13.11' type: development -group: group::memory +group: group::continuous integration default_enabled: false diff --git a/config/metrics/schema.json b/config/metrics/schema.json index 4e5d4162a05..8878b3bc744 100644 --- a/config/metrics/schema.json +++ b/config/metrics/schema.json @@ -5,6 +5,10 @@ "key_path": { "type": "string" }, + "name": { + "type": ["string", "null"], + "pattern": "^([a-z]+_)*[a-z]+$" + }, "description": { "type": "string" }, diff --git a/db/post_migrate/20200406102120_backfill_deployment_clusters_from_deployments.rb b/db/post_migrate/20200406102120_backfill_deployment_clusters_from_deployments.rb index 76b00796d1a..ab217ba92ab 100644 --- a/db/post_migrate/20200406102120_backfill_deployment_clusters_from_deployments.rb +++ b/db/post_migrate/20200406102120_backfill_deployment_clusters_from_deployments.rb @@ -17,7 +17,7 @@ class BackfillDeploymentClustersFromDeployments < ActiveRecord::Migration[6.0] class Deployment < ActiveRecord::Base 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' end diff --git a/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb b/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb index 891201eaa52..031d9ea49e2 100644 --- a/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb +++ b/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb @@ -18,11 +18,11 @@ class CreateMissingVulnerabilitiesIssueLinks < ActiveRecord::Migration[6.0] disable_ddl_transaction! 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 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") - .where('vo.vulnerability_id IS NOT NULL') + .where.not('vo.vulnerability_id' => nil) .pluck(:vulnerability_id, :issue_id) .map do |v_id, i_id| { diff --git a/db/post_migrate/20210311045139_set_traversal_ids_for_gitlab_org_group_com.rb b/db/post_migrate/20210311045139_set_traversal_ids_for_gitlab_org_group_com.rb new file mode 100644 index 00000000000..8cef1f1cc2b --- /dev/null +++ b/db/post_migrate/20210311045139_set_traversal_ids_for_gitlab_org_group_com.rb @@ -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 diff --git a/db/schema_migrations/20210311045139 b/db/schema_migrations/20210311045139 new file mode 100644 index 00000000000..71026c1b2af --- /dev/null +++ b/db/schema_migrations/20210311045139 @@ -0,0 +1 @@ +2387c8a5516aaf8bcf44c9bad45bfc9844d68d2c03330f67773ce046b21a7a6c \ No newline at end of file diff --git a/doc/user/project/integrations/img/microsoft_teams_configuration.png b/doc/user/project/integrations/img/microsoft_teams_configuration.png deleted file mode 100644 index 22ad28e3f73..00000000000 Binary files a/doc/user/project/integrations/img/microsoft_teams_configuration.png and /dev/null differ diff --git a/doc/user/project/integrations/microsoft_teams.md b/doc/user/project/integrations/microsoft_teams.md index 41e0938fc3b..795ead573f2 100644 --- a/doc/user/project/integrations/microsoft_teams.md +++ b/doc/user/project/integrations/microsoft_teams.md @@ -6,49 +6,55 @@ info: To determine the technical writer assigned to the Stage/Group associated w # 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 -Teams by following the steps below: +1. [Configure Microsoft Teams](#configure-microsoft-teams) to enable a webhook + 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 - **Incoming Webhook** item. +## Configure Microsoft Teams + +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) -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. Add a name for the webhook. The name is displayed next to every message that 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 -[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). +## Configure your GitLab project -## 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) -and select the **Microsoft Teams Notification** service to configure it. -There, you see a checkbox with the following events that can be triggered: +## Resources -- Push -- 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) +- [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). diff --git a/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb b/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb index eb4bc0aaf28..28cc4a5e3fa 100644 --- a/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb +++ b/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb @@ -11,7 +11,7 @@ module Gitlab MergeRequest .where(merge_request_assignees_not_exists_clause) .where(id: from_id..to_id) - .where('assignee_id IS NOT NULL') + .where.not(assignee_id: nil) .select(:id, :assignee_id) .to_sql diff --git a/lib/gitlab/background_migration/wrongfully_confirmed_email_unconfirmer.rb b/lib/gitlab/background_migration/wrongfully_confirmed_email_unconfirmer.rb index 5b01141d8c1..665ad7abcbb 100644 --- a/lib/gitlab/background_migration/wrongfully_confirmed_email_unconfirmer.rb +++ b/lib/gitlab/background_migration/wrongfully_confirmed_email_unconfirmer.rb @@ -27,7 +27,7 @@ module Gitlab joins(:user) .merge(UserModel.active) .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.email <> users.email') .where('NOT EXISTS (SELECT 1 FROM user_synced_attributes_metadata WHERE user_id=users.id AND email_synced IS true)') diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb index 428bcbe8dc5..2f15cdd7506 100644 --- a/lib/gitlab/import_export/uploads_manager.rb +++ b/lib/gitlab/import_export/uploads_manager.rb @@ -76,7 +76,7 @@ module Gitlab def project_uploads_except_avatar(avatar_path) return @project.uploads unless avatar_path - @project.uploads.where("path != ?", avatar_path) + @project.uploads.where.not(path: avatar_path) end def download_and_copy(upload) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d4e0b68f6e5..01c1699bc4a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -24110,40 +24110,40 @@ msgstr "" msgid "ProjectService|Enter new password" 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 "" -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 "" -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 "" -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 "" -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 "" -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 "" -msgid "ProjectService|Event will be triggered when a new, unique alert is recorded" +msgid "ProjectService|Event triggered when a pipeline status changes." 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 "" -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 "" -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 "" -msgid "ProjectService|Event will be triggered when someone adds a comment" +msgid "ProjectService|Event triggered when someone adds a comment." 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 "" msgid "ProjectService|Issue URL" diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb index 5f92eb42e74..0c1b98e5ec3 100644 --- a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Banzai::ReferenceParser::ExternalIssueParser do levels.each do |level| 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]) diff --git a/spec/lib/banzai/reference_redactor_spec.rb b/spec/lib/banzai/reference_redactor_spec.rb index 668e427cfa2..78cceedd0e5 100644 --- a/spec/lib/banzai/reference_redactor_spec.rb +++ b/spec/lib/banzai/reference_redactor_spec.rb @@ -64,7 +64,7 @@ RSpec.describe Banzai::ReferenceRedactor do let(:redactor) { described_class.new(Banzai::RenderContext.new(project, user)) } before do - project.update(pending_delete: true) + project.update!(pending_delete: true) end it 'redacts an issue attached' do diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb index 52e9f2d9846..b6f9c8106c9 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb @@ -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') - 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).not_to include(other_merge_request) end diff --git a/spec/lib/gitlab/usage/metric_definition_spec.rb b/spec/lib/gitlab/usage/metric_definition_spec.rb index 8b592838f5d..f98cc4cbcb7 100644 --- a/spec/lib/gitlab/usage/metric_definition_spec.rb +++ b/spec/lib/gitlab/usage/metric_definition_spec.rb @@ -16,7 +16,8 @@ RSpec.describe Gitlab::Usage::MetricDefinition do time_frame: 'none', data_source: 'database', 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 @@ -53,6 +54,7 @@ RSpec.describe Gitlab::Usage::MetricDefinition do :distribution | nil :distribution | 'test' :tier | %w(test ee) + :name | 'count__boards' end with_them do diff --git a/spec/migrations/clean_up_noteable_id_for_notes_on_commits_spec.rb b/spec/migrations/clean_up_noteable_id_for_notes_on_commits_spec.rb index 531c1dbb76a..268fadee0af 100644 --- a/spec/migrations/clean_up_noteable_id_for_notes_on_commits_spec.rb +++ b/spec/migrations/clean_up_noteable_id_for_notes_on_commits_spec.rb @@ -25,7 +25,7 @@ RSpec.describe CleanUpNoteableIdForNotesOnCommits do end 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 def other_notes diff --git a/spec/migrations/migrate_bot_type_to_user_type_spec.rb b/spec/migrations/migrate_bot_type_to_user_type_spec.rb index 2b85f2a7f69..fcd7f1ebcb8 100644 --- a/spec/migrations/migrate_bot_type_to_user_type_spec.rb +++ b/spec/migrations/migrate_bot_type_to_user_type_spec.rb @@ -15,6 +15,6 @@ RSpec.describe MigrateBotTypeToUserType, :migration do 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 diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index aa3cc0800a8..fa0e72e7ad4 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -3582,10 +3582,10 @@ RSpec.describe Ci::Build do end describe 'state transition when build fails' do - let(:service) { MergeRequests::AddTodoWhenBuildFailsService.new(project, user) } + let(:service) { ::MergeRequests::AddTodoWhenBuildFailsService.new(project, user) } 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) end @@ -3670,15 +3670,42 @@ RSpec.describe Ci::Build do subject.drop! end - it 'creates a todo' do - project.add_developer(user) + context 'when async_add_build_failure_todo flag enabled' do + it 'creates a todo async', :sidekiq_inline 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) + expect_next_instance_of(TodoService) do |todo_service| + expect(todo_service) + .to receive(:merge_request_build_failed).with(merge_request) + end + + subject.drop! 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 diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 9498aa75289..ac92311e132 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1790,28 +1790,14 @@ RSpec.describe User do context 'when user has running CI pipelines' do let(:service) { double } + let(:pipelines) { build_list(:ci_pipeline, 3, :running) } - context 'with abort_user_pipelines_on_block feature enabled' do - let(:pipelines) { build_list(:ci_pipeline, 3, :running) } + it 'aborts all running pipelines and related jobs' do + 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 - 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 + user.block end end diff --git a/spec/serializers/service_event_entity_spec.rb b/spec/serializers/service_event_entity_spec.rb index 09bb8bca43b..34ffd93ea0c 100644 --- a/spec/serializers/service_event_entity_spec.rb +++ b/spec/serializers/service_event_entity_spec.rb @@ -17,7 +17,7 @@ RSpec.describe ServiceEventEntity do let(:event) { 'push' } 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[:title]).to eq('push') expect(subject[:value]).to be(true) @@ -29,7 +29,7 @@ RSpec.describe ServiceEventEntity do let(:event) { 'note' } 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[:title]).to eq('note') expect(subject[:value]).to eq(false) diff --git a/spec/services/ci/cancel_user_pipelines_service_spec.rb b/spec/services/ci/cancel_user_pipelines_service_spec.rb deleted file mode 100644 index 8491242dfd5..00000000000 --- a/spec/services/ci/cancel_user_pipelines_service_spec.rb +++ /dev/null @@ -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 diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index bdf60bb3fdc..7dd3d963e56 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -181,7 +181,7 @@ RSpec.describe Ci::RetryBuildService do end it 'resolves todos for old build that failed' do - expect(MergeRequests::AddTodoWhenBuildFailsService) + expect(::MergeRequests::AddTodoWhenBuildFailsService) .to receive_message_chain(:new, :close) service.execute(build) diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index 3c6a99efbf8..3e2e9f07723 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -272,7 +272,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do end it 'closes all todos about failed jobs for pipeline' do - expect(MergeRequests::AddTodoWhenBuildFailsService) + expect(::MergeRequests::AddTodoWhenBuildFailsService) .to receive_message_chain(:new, :close_all) service.execute(pipeline) diff --git a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb index 3c81ad6722d..6edaa91b8b2 100644 --- a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb +++ b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe MergeRequests::AddTodoWhenBuildFailsService do +RSpec.describe ::MergeRequests::AddTodoWhenBuildFailsService do let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:sha) { '1234567890abcdef1234567890abcdef12345678' } @@ -24,8 +24,8 @@ RSpec.describe MergeRequests::AddTodoWhenBuildFailsService do before do allow_any_instance_of(MergeRequest) - .to receive(:head_pipeline) - .and_return(pipeline) + .to receive(:head_pipeline_id) + .and_return(pipeline.id) allow(service).to receive(:todo_service).and_return(todo_service) end diff --git a/spec/services/namespaces/in_product_marketing_emails_service_spec.rb b/spec/services/namespaces/in_product_marketing_emails_service_spec.rb index 3aa4d0b2f39..43c41b5c99d 100644 --- a/spec/services/namespaces/in_product_marketing_emails_service_spec.rb +++ b/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @@ -218,7 +218,7 @@ RSpec.describe Namespaces::InProductMarketingEmailsService, '#execute' do stub_const("#{described_class}::TRACKS", { bar: :git_write }) end - it { expect { subject }.to raise_error(NotImplementedError, 'Track foo not defined') } + it { expect { subject }.to raise_error(ArgumentError, 'Track foo not defined') } end context 'when group is a sub-group' do diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb index 6d040f83dc7..638bf8ab12b 100644 --- a/spec/workers/build_finished_worker_spec.rb +++ b/spec/workers/build_finished_worker_spec.rb @@ -6,10 +6,8 @@ RSpec.describe BuildFinishedWorker do subject { described_class.new.perform(build.id) } describe '#perform' do - let(:build) { create(:ci_build, :success, pipeline: create(:ci_pipeline)) } - context 'when build exists' do - let!(:build) { create(:ci_build) } + let_it_be(:build) { create(:ci_build, :success, pipeline: create(:ci_pipeline)) } before do expect(Ci::Build).to receive(:find_by).with(id: build.id).and_return(build) @@ -30,6 +28,42 @@ RSpec.describe BuildFinishedWorker do subject 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 context 'when build does not exist' do @@ -38,15 +72,5 @@ RSpec.describe BuildFinishedWorker do .not_to raise_error 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 diff --git a/spec/workers/ci/merge_requests/add_todo_when_build_fails_worker_spec.rb b/spec/workers/ci/merge_requests/add_todo_when_build_fails_worker_spec.rb new file mode 100644 index 00000000000..4690c73d121 --- /dev/null +++ b/spec/workers/ci/merge_requests/add_todo_when_build_fails_worker_spec.rb @@ -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 diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore old mode 100644 new mode 100755 diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore old mode 100644 new mode 100755