diff options
53 files changed, 635 insertions, 120 deletions
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js index 12a26fd88fa..7520cfb6da0 100644 --- a/app/assets/javascripts/pages/groups/merge_requests/index.js +++ b/app/assets/javascripts/pages/groups/merge_requests/index.js @@ -1,11 +1,15 @@ import projectSelect from '~/project_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; +import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests'; import { FILTERED_SEARCH } from '~/pages/constants'; +const ISSUABLE_BULK_UPDATE_PREFIX = 'merge_request_'; + document.addEventListener('DOMContentLoaded', () => { addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys); + issuableInitBulkUpdateSidebar.init(ISSUABLE_BULK_UPDATE_PREFIX); initFilteredSearch({ page: FILTERED_SEARCH.MERGE_REQUESTS, diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 635fcc86166..da70cb9a9a7 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -531,6 +531,14 @@ module Ci trace.exist? end + def has_live_trace? + trace.live_trace_exist? + end + + def has_archived_trace? + trace.archived_trace_exist? + end + def artifacts_file job_artifacts_archive&.file end diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index f80e98e5bca..e132cb045e2 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -176,6 +176,10 @@ module Ci end end + def self.archived_trace_exists_for?(job_id) + where(job_id: job_id).trace.take&.file&.file&.exists? + end + private def file_format_adapter_class diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 07d00503861..43ff874ac23 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -264,7 +264,7 @@ module Ci private def cleanup_runner_queue - Gitlab::Redis::Queues.with do |redis| + Gitlab::Redis::SharedState.with do |redis| redis.del(runner_queue_key) end end diff --git a/app/models/cycle_analytics/group_level.rb b/app/models/cycle_analytics/group_level.rb index 508cde0ca00..a41e1375484 100644 --- a/app/models/cycle_analytics/group_level.rb +++ b/app/models/cycle_analytics/group_level.rb @@ -11,9 +11,7 @@ module CycleAnalytics end def summary - @summary ||= ::Gitlab::CycleAnalytics::GroupStageSummary.new(group, - from: options[:from], - current_user: options[:current_user]).data + @summary ||= ::Gitlab::CycleAnalytics::GroupStageSummary.new(group, options: options).data end def permissions(*) diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index af705b29f7a..6b5605f9999 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -31,7 +31,7 @@ class RemoteMirror < ApplicationRecord scope :enabled, -> { where(enabled: true) } scope :started, -> { with_update_status(:started) } - scope :stuck, -> { started.where('last_update_at < ? OR (last_update_at IS NULL AND updated_at < ?)', 1.day.ago, 1.day.ago) } + scope :stuck, -> { started.where('last_update_at < ? OR (last_update_at IS NULL AND updated_at < ?)', 1.hour.ago, 3.hours.ago) } state_machine :update_status, initial: :none do event :update_start do @@ -173,7 +173,7 @@ class RemoteMirror < ApplicationRecord result = URI.parse(url) result.password = '*****' if result.password - result.user = '*****' if result.user && result.user != "git" # tokens or other data may be saved as user + result.user = '*****' if result.user && result.user != 'git' # tokens or other data may be saved as user result.to_s end diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb index 91c9abe750b..2cf3278d240 100644 --- a/app/presenters/blob_presenter.rb +++ b/app/presenters/blob_presenter.rb @@ -4,7 +4,7 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated presents :blob def highlight(plain: nil) - blob.load_all_data! if blob.respond_to?(:load_all_data!) + load_all_blob_data Gitlab::Highlight.highlight( blob.path, @@ -17,4 +17,10 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated def web_url Gitlab::Routing.url_helpers.project_blob_url(blob.repository.project, File.join(blob.commit_id, blob.path)) end + + private + + def load_all_blob_data + blob.load_all_data! if blob.respond_to?(:load_all_data!) + end end diff --git a/app/presenters/blobs/unfold_presenter.rb b/app/presenters/blobs/unfold_presenter.rb index 7b13db3bb74..21a1e1309e0 100644 --- a/app/presenters/blobs/unfold_presenter.rb +++ b/app/presenters/blobs/unfold_presenter.rb @@ -16,8 +16,12 @@ module Blobs attribute :indent, Integer, default: 0 def initialize(blob, params) + # Load all blob data first as we need to ensure they're all loaded first + # so we can accurately show the rest of the diff when unfolding. + load_all_blob_data + @subject = blob - @all_lines = highlight.lines + @all_lines = blob.data.lines super(params) if full? @@ -25,10 +29,12 @@ module Blobs end end - # Converts a String array to Gitlab::Diff::Line array, with match line added + # Returns an array of Gitlab::Diff::Line with match line added def diff_lines - diff_lines = lines.map do |line| - Gitlab::Diff::Line.new(line, nil, nil, nil, nil, rich_text: line) + diff_lines = lines.map.with_index do |line, index| + full_line = limited_blob_lines[index].delete("\n") + + Gitlab::Diff::Line.new(full_line, nil, nil, nil, nil, rich_text: line) end add_match_line(diff_lines) @@ -37,11 +43,7 @@ module Blobs end def lines - strong_memoize(:lines) do - lines = @all_lines - lines = lines[since - 1..to - 1] unless full? - lines.map(&:html_safe) - end + @lines ||= limit(highlight.lines).map(&:html_safe) end def match_line_text @@ -71,5 +73,15 @@ module Blobs bottom? ? diff_lines.push(match_line) : diff_lines.unshift(match_line) end + + def limited_blob_lines + @limited_blob_lines ||= limit(@all_lines) + end + + def limit(lines) + return lines if full? + + lines[since - 1..to - 1] + end end end diff --git a/app/services/ci/archive_trace_service.rb b/app/services/ci/archive_trace_service.rb index b5cfa1d019c..700d78361a4 100644 --- a/app/services/ci/archive_trace_service.rb +++ b/app/services/ci/archive_trace_service.rb @@ -2,8 +2,25 @@ module Ci class ArchiveTraceService - def execute(job) + def execute(job, worker_name:) + # TODO: Remove this logging once we confirmed new live trace architecture is functional. + # See https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/4667. + unless job.has_live_trace? + Sidekiq.logger.warn(class: worker_name, + message: 'The job does not have live trace but going to be archived.', + job_id: job.id) + return + end + job.trace.archive! + + # TODO: Remove this logging once we confirmed new live trace architecture is functional. + # See https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/4667. + unless job.has_archived_trace? + Sidekiq.logger.warn(class: worker_name, + message: 'The job does not have archived trace after archiving.', + job_id: job.id) + end rescue ::Gitlab::Ci::Trace::AlreadyArchivedError # It's already archived, thus we can safely ignore this exception. rescue => e @@ -11,7 +28,7 @@ module Ci # If `archive!` keeps failing for over a week, that could incur data loss. # (See more https://docs.gitlab.com/ee/administration/job_traces.html#new-live-trace-architecture) # In order to avoid interrupting the system, we do not raise an exception here. - archive_error(e, job) + archive_error(e, job, worker_name) end private @@ -22,9 +39,12 @@ module Ci "Counter of failed attempts of trace archiving") end - def archive_error(error, job) + def archive_error(error, job, worker_name) failed_archive_counter.increment - Rails.logger.error "Failed to archive trace. id: #{job.id} message: #{error.message}" # rubocop:disable Gitlab/RailsLogger + + Sidekiq.logger.warn(class: worker_name, + message: "Failed to archive trace. message: #{error.message}.", + job_id: job.id) Gitlab::Sentry .track_exception(error, diff --git a/app/validators/qualified_domain_array_validator.rb b/app/validators/qualified_domain_array_validator.rb new file mode 100644 index 00000000000..986c146a9db --- /dev/null +++ b/app/validators/qualified_domain_array_validator.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +# QualifiedDomainArrayValidator +# +# Custom validator for URL hosts/'qualified domains' (FQDNs, ex: gitlab.com, sub.example.com). +# This does not check if the domain actually exists. It only checks if it is a +# valid domain string. +# +# Example: +# +# class ApplicationSetting < ApplicationRecord +# validates :outbound_local_requests_whitelist, qualified_domain_array: true +# end +# +class QualifiedDomainArrayValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + validate_value_present(record, attribute, value) + validate_host_length(record, attribute, value) + validate_idna_encoding(record, attribute, value) + validate_sanitization(record, attribute, value) + end + + private + + def validate_value_present(record, attribute, value) + return unless value.blank? + + record.errors.add(attribute, _('entries cannot be blank')) + end + + def validate_host_length(record, attribute, value) + return unless value&.any? { |entry| entry.size > 255 } + + record.errors.add(attribute, _('entries cannot be larger than 255 characters')) + end + + def validate_idna_encoding(record, attribute, value) + return if value&.all?(&:ascii_only?) + + record.errors.add(attribute, _('unicode domains should use IDNA encoding')) + end + + def validate_sanitization(record, attribute, value) + sanitizer = Rails::Html::FullSanitizer.new + return unless value&.any? { |str| sanitizer.sanitize(str) != str } + + record.errors.add(attribute, _('entries cannot contain HTML tags')) + end +end diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index f05e269553a..2163446425c 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -13,7 +13,7 @@ = render 'shared/issuable/feed_buttons' - if @can_bulk_update - = render_if_exists 'shared/issuable/bulk_update_button' + = render_if_exists 'shared/issuable/bulk_update_button', type: :issues = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", type: :issues, with_feature_enabled: 'issues', with_shared: false, include_projects_in_subgroups: true diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 808bb1309b1..b5a2bab4799 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,3 +1,5 @@ +- @can_bulk_update = can?(current_user, :admin_merge_request, @group) + - page_title "Merge Requests" - if group_merge_requests_count(state: 'all').zero? @@ -7,8 +9,14 @@ = render 'shared/issuable/nav', type: :merge_requests - if current_user .nav-controls + - if @can_bulk_update + = render_if_exists 'shared/issuable/bulk_update_button', type: :merge_requests + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests, with_feature_enabled: 'merge_requests', with_shared: false, include_projects_in_subgroups: true = render 'shared/issuable/search_bar', type: :merge_requests + - if @can_bulk_update + = render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :merge_requests + = render 'shared/merge_requests' diff --git a/app/workers/archive_trace_worker.rb b/app/workers/archive_trace_worker.rb index 4a9becf0ca7..66f9b8d9e80 100644 --- a/app/workers/archive_trace_worker.rb +++ b/app/workers/archive_trace_worker.rb @@ -7,7 +7,7 @@ class ArchiveTraceWorker # rubocop: disable CodeReuse/ActiveRecord def perform(job_id) Ci::Build.without_archived_trace.find_by(id: job_id).try do |job| - Ci::ArchiveTraceService.new.execute(job) + Ci::ArchiveTraceService.new.execute(job, worker_name: self.class.name) end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/workers/ci/archive_traces_cron_worker.rb b/app/workers/ci/archive_traces_cron_worker.rb index f65ff239866..75e68d0233a 100644 --- a/app/workers/ci/archive_traces_cron_worker.rb +++ b/app/workers/ci/archive_traces_cron_worker.rb @@ -11,7 +11,7 @@ module Ci # This could happen when ArchiveTraceWorker sidekiq jobs were lost by receiving SIGKILL # More details in https://gitlab.com/gitlab-org/gitlab-ce/issues/36791 Ci::Build.finished.with_live_trace.find_each(batch_size: 100) do |build| - Ci::ArchiveTraceService.new.execute(build) + Ci::ArchiveTraceService.new.execute(build, worker_name: self.class.name) end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/changelogs/unreleased/57953-fix-unfolded-diff-suggestions.yml b/changelogs/unreleased/57953-fix-unfolded-diff-suggestions.yml new file mode 100644 index 00000000000..f634c0cd98a --- /dev/null +++ b/changelogs/unreleased/57953-fix-unfolded-diff-suggestions.yml @@ -0,0 +1,5 @@ +--- +title: Fix suggestion on lines that are not part of an MR +merge_request: 30606 +author: +type: fixed diff --git a/changelogs/unreleased/GL-12412.yml b/changelogs/unreleased/GL-12412.yml new file mode 100644 index 00000000000..304bd63d150 --- /dev/null +++ b/changelogs/unreleased/GL-12412.yml @@ -0,0 +1,5 @@ +--- +title: Add DS_PIP_DEPENDENCY_PATH option to configure Dependency Scanning for projects using pip. +merge_request: 30762 +author: +type: changed diff --git a/changelogs/unreleased/adjust-group-level-analytics-to-accept-multiple-ids.yml b/changelogs/unreleased/adjust-group-level-analytics-to-accept-multiple-ids.yml new file mode 100644 index 00000000000..5e138e1059c --- /dev/null +++ b/changelogs/unreleased/adjust-group-level-analytics-to-accept-multiple-ids.yml @@ -0,0 +1,5 @@ +--- +title: Adjust group level analytics to accept multiple ids +merge_request: 30744 +author: +type: added diff --git a/changelogs/unreleased/bvl-mark-remote-mirrors-as-failed-sooner.yml b/changelogs/unreleased/bvl-mark-remote-mirrors-as-failed-sooner.yml new file mode 100644 index 00000000000..1db0a4952b2 --- /dev/null +++ b/changelogs/unreleased/bvl-mark-remote-mirrors-as-failed-sooner.yml @@ -0,0 +1,5 @@ +--- +title: Mark push mirrors as failed after 1 hour +merge_request: 30999 +author: +type: changed diff --git a/changelogs/unreleased/safe-archiving-for-traces.yml b/changelogs/unreleased/safe-archiving-for-traces.yml new file mode 100644 index 00000000000..2b9070bacfe --- /dev/null +++ b/changelogs/unreleased/safe-archiving-for-traces.yml @@ -0,0 +1,5 @@ +--- +title: Extra logging for new live trace architecture +merge_request: 30892 +author: +type: fixed diff --git a/changelogs/unreleased/sh-use-shared-state-cluster-pubsub.yml b/changelogs/unreleased/sh-use-shared-state-cluster-pubsub.yml new file mode 100644 index 00000000000..5e72f23d7ad --- /dev/null +++ b/changelogs/unreleased/sh-use-shared-state-cluster-pubsub.yml @@ -0,0 +1,5 @@ +--- +title: Use persistent Redis cluster for Workhorse pub/sub notifications +merge_request: 30990 +author: +type: fixed diff --git a/config/boot.rb b/config/boot.rb index 2eacff868eb..da4e6b7290c 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -2,8 +2,4 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) # Set up gems listed in the Gemfile. require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) -begin - require 'bootsnap/setup' if ENV['RAILS_ENV'] != 'production' || %w(1 yes true).include?(ENV['ENABLE_BOOTSNAP']) -rescue LoadError - # bootsnap is an optional dependency, so if we don't have it, it's fine -end +require 'bootsnap/setup' if ENV['RAILS_ENV'] != 'production' || %w(1 yes true).include?(ENV['ENABLE_BOOTSNAP']) diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 09bd306363c..7473647f129 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -142,6 +142,7 @@ using environment variables. | `DS_ANALYZER_IMAGE_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). Read more about [customizing analyzers](analyzers.md). | | `DS_ANALYZER_IMAGE_TAG` | Override the Docker tag of the official default images. Read more about [customizing analyzers](analyzers.md). | | `DS_PYTHON_VERSION` | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/12296) in GitLab 12.1)| +| `DS_PIP_DEPENDENCY_PATH` | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/12412) in GitLab 12.2) | | `DS_DEFAULT_ANALYZERS` | Override the names of the official default images. Read more about [customizing analyzers](analyzers.md). | | `DS_DISABLE_REMOTE_CHECKS` | Do not send any data to GitLab. Used in the [Gemnasium analyzer](#remote-checks). | | `DS_PULL_ANALYZER_IMAGES` | Pull the images from the Docker registry (set to `0` to disable). | diff --git a/doc/user/group/bulk_editing/index.md b/doc/user/group/bulk_editing/index.md index 117a46da0ea..5b5f75c2dd9 100644 --- a/doc/user/group/bulk_editing/index.md +++ b/doc/user/group/bulk_editing/index.md @@ -1,22 +1,26 @@ -# Bulk editing issue milestones **(PREMIUM)** +# Bulk editing issue and merge request milestones **(PREMIUM)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7249) in -[GitLab Ultimate](https://about.gitlab.com/pricing/) 12.1. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7249) for issues in + [GitLab Premium](https://about.gitlab.com/pricing/) 12.1. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/12719) for merge + requests in GitLab [GitLab Premium](https://about.gitlab.com/pricing/) 12.2. -NOTE: **Note:** -A permission level of `Reporter` or higher is required in order to manage issues. +> NOTE: **Note:** +> +> - A permission level of `Reporter` or higher is required in order to manage issues. +> - A permission level of `Developer` or higher is required in order to manage merge requests. -Milestones can be updated simultaneously across multiple issues by using the bulk editing feature. +Milestones can be updated simultaneously across multiple issues or merge requests by using the bulk editing feature. ![Bulk editing](img/bulk-editing.png) -To bulk update group issue milestones: +To bulk update group issue or merge request milestones: -1. Navigate to the issues list. -1. Click **Edit issues**. +1. Navigate to the issues or merge requests list. +1. Click the **Edit issues** or **Edit merge requests** button. - This will open a sidebar on the right-hand side of your screen where an editable field for milestones will be displayed. - - Checkboxes will also appear beside each issue. + - Checkboxes will also appear beside each issue or merge request. 1. Check the checkbox beside each issue to be edited. 1. Select the desired milestone from the sidebar. 1. Click **Update all**. diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 7597105f36f..14c831fe671 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -78,9 +78,9 @@ Issues and merge requests are part of projects. For a given group, you can view [issues](../project/issues/index.md#issues-list) and [merge requests](../project/merge_requests/index.md#merge-requests-per-group) across all projects in that group, together in a single list view. -### Bulk editing issues +### Bulk editing issues and merge requests -For details, see [bulk editing issues](../group/bulk_editing/index.md). +For details, see [bulk editing issues and merge requests](../group/bulk_editing/index.md). ## Create a new group diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md index 1783f81df3a..f4733620640 100644 --- a/doc/user/project/bulk_editing.md +++ b/doc/user/project/bulk_editing.md @@ -13,8 +13,8 @@ by using the bulk editing feature. ![Bulk editing](img/bulk-editing.png) NOTE: **Note:** -Bulk editing of merge requests is only available at the project level. -For more details, see [bulk editing group issues](../group/bulk_editing/index.md). +Bulk editing issues and merge requests is also available at the group level. +For more details, see [bulk editing group issues and merge requests](../group/bulk_editing/index.md). To update multiple project issues or merge requests at the same time: diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml index 89eccce69f6..600762dd39f 100644 --- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml @@ -41,6 +41,7 @@ dependency_scanning: DS_PULL_ANALYZER_IMAGE_TIMEOUT \ DS_RUN_ANALYZER_TIMEOUT \ DS_PYTHON_VERSION \ + DS_PIP_DEPENDENCY_PATH \ PIP_INDEX_URL \ PIP_EXTRA_INDEX_URL \ ) \ diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb index ce5857965bf..cb617080c76 100644 --- a/lib/gitlab/ci/trace.rb +++ b/lib/gitlab/ci/trace.rb @@ -63,7 +63,15 @@ module Gitlab end def exist? - trace_artifact&.exists? || job.trace_chunks.any? || current_path.present? || old_trace.present? + archived_trace_exist? || live_trace_exist? + end + + def archived_trace_exist? + trace_artifact&.exists? + end + + def live_trace_exist? + job.trace_chunks.any? || current_path.present? || old_trace.present? end def read @@ -167,7 +175,7 @@ module Gitlab def clone_file!(src_stream, temp_dir) FileUtils.mkdir_p(temp_dir) - Dir.mktmpdir('tmp-trace', temp_dir) do |dir_path| + Dir.mktmpdir("tmp-trace-#{job.id}", temp_dir) do |dir_path| temp_path = File.join(dir_path, "job.log") FileUtils.touch(temp_path) size = IO.copy_stream(src_stream, temp_path) diff --git a/lib/gitlab/ci/trace/chunked_io.rb b/lib/gitlab/ci/trace/chunked_io.rb index 8c6fd56493f..e99889f4a25 100644 --- a/lib/gitlab/ci/trace/chunked_io.rb +++ b/lib/gitlab/ci/trace/chunked_io.rb @@ -166,6 +166,13 @@ module Gitlab end def destroy! + # TODO: Remove this logging once we confirmed new live trace architecture is functional. + # See https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/4667. + unless build.has_archived_trace? + Sidekiq.logger.warn(message: 'The job does not have archived trace but going to be destroyed.', + job_id: build.id) + end + trace_chunks.fast_destroy_all @tell = @size = 0 ensure diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index 96aa799e864..07ae430c45e 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -4,6 +4,7 @@ module Gitlab module CycleAnalytics class BaseEventFetcher include BaseQuery + include GroupProjectsProvider attr_reader :projections, :query, :stage, :order, :options @@ -73,18 +74,6 @@ module Gitlab def serialization_context {} end - - def projects - group ? Project.inside_path(group.full_path) : [project] - end - - def group - @group ||= options.fetch(:group, nil) - end - - def project - @project ||= options.fetch(:project, nil) - end end end end diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 678a891e941..1cd54238bb4 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -4,6 +4,7 @@ module Gitlab module CycleAnalytics class BaseStage include BaseQuery + include GroupProjectsProvider attr_reader :options @@ -77,18 +78,6 @@ module Gitlab def event_options options.merge(start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs) end - - def projects - group ? Project.inside_path(group.full_path) : [project] - end - - def group - @group ||= options.fetch(:group, nil) - end - - def project - @project ||= options.fetch(:project, nil) - end end end end diff --git a/lib/gitlab/cycle_analytics/group_projects_provider.rb b/lib/gitlab/cycle_analytics/group_projects_provider.rb new file mode 100644 index 00000000000..1287a48daaa --- /dev/null +++ b/lib/gitlab/cycle_analytics/group_projects_provider.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Gitlab + module CycleAnalytics + module GroupProjectsProvider + def projects + group ? projects_for_group : [project] + end + + def group + @group ||= options.fetch(:group, nil) + end + + def project + @project ||= options.fetch(:project, nil) + end + + private + + def projects_for_group + projects = Project.inside_path(group.full_path) + projects = projects.where(id: options[:projects]) if options[:projects] + projects + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/group_stage_summary.rb b/lib/gitlab/cycle_analytics/group_stage_summary.rb index 7b5c74e1a1b..a1fc941495d 100644 --- a/lib/gitlab/cycle_analytics/group_stage_summary.rb +++ b/lib/gitlab/cycle_analytics/group_stage_summary.rb @@ -3,15 +3,18 @@ module Gitlab module CycleAnalytics class GroupStageSummary - def initialize(group, from:, current_user:) + attr_reader :group, :from, :current_user, :options + + def initialize(group, options:) @group = group - @from = from - @current_user = current_user + @from = options[:from] + @current_user = options[:current_user] + @options = options end def data - [serialize(Summary::Group::Issue.new(group: @group, from: @from, current_user: @current_user)), - serialize(Summary::Group::Deploy.new(group: @group, from: @from))] + [serialize(Summary::Group::Issue.new(group: group, from: from, current_user: current_user, options: options)), + serialize(Summary::Group::Deploy.new(group: group, from: from, options: options))] end private diff --git a/lib/gitlab/cycle_analytics/summary/group/base.rb b/lib/gitlab/cycle_analytics/summary/group/base.rb index 7f18b61d309..48d8164bde1 100644 --- a/lib/gitlab/cycle_analytics/summary/group/base.rb +++ b/lib/gitlab/cycle_analytics/summary/group/base.rb @@ -5,9 +5,12 @@ module Gitlab module Summary module Group class Base - def initialize(group:, from:) + attr_reader :group, :from, :options + + def initialize(group:, from:, options:) @group = group @from = from + @options = options end def title diff --git a/lib/gitlab/cycle_analytics/summary/group/deploy.rb b/lib/gitlab/cycle_analytics/summary/group/deploy.rb index d8fcd8f2ce4..78d677cf558 100644 --- a/lib/gitlab/cycle_analytics/summary/group/deploy.rb +++ b/lib/gitlab/cycle_analytics/summary/group/deploy.rb @@ -5,22 +5,23 @@ module Gitlab module Summary module Group class Deploy < Group::Base + include GroupProjectsProvider + def title n_('Deploy', 'Deploys', value) end def value - @value ||= Deployment.joins(:project) - .where(projects: { id: projects }) - .where("deployments.created_at > ?", @from) - .success - .count + @value ||= find_deployments end private - def projects - Project.inside_path(@group.full_path).ids + def find_deployments + deployments = Deployment.joins(:project).merge(Project.inside_path(group.full_path)) + deployments = deployments.where(projects: { id: options[:projects] }) if options[:projects] + deployments = deployments.where("deployments.created_at > ?", from) + deployments.success.count end end end diff --git a/lib/gitlab/cycle_analytics/summary/group/issue.rb b/lib/gitlab/cycle_analytics/summary/group/issue.rb index 70073e6d843..9daae8531d8 100644 --- a/lib/gitlab/cycle_analytics/summary/group/issue.rb +++ b/lib/gitlab/cycle_analytics/summary/group/issue.rb @@ -5,10 +5,13 @@ module Gitlab module Summary module Group class Issue < Group::Base - def initialize(group:, from:, current_user:) + attr_reader :group, :from, :current_user, :options + + def initialize(group:, from:, current_user:, options:) @group = group @from = from @current_user = current_user + @options = options end def title @@ -16,7 +19,15 @@ module Gitlab end def value - @value ||= IssuesFinder.new(@current_user, group_id: @group.id, include_subgroups: true, created_after: @from).execute.count + @value ||= find_issues + end + + private + + def find_issues + issues = IssuesFinder.new(current_user, group_id: group.id, include_subgroups: true, created_after: from).execute + issues = issues.where(projects: { id: options[:projects] }) if options[:projects] + issues.count end end end diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 46a7b5b982a..3b77fe838ae 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -221,7 +221,7 @@ module Gitlab end def set_key_and_notify(key, value, expire: nil, overwrite: true) - Gitlab::Redis::Queues.with do |redis| + Gitlab::Redis::SharedState.with do |redis| result = redis.set(key, value, ex: expire, nx: !overwrite) if result redis.publish(NOTIFICATION_CHANNEL, "#{key}=#{value}") diff --git a/locale/gitlab.pot b/locale/gitlab.pot index bd26ca6714d..5cd9aa7e2de 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12803,6 +12803,15 @@ msgstr "" msgid "encrypted: needs to be a :required, :optional or :migrating!" msgstr "" +msgid "entries cannot be blank" +msgstr "" + +msgid "entries cannot be larger than 255 characters" +msgstr "" + +msgid "entries cannot contain HTML tags" +msgstr "" + msgid "error" msgstr "" @@ -13308,6 +13317,9 @@ msgstr "" msgid "triggered" msgstr "" +msgid "unicode domains should use IDNA encoding" +msgstr "" + msgid "updated" msgstr "" diff --git a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb index 546a9e7d0cc..3e5cd81f929 100644 --- a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb +++ b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb @@ -442,5 +442,15 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do expect(Ci::BuildTraceChunk.where(build: build).count).to eq(0) end + + context 'when the job does not have archived trace' do + it 'leaves a message in sidekiq log' do + expect(Sidekiq.logger).to receive(:warn).with( + message: 'The job does not have archived trace but going to be destroyed.', + job_id: build.id).and_call_original + + subject + end + end end end diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb index eea4f33ccb8..d5c2f7cc579 100644 --- a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do let(:from) { 1.day.ago } let(:user) { create(:user, :admin) } - subject { described_class.new(group, from: Time.now, current_user: user).data } + subject { described_class.new(group, options: { from: Time.now, current_user: user }).data } describe "#new_issues" do context 'with from date' do @@ -32,6 +32,18 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do expect(subject.first[:value]).to eq(3) end end + + context 'with projects specified in options' do + before do + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: group)) } + end + + subject { described_class.new(group, options: { from: Time.now, current_user: user, projects: [project.id, project_2.id] }).data } + + it 'finds issues from those projects' do + expect(subject.first[:value]).to eq(2) + end + end end context 'with other projects' do @@ -71,6 +83,20 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do expect(subject.second[:value]).to eq(3) end end + + context 'with projects specified in options' do + before do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: create(:project, :repository, namespace: group, name: 'not_applicable')) + end + end + + subject { described_class.new(group, options: { from: Time.now, current_user: user, projects: [project.id, project_2.id] }).data } + + it 'shows deploys from those projects' do + expect(subject.second[:value]).to eq(2) + end + end end context 'with other projects' do diff --git a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb index ffd0b84cb57..dea17e4f3dc 100644 --- a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb @@ -71,6 +71,29 @@ describe Gitlab::CycleAnalytics::IssueStage do end end + context 'when only part of projects is chosen' do + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group, projects: [project_2.id] }) } + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + subject { stage.events } + + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(1) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title) + end + end + end + context 'when subgroup is given' do let(:subgroup) { create(:group, parent: group) } let(:project_4) { create(:project, group: subgroup) } diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index f8332757fcd..451e18ed91b 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -404,6 +404,7 @@ describe Gitlab::Workhorse do end it 'set and notify' do + expect(Gitlab::Redis::SharedState).to receive(:with).and_call_original expect_any_instance_of(::Redis).to receive(:publish) .with(described_class::NOTIFICATION_CHANNEL, "test-key=test-value") diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 78862de0657..c30cb70e1c1 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -692,6 +692,34 @@ describe Ci::Build do end end + describe '#has_live_trace?' do + subject { build.has_live_trace? } + + let(:build) { create(:ci_build, :trace_live) } + + it { is_expected.to be_truthy } + + context 'when build does not have live trace' do + let(:build) { create(:ci_build) } + + it { is_expected.to be_falsy } + end + end + + describe '#has_archived_trace?' do + subject { build.has_archived_trace? } + + let(:build) { create(:ci_build, :trace_artifact) } + + it { is_expected.to be_truthy } + + context 'when build does not have archived trace' do + let(:build) { create(:ci_build) } + + it { is_expected.to be_falsy } + end + end + describe '#has_job_artifacts?' do subject { build.has_job_artifacts? } diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index 1ba66565e03..1413da231e0 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -70,6 +70,31 @@ describe Ci::JobArtifact do end end + describe '.archived_trace_exists_for?' do + subject { described_class.archived_trace_exists_for?(job_id) } + + let!(:artifact) { create(:ci_job_artifact, :trace, job: job) } + let(:job) { create(:ci_build) } + + context 'when the specified job_id exists' do + let(:job_id) { job.id } + + it { is_expected.to be_truthy } + + context 'when the job does have archived trace' do + let!(:artifact) { } + + it { is_expected.to be_falsy } + end + end + + context 'when the specified job_id does not exist' do + let(:job_id) { 10000 } + + it { is_expected.to be_falsy } + end + end + describe 'callbacks' do subject { create(:ci_job_artifact, :archive) } diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index f735a89f69f..24ea059e871 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -554,7 +554,7 @@ describe Ci::Runner do end def expect_value_in_queues - Gitlab::Redis::Queues.with do |redis| + Gitlab::Redis::SharedState.with do |redis| runner_queue_key = runner.send(:runner_queue_key) expect(redis.get(runner_queue_key)) end @@ -627,7 +627,7 @@ describe Ci::Runner do end it 'cleans up the queue' do - Gitlab::Redis::Queues.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.get(queue_key)).to be_nil end end diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index e14b19db915..687b0935c55 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -113,7 +113,7 @@ describe RemoteMirror, :mailer do remote_mirror = create(:remote_mirror) - expect(remote_mirror.remote_name).to eq("remote_mirror_secret") + expect(remote_mirror.remote_name).to eq('remote_mirror_secret') end end @@ -201,11 +201,20 @@ describe RemoteMirror, :mailer do end context 'stuck mirrors' do - it 'includes mirrors stuck in started with no last_update_at set' do + it 'includes mirrors that were started over an hour ago' do + mirror = create_mirror(url: 'http://cantbeblank', + update_status: 'started', + last_update_at: 3.hours.ago, + updated_at: 2.hours.ago) + + expect(described_class.stuck.last).to eq(mirror) + end + + it 'includes mirrors started over 3 hours ago for their first sync' do mirror = create_mirror(url: 'http://cantbeblank', update_status: 'started', last_update_at: nil, - updated_at: 25.hours.ago) + updated_at: 4.hours.ago) expect(described_class.stuck.last).to eq(mirror) end diff --git a/spec/presenters/blobs/unfold_presenter_spec.rb b/spec/presenters/blobs/unfold_presenter_spec.rb index 7ece5f623ce..1534c572b30 100644 --- a/spec/presenters/blobs/unfold_presenter_spec.rb +++ b/spec/presenters/blobs/unfold_presenter_spec.rb @@ -54,8 +54,10 @@ describe Blobs::UnfoldPresenter do expect(lines.size).to eq(total_lines) lines.each.with_index do |line, index| - expect(line.text).to include("LC#{index + 1}") - expect(line.text).to eq(line.rich_text) + line_number = index + 1 + + expect(line.text).to eq(line_number.to_s) + expect(line.rich_text).to include("LC#{line_number}") expect(line.type).to be_nil end end diff --git a/spec/services/ci/archive_trace_service_spec.rb b/spec/services/ci/archive_trace_service_spec.rb index 44a77c29086..454db3d5a48 100644 --- a/spec/services/ci/archive_trace_service_spec.rb +++ b/spec/services/ci/archive_trace_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Ci::ArchiveTraceService, '#execute' do - subject { described_class.new.execute(job) } + subject { described_class.new.execute(job, worker_name: ArchiveTraceWorker.name) } context 'when job is finished' do let(:job) { create(:ci_build, :success, :trace_live) } @@ -25,6 +25,34 @@ describe Ci::ArchiveTraceService, '#execute' do expect { subject }.not_to change { Ci::JobArtifact.trace.count } end end + + context 'when job does not have trace' do + let(:job) { create(:ci_build, :success) } + + it 'leaves a warning message in sidekiq log' do + expect(Sidekiq.logger).to receive(:warn).with( + class: ArchiveTraceWorker.name, + message: 'The job does not have live trace but going to be archived.', + job_id: job.id) + + subject + end + end + + context 'when job failed to archive trace but did not raise an exception' do + before do + allow_any_instance_of(Gitlab::Ci::Trace).to receive(:archive!) {} + end + + it 'leaves a warning message in sidekiq log' do + expect(Sidekiq.logger).to receive(:warn).with( + class: ArchiveTraceWorker.name, + message: 'The job does not have archived trace after archiving.', + job_id: job.id) + + subject + end + end end context 'when job is running' do @@ -37,10 +65,10 @@ describe Ci::ArchiveTraceService, '#execute' do issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/51502', extra: { job_id: job.id } ).once - expect(Rails.logger) - .to receive(:error) - .with("Failed to archive trace. id: #{job.id} message: Job is not finished yet") - .and_call_original + expect(Sidekiq.logger).to receive(:warn).with( + class: ArchiveTraceWorker.name, + message: "Failed to archive trace. message: Job is not finished yet.", + job_id: job.id).and_call_original expect(Gitlab::Metrics) .to receive(:counter) diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb index b0bcd7a36ba..40da3d31408 100644 --- a/spec/services/issuable/bulk_update_service_spec.rb +++ b/spec/services/issuable/bulk_update_service_spec.rb @@ -16,22 +16,22 @@ describe Issuable::BulkUpdateService do shared_examples 'updates milestones' do it 'succeeds' do - result = bulk_update(issues, milestone_id: milestone.id) + result = bulk_update(issuables, milestone_id: milestone.id) expect(result[:success]).to be_truthy - expect(result[:count]).to eq(issues.count) + expect(result[:count]).to eq(issuables.count) end - it 'updates the issues milestone' do - bulk_update(issues, milestone_id: milestone.id) + it 'updates the issuables milestone' do + bulk_update(issuables, milestone_id: milestone.id) - issues.each do |issue| - expect(issue.reload.milestone).to eq(milestone) + issuables.each do |issuable| + expect(issuable.reload.milestone).to eq(milestone) end end end - context 'with project issues' do + context 'with project issuables' do describe 'close issues' do let(:issues) { create_list(:issue, 2, project: project) } @@ -171,7 +171,7 @@ describe Issuable::BulkUpdateService do end describe 'updating milestones' do - let(:issues) { [create(:issue, project: project)] } + let(:issuables) { [create(:issue, project: project)] } let(:milestone) { create(:milestone, project: project) } it_behaves_like 'updates milestones' @@ -360,22 +360,32 @@ describe Issuable::BulkUpdateService do end end - context 'with group issues' do + context 'with group issuables ' do let(:group) { create(:group) } - context 'updating milestone' do + describe 'updating milestones' do let(:milestone) { create(:milestone, group: group) } - let(:project1) { create(:project, :repository, group: group) } - let(:project2) { create(:project, :repository, group: group) } - let(:issue1) { create(:issue, project: project1) } - let(:issue2) { create(:issue, project: project2) } - let(:issues) { [issue1, issue2] } + let(:project) { create(:project, :repository, group: group) } before do group.add_maintainer(user) end - it_behaves_like 'updates milestones' + context 'when issues' do + let(:issue1) { create(:issue, project: project) } + let(:issue2) { create(:issue, project: project) } + let(:issuables) { [issue1, issue2] } + + it_behaves_like 'updates milestones' + end + + context 'when merge requests' do + let(:merge_request1) { create(:merge_request, source_project: project, source_branch: 'branch-1') } + let(:merge_request2) { create(:merge_request, source_project: project, source_branch: 'branch-2') } + let(:issuables) { [merge_request1, merge_request2] } + + it_behaves_like 'updates milestones' + end end end end diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb index ab0550e2613..68c2b6e10e2 100644 --- a/spec/support/shared_examples/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/ci_trace_shared_examples.rb @@ -720,6 +720,58 @@ shared_examples_for 'trace with enabled live trace feature' do end end + describe '#archived_trace_exist?' do + subject { trace.archived_trace_exist? } + + context 'when trace does not exist' do + it { is_expected.to be_falsy } + end + + context 'when archived trace exists' do + before do + create(:ci_job_artifact, :trace, job: build) + end + + it { is_expected.to be_truthy } + end + + context 'when live trace exists' do + before do + Gitlab::Ci::Trace::ChunkedIO.new(build) do |stream| + stream.write('abc') + end + end + + it { is_expected.to be_falsy } + end + end + + describe '#live_trace_exist?' do + subject { trace.live_trace_exist? } + + context 'when trace does not exist' do + it { is_expected.to be_falsy } + end + + context 'when archived trace exists' do + before do + create(:ci_job_artifact, :trace, job: build) + end + + it { is_expected.to be_falsy } + end + + context 'when live trace exists' do + before do + Gitlab::Ci::Trace::ChunkedIO.new(build) do |stream| + stream.write('abc') + end + end + + it { is_expected.to be_truthy } + end + end + describe '#archive!' do subject { trace.archive! } diff --git a/spec/validators/qualified_domain_array_validator_spec.rb b/spec/validators/qualified_domain_array_validator_spec.rb new file mode 100644 index 00000000000..a96b00bfd1d --- /dev/null +++ b/spec/validators/qualified_domain_array_validator_spec.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe QualifiedDomainArrayValidator do + class TestClass + include ActiveModel::Validations + + attr_accessor :domain_array + + def initialize(domain_array) + self.domain_array = domain_array + end + end + + let!(:record) do + TestClass.new(['gitlab.com']) + end + + subject { validator.validate(record) } + + shared_examples 'cannot be blank' do + it 'returns error when attribute is blank' do + record.domain_array = [] + + subject + + expect(record.errors).to be_present + expect(record.errors.first[1]).to eq 'entries cannot be blank' + end + end + + shared_examples 'can be nil' do + it 'allows when attribute is nil' do + record.domain_array = nil + + subject + + expect(record.errors).to be_empty + end + end + + describe 'validations' do + let(:validator) { described_class.new(attributes: [:domain_array]) } + + it_behaves_like 'cannot be blank' + + it 'returns error when attribute is nil' do + record.domain_array = nil + + subject + + expect(record.errors).to be_present + end + + it 'allows when domain is valid' do + subject + + expect(record.errors).to be_empty + end + + it 'returns error when domain contains unicode' do + record.domain_array = ['ğitlab.com'] + + subject + + expect(record.errors).to be_present + expect(record.errors.first[1]).to eq 'unicode domains should use IDNA encoding' + end + + it 'returns error when entry is larger than 255 chars' do + record.domain_array = ['a' * 256] + + subject + + expect(record.errors).to be_present + expect(record.errors.first[1]).to eq 'entries cannot be larger than 255 characters' + end + + it 'returns error when entry contains HTML tags' do + record.domain_array = ['gitlab.com<h1>something</h1>'] + + subject + + expect(record.errors).to be_present + expect(record.errors.first[1]).to eq 'entries cannot contain HTML tags' + end + end + + context 'when allow_nil is set to true' do + let(:validator) { described_class.new(attributes: [:domain_array], allow_nil: true) } + + it_behaves_like 'can be nil' + + it_behaves_like 'cannot be blank' + end + + context 'when allow_blank is set to true' do + let(:validator) { described_class.new(attributes: [:domain_array], allow_blank: true) } + + it_behaves_like 'can be nil' + + it 'allows when attribute is blank' do + record.domain_array = [] + + subject + + expect(record.errors).to be_empty + end + end +end diff --git a/spec/workers/archive_trace_worker_spec.rb b/spec/workers/archive_trace_worker_spec.rb index 368ed3f3db1..44f7be15201 100644 --- a/spec/workers/archive_trace_worker_spec.rb +++ b/spec/workers/archive_trace_worker_spec.rb @@ -11,7 +11,7 @@ describe ArchiveTraceWorker do it 'executes service' do expect_any_instance_of(Ci::ArchiveTraceService) - .to receive(:execute).with(job) + .to receive(:execute).with(job, anything) subject end diff --git a/spec/workers/ci/archive_traces_cron_worker_spec.rb b/spec/workers/ci/archive_traces_cron_worker_spec.rb index eca6cf5235f..28381fdc3be 100644 --- a/spec/workers/ci/archive_traces_cron_worker_spec.rb +++ b/spec/workers/ci/archive_traces_cron_worker_spec.rb @@ -34,7 +34,7 @@ describe Ci::ArchiveTracesCronWorker do it 'executes service' do expect_any_instance_of(Ci::ArchiveTraceService) - .to receive(:execute).with(build) + .to receive(:execute).with(build, anything) subject end @@ -60,7 +60,10 @@ describe Ci::ArchiveTracesCronWorker do end it 'puts a log' do - expect(Rails.logger).to receive(:error).with("Failed to archive trace. id: #{build.id} message: Unexpected error") + expect(Sidekiq.logger).to receive(:warn).with( + class: described_class.name, + message: "Failed to archive trace. message: Unexpected error.", + job_id: build.id) subject end diff --git a/spec/workers/stuck_ci_jobs_worker_spec.rb b/spec/workers/stuck_ci_jobs_worker_spec.rb index 72de62f1188..c3d577e2dae 100644 --- a/spec/workers/stuck_ci_jobs_worker_spec.rb +++ b/spec/workers/stuck_ci_jobs_worker_spec.rb @@ -7,8 +7,6 @@ describe StuckCiJobsWorker do let!(:runner) { create :ci_runner } let!(:job) { create :ci_build, runner: runner } - let(:trace_lease_key) { "trace:write:lock:#{job.id}" } - let(:trace_lease_uuid) { SecureRandom.uuid } let(:worker_lease_key) { StuckCiJobsWorker::EXCLUSIVE_LEASE_KEY } let(:worker_lease_uuid) { SecureRandom.uuid } @@ -16,7 +14,6 @@ describe StuckCiJobsWorker do before do stub_exclusive_lease(worker_lease_key, worker_lease_uuid) - stub_exclusive_lease(trace_lease_key, trace_lease_uuid) job.update!(status: status, updated_at: updated_at) end @@ -195,7 +192,6 @@ describe StuckCiJobsWorker do end it 'cancels exclusive leases after worker perform' do - expect_to_cancel_exclusive_lease(trace_lease_key, trace_lease_uuid) expect_to_cancel_exclusive_lease(worker_lease_key, worker_lease_uuid) worker.perform |