From f0809fbc2c72869130134d829232153a2e8193c6 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 9 Sep 2020 00:08:42 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- Gemfile | 1 - Gemfile.lock | 4 - .../javascripts/helpers/startup_css_helper.js | 68 ++++++++ .../pages/projects/graphs/charts/index.js | 5 +- .../concerns/authenticates_with_two_factor.rb | 17 +- app/helpers/application_helper.rb | 6 +- app/helpers/startup_css_helper.rb | 7 + app/models/commit_status.rb | 2 +- app/models/project.rb | 4 +- app/models/snippet.rb | 2 +- app/models/wiki.rb | 2 +- .../layouts/_startup_css_activation.haml | 5 +- ...2-add-target-id-column-to-audit-events.yml | 5 + ...en-startup-css-enabled-due-to-cloaking.yml | 5 + .../unreleased/244638-change-time-to-salt.yml | 5 + .../unreleased/gaga5lala-issue-222306.yml | 5 + .../unreleased/matrix-job-names-rollout.yml | 5 + .../development/ci_matrix_job_names.yml | 7 - ...819113644_add_target_id_to_audit_events.rb | 125 +++++++++++++++ db/schema_migrations/20200819113644 | 1 + db/structure.sql | 5 + doc/administration/audit_reports.md | 1 + doc/ci/yaml/README.md | 22 +-- doc/development/integrations/secure.md | 8 +- lib/backup/repository.rb | 4 +- .../ci/config/normalizer/matrix_strategy.rb | 14 +- lib/gitlab/ci/features.rb | 4 - lib/gitlab/gl_repository.rb | 6 +- lib/gitlab/gl_repository/repo_type.rb | 2 + lib/gitlab/gon_helper.rb | 4 + spec/db/schema_spec.rb | 4 +- .../helpers/startup_css_helper_spec.js | 123 ++++++++++++++ .../config/normalizer/matrix_strategy_spec.rb | 151 ++++++------------ spec/lib/gitlab/ci/config/normalizer_spec.rb | 50 ++---- .../gitlab/gl_repository/repo_type_spec.rb | 14 +- spec/lib/gitlab/gl_repository_spec.rb | 11 -- spec/requests/api/internal/base_spec.rb | 2 +- .../update_column_in_batches_spec.rb | 18 +-- .../lib/gitlab/repo_type_shared_examples.rb | 4 + 39 files changed, 487 insertions(+), 241 deletions(-) create mode 100644 app/assets/javascripts/helpers/startup_css_helper.js create mode 100644 app/helpers/startup_css_helper.rb create mode 100644 changelogs/unreleased/220322-add-target-id-column-to-audit-events.yml create mode 100644 changelogs/unreleased/242284-charts-not-rendered-when-startup-css-enabled-due-to-cloaking.yml create mode 100644 changelogs/unreleased/244638-change-time-to-salt.yml create mode 100644 changelogs/unreleased/gaga5lala-issue-222306.yml create mode 100644 changelogs/unreleased/matrix-job-names-rollout.yml delete mode 100644 config/feature_flags/development/ci_matrix_job_names.yml create mode 100644 db/post_migrate/20200819113644_add_target_id_to_audit_events.rb create mode 100644 db/schema_migrations/20200819113644 create mode 100644 spec/frontend/helpers/startup_css_helper_spec.js diff --git a/Gemfile b/Gemfile index 2ef351ed4b0..5bd568066fc 100644 --- a/Gemfile +++ b/Gemfile @@ -341,7 +341,6 @@ group :development do # Better errors handler gem 'better_errors', '~> 2.7.1' - gem 'binding_of_caller', '~> 0.8.0' # thin instead webrick gem 'thin', '~> 1.7.0' diff --git a/Gemfile.lock b/Gemfile.lock index 3ea1cb62f4d..4b97e6cf24e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -135,8 +135,6 @@ GEM rack (>= 0.9.0) bindata (2.4.3) binding_ninja (0.2.3) - binding_of_caller (0.8.0) - debug_inspector (>= 0.0.1) bootsnap (1.4.6) msgpack (~> 1.0) bootstrap_form (4.2.0) @@ -218,7 +216,6 @@ GEM octokit (~> 4.7) terminal-table (~> 1) database_cleaner (1.7.0) - debug_inspector (0.0.3) debugger-ruby_core_source (1.3.8) deckar01-task_list (2.3.1) html-pipeline @@ -1263,7 +1260,6 @@ DEPENDENCIES benchmark-ips (~> 2.3.0) benchmark-memory (~> 0.1) better_errors (~> 2.7.1) - binding_of_caller (~> 0.8.0) bootsnap (~> 1.4.6) bootstrap_form (~> 4.2.0) brakeman (~> 4.2) diff --git a/app/assets/javascripts/helpers/startup_css_helper.js b/app/assets/javascripts/helpers/startup_css_helper.js new file mode 100644 index 00000000000..66cf0630a79 --- /dev/null +++ b/app/assets/javascripts/helpers/startup_css_helper.js @@ -0,0 +1,68 @@ +/* Wait for.... The methods can be used: + - with a callback (preferred), + waitFor(action) + + - with then (discouraged), + await waitFor().then(action); + + - with await, + await waitFor; + action(); +*/ + +const CSS_LOADED_EVENT = 'CSSLoaded'; +const DOM_LOADED_EVENT = 'DOMContentLoaded'; +const STARTUP_LINK_LOADED_EVENT = 'CSSStartupLinkLoaded'; + +const isStartupLinkLoaded = ({ dataset }) => dataset.startupcss === 'loaded'; + +export const handleLoadedEvents = (action = () => {}) => { + let isCssLoaded = false; + let eventsList = [CSS_LOADED_EVENT, DOM_LOADED_EVENT]; + return ({ type } = {}) => { + eventsList = eventsList.filter(e => e !== type); + if (isCssLoaded) { + return; + } + if (!eventsList.length) { + isCssLoaded = true; + action(); + } + }; +}; + +export const handleStartupEvents = (action = () => {}) => { + if (!gon.features.startupCss) { + return action; + } + const startupLinks = Array.from(document.querySelectorAll('link[data-startupcss]')); + return () => { + if (startupLinks.every(isStartupLinkLoaded)) { + action(); + } + }; +}; + +export const waitForStartupLinks = () => { + let eventListener; + const promise = new Promise(resolve => { + eventListener = handleStartupEvents(resolve); + document.addEventListener(STARTUP_LINK_LOADED_EVENT, eventListener); + }).then(() => { + document.dispatchEvent(new CustomEvent(CSS_LOADED_EVENT)); + document.removeEventListener(STARTUP_LINK_LOADED_EVENT, eventListener); + }); + document.dispatchEvent(new CustomEvent(STARTUP_LINK_LOADED_EVENT)); + return promise; +}; + +export const waitForCSSLoaded = (action = () => {}) => { + let eventListener; + const promise = new Promise(resolve => { + eventListener = handleLoadedEvents(resolve); + document.addEventListener(DOM_LOADED_EVENT, eventListener, { once: true }); + document.addEventListener(CSS_LOADED_EVENT, eventListener, { once: true }); + }).then(action); + waitForStartupLinks(); + return promise; +}; diff --git a/app/assets/javascripts/pages/projects/graphs/charts/index.js b/app/assets/javascripts/pages/projects/graphs/charts/index.js index 09b440d1413..0d4c4332c99 100644 --- a/app/assets/javascripts/pages/projects/graphs/charts/index.js +++ b/app/assets/javascripts/pages/projects/graphs/charts/index.js @@ -1,18 +1,17 @@ import Vue from 'vue'; import { GlColumnChart } from '@gitlab/ui/dist/charts'; +import { waitForCSSLoaded } from '../../../../helpers/startup_css_helper'; import { __ } from '~/locale'; import CodeCoverage from '../components/code_coverage.vue'; import SeriesDataMixin from './series_data_mixin'; -document.addEventListener('DOMContentLoaded', () => { +waitForCSSLoaded(() => { const languagesContainer = document.getElementById('js-languages-chart'); const codeCoverageContainer = document.getElementById('js-code-coverage-chart'); const monthContainer = document.getElementById('js-month-chart'); const weekdayContainer = document.getElementById('js-weekday-chart'); const hourContainer = document.getElementById('js-hour-chart'); - const LANGUAGE_CHART_HEIGHT = 300; - const reorderWeekDays = (weekDays, firstDayOfWeek = 0) => { if (firstDayOfWeek === 0) { return weekDays; diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index 5bdabe2bfd1..9ff97f398f5 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -22,7 +22,7 @@ module AuthenticatesWithTwoFactor return handle_locked_user(user) unless user.can?(:log_in) session[:otp_user_id] = user.id - session[:user_updated_at] = user.updated_at + session[:user_password_hash] = Digest::SHA256.hexdigest(user.encrypted_password) push_frontend_feature_flag(:webauthn) if user.two_factor_webauthn_enabled? @@ -47,7 +47,7 @@ module AuthenticatesWithTwoFactor def authenticate_with_two_factor user = self.resource = find_user return handle_locked_user(user) unless user.can?(:log_in) - return handle_changed_user(user) if user_changed?(user) + return handle_changed_user(user) if user_password_changed?(user) if user_params[:otp_attempt].present? && session[:otp_user_id] authenticate_with_two_factor_via_otp(user) @@ -76,7 +76,7 @@ module AuthenticatesWithTwoFactor def clear_two_factor_attempt! session.delete(:otp_user_id) - session.delete(:user_updated_at) + session.delete(:user_password_hash) session.delete(:challenge) end @@ -142,7 +142,6 @@ module AuthenticatesWithTwoFactor gon.push(webauthn: { options: get_options.to_json }) end end - # rubocop: enable CodeReuse/ActiveRecord def handle_two_factor_success(user) @@ -168,13 +167,9 @@ module AuthenticatesWithTwoFactor # If user has been updated since we validated the password, # the password might have changed. - def user_changed?(user) - return false unless session[:user_updated_at] + def user_password_changed?(user) + return false unless session[:user_password_hash] - # See: https://gitlab.com/gitlab-org/gitlab/-/issues/244638 - # Rounding errors happen when the user is updated, as the Rails ActiveRecord - # object has higher precision than what is stored in the database, therefore - # using .to_i to force truncation to the timestamp - user.updated_at.to_i != session[:user_updated_at].to_i + Digest::SHA256.hexdigest(user.encrypted_password) != session[:user_password_hash] end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 09f21e4b727..a81225c8954 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -4,6 +4,8 @@ require 'digest/md5' require 'uri' module ApplicationHelper + include StartupCssHelper + # See https://docs.gitlab.com/ee/development/ee_features.html#code-in-app-views # rubocop: disable CodeReuse/ActiveRecord def render_if_exists(partial, locals = {}) @@ -235,10 +237,6 @@ module ApplicationHelper "#{request.path}?#{options.compact.to_param}" end - def use_startup_css? - (Feature.enabled?(:startup_css) || params[:startup_css] == 'true' || cookies['startup_css'] == 'true') && !Rails.env.test? - end - def stylesheet_link_tag_defer(path) if use_startup_css? stylesheet_link_tag(path, media: "print", crossorigin: ActionController::Base.asset_host ? 'anonymous' : nil) diff --git a/app/helpers/startup_css_helper.rb b/app/helpers/startup_css_helper.rb new file mode 100644 index 00000000000..b54e19bfc0d --- /dev/null +++ b/app/helpers/startup_css_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module StartupCssHelper + def use_startup_css? + (Feature.enabled?(:startup_css) || params[:startup_css] == 'true' || cookies['startup_css'] == 'true') && !Rails.env.test? + end +end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index eb88cfce912..4539336b717 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -203,7 +203,7 @@ class CommitStatus < ApplicationRecord common_name = name.to_s.gsub(%r{\d+[\s:\/\\]+\d+\s*}, '') # 'rspec:linux: [aws, max memory]' => 'rspec:linux' - common_name.gsub!(%r{: \[.*, .*\]\s*\z}, '') if Gitlab::Ci::Features.new_matrix_job_names_enabled? + common_name.gsub!(%r{: \[.*, .*\]\s*\z}, '') common_name.strip! common_name diff --git a/app/models/project.rb b/app/models/project.rb index 6f0497d3646..d588cb791de 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -896,12 +896,12 @@ class Project < ApplicationRecord end def repository - @repository ||= Repository.new(full_path, self, shard: repository_storage, disk_path: disk_path) + @repository ||= Gitlab::GlRepository::PROJECT.repository_for(self) end def design_repository strong_memoize(:design_repository) do - DesignManagement::Repository.new(self) + Gitlab::GlRepository::DESIGN.repository_for(self) end end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 0179176ba9e..f4c3fa5acc3 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -275,7 +275,7 @@ class Snippet < ApplicationRecord override :repository def repository - @repository ||= Repository.new(full_path, self, shard: repository_storage, disk_path: disk_path, repo_type: Gitlab::GlRepository::SNIPPET) + @repository ||= Gitlab::GlRepository::SNIPPET.repository_for(self) end override :repository_size_checker diff --git a/app/models/wiki.rb b/app/models/wiki.rb index 30273d646cf..22ae9b65564 100644 --- a/app/models/wiki.rb +++ b/app/models/wiki.rb @@ -180,7 +180,7 @@ class Wiki override :repository def repository - @repository ||= Repository.new(full_path, container, shard: repository_storage, disk_path: disk_path, repo_type: Gitlab::GlRepository::WIKI) + @repository ||= Gitlab::GlRepository::WIKI.repository_for(container) end def repository_storage diff --git a/app/views/layouts/_startup_css_activation.haml b/app/views/layouts/_startup_css_activation.haml index 0b1cce06f47..022b9a695bc 100644 --- a/app/views/layouts/_startup_css_activation.haml +++ b/app/views/layouts/_startup_css_activation.haml @@ -3,5 +3,8 @@ = javascript_tag nonce: true do :plain document.querySelectorAll('link[media="print"]').forEach(linkTag => { - linkTag.addEventListener('load', function() {this.media='all'}, {once: true}); + linkTag.setAttribute('data-startupcss', 'loading'); + const startupLinkLoadedEvent = new CustomEvent('CSSStartupLinkLoaded'); + linkTag.addEventListener('load',function(){this.media='all';this.setAttribute('data-startupcss', 'loaded');document.dispatchEvent(startupLinkLoadedEvent);},{once: true}); }) +- return unless use_startup_css? diff --git a/changelogs/unreleased/220322-add-target-id-column-to-audit-events.yml b/changelogs/unreleased/220322-add-target-id-column-to-audit-events.yml new file mode 100644 index 00000000000..3ea7c5dbd82 --- /dev/null +++ b/changelogs/unreleased/220322-add-target-id-column-to-audit-events.yml @@ -0,0 +1,5 @@ +--- +title: Add target_id column to audit_events table +merge_request: 40954 +author: +type: other diff --git a/changelogs/unreleased/242284-charts-not-rendered-when-startup-css-enabled-due-to-cloaking.yml b/changelogs/unreleased/242284-charts-not-rendered-when-startup-css-enabled-due-to-cloaking.yml new file mode 100644 index 00000000000..5755900199b --- /dev/null +++ b/changelogs/unreleased/242284-charts-not-rendered-when-startup-css-enabled-due-to-cloaking.yml @@ -0,0 +1,5 @@ +--- +title: Initialise charts when container display property is set +merge_request: 40787 +author: +type: fixed diff --git a/changelogs/unreleased/244638-change-time-to-salt.yml b/changelogs/unreleased/244638-change-time-to-salt.yml new file mode 100644 index 00000000000..3dd7742bb4d --- /dev/null +++ b/changelogs/unreleased/244638-change-time-to-salt.yml @@ -0,0 +1,5 @@ +--- +title: Change 2FA to verify password hash instead of timestamp +merge_request: 41366 +author: +type: changed diff --git a/changelogs/unreleased/gaga5lala-issue-222306.yml b/changelogs/unreleased/gaga5lala-issue-222306.yml new file mode 100644 index 00000000000..eda8b2f7929 --- /dev/null +++ b/changelogs/unreleased/gaga5lala-issue-222306.yml @@ -0,0 +1,5 @@ +--- +title: Convert spec_helper to fast_spec_helper +merge_request: 41755 +author: gaga5lala +type: other diff --git a/changelogs/unreleased/matrix-job-names-rollout.yml b/changelogs/unreleased/matrix-job-names-rollout.yml new file mode 100644 index 00000000000..177d09802c3 --- /dev/null +++ b/changelogs/unreleased/matrix-job-names-rollout.yml @@ -0,0 +1,5 @@ +--- +title: Parallel matrix jobs show relevant variables in job name +merge_request: 41080 +author: +type: added diff --git a/config/feature_flags/development/ci_matrix_job_names.yml b/config/feature_flags/development/ci_matrix_job_names.yml deleted file mode 100644 index d8f7b2a5543..00000000000 --- a/config/feature_flags/development/ci_matrix_job_names.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: ci_matrix_job_names -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39985 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/239012 -group: 'group::continuous integration' -type: development -default_enabled: false diff --git a/db/post_migrate/20200819113644_add_target_id_to_audit_events.rb b/db/post_migrate/20200819113644_add_target_id_to_audit_events.rb new file mode 100644 index 00000000000..c2d9aff928b --- /dev/null +++ b/db/post_migrate/20200819113644_add_target_id_to_audit_events.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +class AddTargetIdToAuditEvents < ActiveRecord::Migration[6.0] + include Gitlab::Database::SchemaHelpers + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + SOURCE_TABLE_NAME = 'audit_events' + PARTITIONED_TABLE_NAME = 'audit_events_part_5fc467ac26' + TRIGGER_FUNCTION_NAME = 'table_sync_function_2be879775d' + + def up + with_lock_retries do + add_column(SOURCE_TABLE_NAME, :target_id, :bigint) + add_column(PARTITIONED_TABLE_NAME, :target_id, :bigint) + + create_trigger_function(TRIGGER_FUNCTION_NAME, replace: true) do + <<~SQL + IF (TG_OP = 'DELETE') THEN + DELETE FROM #{PARTITIONED_TABLE_NAME} where id = OLD.id; + ELSIF (TG_OP = 'UPDATE') THEN + UPDATE #{PARTITIONED_TABLE_NAME} + SET author_id = NEW.author_id, + type = NEW.type, + entity_id = NEW.entity_id, + entity_type = NEW.entity_type, + details = NEW.details, + ip_address = NEW.ip_address, + author_name = NEW.author_name, + entity_path = NEW.entity_path, + target_details = NEW.target_details, + target_type = NEW.target_type, + target_id = NEW.target_id, + created_at = NEW.created_at + WHERE #{PARTITIONED_TABLE_NAME}.id = NEW.id; + ELSIF (TG_OP = 'INSERT') THEN + INSERT INTO #{PARTITIONED_TABLE_NAME} (id, + author_id, + type, + entity_id, + entity_type, + details, + ip_address, + author_name, + entity_path, + target_details, + target_type, + target_id, + created_at) + VALUES (NEW.id, + NEW.author_id, + NEW.type, + NEW.entity_id, + NEW.entity_type, + NEW.details, + NEW.ip_address, + NEW.author_name, + NEW.entity_path, + NEW.target_details, + NEW.target_type, + NEW.target_id, + NEW.created_at); + END IF; + RETURN NULL; + SQL + end + end + end + + def down + with_lock_retries do + remove_column SOURCE_TABLE_NAME, :target_id + + create_trigger_function(TRIGGER_FUNCTION_NAME, replace: true) do + <<~SQL + IF (TG_OP = 'DELETE') THEN + DELETE FROM #{PARTITIONED_TABLE_NAME} where id = OLD.id; + ELSIF (TG_OP = 'UPDATE') THEN + UPDATE #{PARTITIONED_TABLE_NAME} + SET author_id = NEW.author_id, + type = NEW.type, + entity_id = NEW.entity_id, + entity_type = NEW.entity_type, + details = NEW.details, + ip_address = NEW.ip_address, + author_name = NEW.author_name, + entity_path = NEW.entity_path, + target_details = NEW.target_details, + target_type = NEW.target_type, + created_at = NEW.created_at + WHERE #{PARTITIONED_TABLE_NAME}.id = NEW.id; + ELSIF (TG_OP = 'INSERT') THEN + INSERT INTO #{PARTITIONED_TABLE_NAME} (id, + author_id, + type, + entity_id, + entity_type, + details, + ip_address, + author_name, + entity_path, + target_details, + target_type, + created_at) + VALUES (NEW.id, + NEW.author_id, + NEW.type, + NEW.entity_id, + NEW.entity_type, + NEW.details, + NEW.ip_address, + NEW.author_name, + NEW.entity_path, + NEW.target_details, + NEW.target_type, + NEW.created_at); + END IF; + RETURN NULL; + SQL + end + + remove_column PARTITIONED_TABLE_NAME, :target_id + end + end +end diff --git a/db/schema_migrations/20200819113644 b/db/schema_migrations/20200819113644 new file mode 100644 index 00000000000..2208ad26134 --- /dev/null +++ b/db/schema_migrations/20200819113644 @@ -0,0 +1 @@ +19c90689d0af6adb017dbd7127c6cd147d9c92581118dbfd99c87bc6a6dda3be \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 52cf480c2fa..2994f257464 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -30,6 +30,7 @@ ELSIF (TG_OP = 'UPDATE') THEN entity_path = NEW.entity_path, target_details = NEW.target_details, target_type = NEW.target_type, + target_id = NEW.target_id, created_at = NEW.created_at WHERE audit_events_part_5fc467ac26.id = NEW.id; ELSIF (TG_OP = 'INSERT') THEN @@ -44,6 +45,7 @@ ELSIF (TG_OP = 'INSERT') THEN entity_path, target_details, target_type, + target_id, created_at) VALUES (NEW.id, NEW.author_id, @@ -56,6 +58,7 @@ ELSIF (TG_OP = 'INSERT') THEN NEW.entity_path, NEW.target_details, NEW.target_type, + NEW.target_id, NEW.created_at); END IF; RETURN NULL; @@ -78,6 +81,7 @@ CREATE TABLE public.audit_events_part_5fc467ac26 ( target_details text, created_at timestamp without time zone NOT NULL, target_type text, + target_id bigint, CONSTRAINT check_492aaa021d CHECK ((char_length(entity_path) <= 5500)), CONSTRAINT check_83ff8406e2 CHECK ((char_length(author_name) <= 255)), CONSTRAINT check_97a8c868e7 CHECK ((char_length(target_type) <= 255)), @@ -9518,6 +9522,7 @@ CREATE TABLE public.audit_events ( entity_path text, target_details text, target_type text, + target_id bigint, CONSTRAINT check_492aaa021d CHECK ((char_length(entity_path) <= 5500)), CONSTRAINT check_82294106dd CHECK ((char_length(target_type) <= 255)), CONSTRAINT check_83ff8406e2 CHECK ((char_length(author_name) <= 255)), diff --git a/doc/administration/audit_reports.md b/doc/administration/audit_reports.md index d5a08b711be..44a623298b6 100644 --- a/doc/administration/audit_reports.md +++ b/doc/administration/audit_reports.md @@ -1,6 +1,7 @@ --- stage: Manage group: Compliance +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers description: 'Learn how to create evidence artifacts typically requested by a 3rd party auditor.' --- diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index ce05bc2c5c6..293dafb75b5 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -3594,18 +3594,20 @@ deploystacks: This generates 10 parallel `deploystacks` jobs, each with different values for `PROVIDER` and `STACK`: ```plaintext -deploystacks 1/10 with PROVIDER=aws and STACK=monitoring -deploystacks 2/10 with PROVIDER=aws and STACK=app1 -deploystacks 3/10 with PROVIDER=aws and STACK=app2 -deploystacks 4/10 with PROVIDER=ovh and STACK=monitoring -deploystacks 5/10 with PROVIDER=ovh and STACK=backup -deploystacks 6/10 with PROVIDER=ovh and STACK=app -deploystacks 7/10 with PROVIDER=gcp and STACK=data -deploystacks 8/10 with PROVIDER=gcp and STACK=processing -deploystacks 9/10 with PROVIDER=vultr and STACK=data -deploystacks 10/10 with PROVIDER=vultr and STACK=processing +deploystacks: [aws, monitoring] +deploystacks: [aws, app1] +deploystacks: [aws, app2] +deploystacks: [ovh, monitoring] +deploystacks: [ovh, backup] +deploystacks: [ovh, app] +deploystacks: [gcp, data] +deploystacks: [gcp, processing] +deploystacks: [vultr, data] +deploystacks: [vultr, processing] ``` +Job naming style [was improved](https://gitlab.com/gitlab-org/gitlab/-/issues/230452) in GitLab 13.4. + ### `trigger` > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8997) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8. diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md index 21076fa681f..dcfd0f40bf0 100644 --- a/doc/development/integrations/secure.md +++ b/doc/development/integrations/secure.md @@ -265,10 +265,16 @@ This documentation gives an overview of the report JSON format, as well as recommendations and examples to help integrators set its fields. The format is extensively described in the documentation of [SAST](../../user/application_security/sast/index.md#reports-json-format), +[DAST](../../user/application_security/dast/#reports), [Dependency Scanning](../../user/application_security/dependency_scanning/index.md#reports-json-format), and [Container Scanning](../../user/application_security/container_scanning/index.md#reports-json-format). -The DAST variant of the report JSON format is not documented at the moment. +You can find the schemas for these scanners here: + +- [SAST](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/sast-report-format.json) +- [DAST](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/dast-report-format.json) +- [Dependency Scanning](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/dependency-scanning-report-format.json) +- [Container Scanning](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/container-scanning-report-format.json) ### Version diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 1e237d3c423..eb0b230904e 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -148,7 +148,7 @@ module Backup private def dump_consecutive - Project.includes(:route).find_each(batch_size: 1000) do |project| + Project.includes(:route, :group, namespace: :owner).find_each(batch_size: 1000) do |project| dump_project(project) end end @@ -178,7 +178,7 @@ module Backup end end - Project.for_repository_storage(storage).includes(:route).find_each(batch_size: 100) do |project| + Project.for_repository_storage(storage).includes(:route, :group, namespace: :owner).find_each(batch_size: 100) do |project| break unless errors.empty? queue.push(project) diff --git a/lib/gitlab/ci/config/normalizer/matrix_strategy.rb b/lib/gitlab/ci/config/normalizer/matrix_strategy.rb index 2865b2dcdb7..5a23836d8a0 100644 --- a/lib/gitlab/ci/config/normalizer/matrix_strategy.rb +++ b/lib/gitlab/ci/config/normalizer/matrix_strategy.rb @@ -49,16 +49,6 @@ module Gitlab end def name - if Gitlab::Ci::Features.new_matrix_job_names_enabled? - name_with_variable_details - else - old_name - end - end - - private - - def name_with_variable_details vars = variables .values .compact @@ -67,9 +57,7 @@ module Gitlab "#{job_name}: [#{vars}]" end - def old_name - "#{job_name} #{instance}/#{total}" - end + private attr_reader :job_name, :instance, :variables, :total end diff --git a/lib/gitlab/ci/features.rb b/lib/gitlab/ci/features.rb index 3f1d3545fc7..f50014a9ae3 100644 --- a/lib/gitlab/ci/features.rb +++ b/lib/gitlab/ci/features.rb @@ -67,10 +67,6 @@ module Gitlab Feature.enabled?(:project_transactionless_destroy, project, default_enabled: false) end - def self.new_matrix_job_names_enabled? - ::Feature.enabled?(:ci_matrix_job_names, default_enabled: false) - end - def self.coverage_report_view?(project) ::Feature.enabled?(:coverage_report_view, project) end diff --git a/lib/gitlab/gl_repository.rb b/lib/gitlab/gl_repository.rb index 7346de13626..54dca93a891 100644 --- a/lib/gitlab/gl_repository.rb +++ b/lib/gitlab/gl_repository.rb @@ -7,19 +7,19 @@ module Gitlab PROJECT = RepoType.new( name: :project, access_checker_class: Gitlab::GitAccessProject, - repository_resolver: -> (project) { project&.repository } + repository_resolver: -> (project) { ::Repository.new(project.full_path, project, shard: project.repository_storage, disk_path: project.disk_path) } ).freeze WIKI = RepoType.new( name: :wiki, access_checker_class: Gitlab::GitAccessWiki, - repository_resolver: -> (container) { container&.wiki&.repository }, + repository_resolver: -> (container) { ::Repository.new(container.wiki.full_path, container, shard: container.wiki.repository_storage, disk_path: container.wiki.disk_path, repo_type: WIKI) }, project_resolver: -> (container) { container.is_a?(Project) ? container : nil }, suffix: :wiki ).freeze SNIPPET = RepoType.new( name: :snippet, access_checker_class: Gitlab::GitAccessSnippet, - repository_resolver: -> (snippet) { snippet&.repository }, + repository_resolver: -> (snippet) { ::Repository.new(snippet.full_path, snippet, shard: snippet.repository_storage, disk_path: snippet.disk_path, repo_type: SNIPPET) }, container_class: Snippet, project_resolver: -> (snippet) { snippet&.project }, guest_read_ability: :read_snippet diff --git a/lib/gitlab/gl_repository/repo_type.rb b/lib/gitlab/gl_repository/repo_type.rb index 2c0038b61e2..346f6be0d98 100644 --- a/lib/gitlab/gl_repository/repo_type.rb +++ b/lib/gitlab/gl_repository/repo_type.rb @@ -57,6 +57,8 @@ module Gitlab end def repository_for(container) + return unless container + repository_resolver.call(container) end diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index dfba68ce899..74c68f24483 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -4,6 +4,7 @@ module Gitlab module GonHelper + include StartupCssHelper include WebpackHelper def add_gon_variables @@ -48,6 +49,9 @@ module Gitlab push_frontend_feature_flag(:snippets_edit_vue, default_enabled: false) push_frontend_feature_flag(:webperf_experiment, default_enabled: false) push_frontend_feature_flag(:snippets_binary_blob, default_enabled: false) + + # Startup CSS feature is a special one as it can be enabled by means of cookies and params + gon.push({ features: { 'startupCss' => use_startup_css? } }, true) end # Exposes the state of a feature flag to the frontend code. diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index ff39bf81d3f..69cd08d82e1 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -18,8 +18,8 @@ RSpec.describe 'Database schema' do approvals: %w[user_id], approver_groups: %w[target_id], approvers: %w[target_id user_id], - audit_events: %w[author_id entity_id], - audit_events_part_5fc467ac26: %w[author_id entity_id], + audit_events: %w[author_id entity_id target_id], + audit_events_part_5fc467ac26: %w[author_id entity_id target_id], award_emoji: %w[awardable_id user_id], aws_roles: %w[role_external_id], boards: %w[milestone_id], diff --git a/spec/frontend/helpers/startup_css_helper_spec.js b/spec/frontend/helpers/startup_css_helper_spec.js new file mode 100644 index 00000000000..9eff6efd21a --- /dev/null +++ b/spec/frontend/helpers/startup_css_helper_spec.js @@ -0,0 +1,123 @@ +import { + handleLoadedEvents, + waitForCSSLoaded, +} from '../../../app/assets/javascripts/helpers/startup_css_helper'; + +describe('handleLoadedEvents', () => { + let mock; + beforeEach(() => { + mock = jest.fn(); + }); + + it('should not call the callback on wrong conditions', () => { + const resolverToCall = handleLoadedEvents(mock); + resolverToCall({ type: 'UnrelatedEvent' }); + resolverToCall({ type: 'UnrelatedEvent' }); + resolverToCall({ type: 'UnrelatedEvent' }); + resolverToCall({ type: 'UnrelatedEvent' }); + resolverToCall({ type: 'CSSLoaded' }); + resolverToCall(); + expect(mock).not.toHaveBeenCalled(); + }); + + it('should call the callback when all the events have been triggered', () => { + const resolverToCall = handleLoadedEvents(mock); + resolverToCall(); + resolverToCall({ type: 'DOMContentLoaded' }); + resolverToCall({ type: 'CSSLoaded' }); + resolverToCall(); + expect(mock).toHaveBeenCalledTimes(1); + }); +}); + +describe('waitForCSSLoaded', () => { + let mock; + beforeEach(() => { + mock = jest.fn(); + }); + + describe('with startup css disabled', () => { + beforeEach(() => { + gon.features = { + startupCss: false, + }; + }); + + it('should call CssLoaded when the conditions are met', async () => { + const docAddListener = jest.spyOn(document, 'addEventListener'); + const docRemoveListener = jest.spyOn(document, 'removeEventListener'); + const docDispatch = jest.spyOn(document, 'dispatchEvent'); + const events = waitForCSSLoaded(mock); + + expect(docAddListener).toHaveBeenCalledTimes(3); + expect(docDispatch.mock.calls[0][0].type).toBe('CSSStartupLinkLoaded'); + + document.dispatchEvent(new CustomEvent('DOMContentLoaded')); + await events; + + expect(docDispatch).toHaveBeenCalledTimes(3); + expect(docDispatch.mock.calls[2][0].type).toBe('CSSLoaded'); + expect(docRemoveListener).toHaveBeenCalledTimes(1); + expect(mock).toHaveBeenCalledTimes(1); + }); + }); + + describe('with startup css enabled', () => { + let docAddListener; + let docRemoveListener; + let docDispatch; + + beforeEach(() => { + docAddListener = jest.spyOn(document, 'addEventListener'); + docRemoveListener = jest.spyOn(document, 'removeEventListener'); + docDispatch = jest.spyOn(document, 'dispatchEvent'); + gon.features = { + startupCss: true, + }; + }); + + it('should call CssLoaded if the assets are cached', async () => { + const events = waitForCSSLoaded(mock); + const fixtures = ` + + + `; + setFixtures(fixtures); + + expect(docAddListener).toHaveBeenCalledTimes(3); + expect(docDispatch.mock.calls[0][0].type).toBe('CSSStartupLinkLoaded'); + + document.dispatchEvent(new CustomEvent('DOMContentLoaded')); + await events; + + expect(docDispatch).toHaveBeenCalledTimes(3); + expect(docDispatch.mock.calls[2][0].type).toBe('CSSLoaded'); + expect(docRemoveListener).toHaveBeenCalledTimes(1); + expect(mock).toHaveBeenCalledTimes(1); + }); + + it('should wait to call CssLoaded until the assets are loaded', async () => { + const events = waitForCSSLoaded(mock); + const fixtures = ` + + + `; + setFixtures(fixtures); + + expect(docAddListener).toHaveBeenCalledTimes(3); + expect(docDispatch.mock.calls[0][0].type).toBe('CSSStartupLinkLoaded'); + + document + .querySelectorAll('[data-startupcss="loading"]') + .forEach(elem => elem.setAttribute('data-startupcss', 'loaded')); + document.dispatchEvent(new CustomEvent('DOMContentLoaded')); + document.dispatchEvent(new CustomEvent('CSSStartupLinkLoaded')); + await events; + + expect(docDispatch).toHaveBeenCalledTimes(4); + expect(docDispatch.mock.calls[3][0].type).toBe('CSSLoaded'); + expect(docRemoveListener).toHaveBeenCalledTimes(1); + expect(mock).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb b/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb index 81f17e0f528..fbf86927bd9 100644 --- a/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb +++ b/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'spec_helper' +require 'fast_spec_helper' RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do describe '.applies_to?' do @@ -39,114 +39,53 @@ RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do it { expect(subject.size).to eq(4) } - context 'with new_matrix_job_names_enabled ff disabled' do - before do - stub_feature_flags(ci_matrix_job_names: false) - end - - it 'has attributes' do - expect(subject.map(&:attributes)).to match_array( - [ - { - name: 'test 1/4', - instance: 1, - parallel: { total: 4 }, - variables: { - 'PROVIDER' => 'aws', - 'STACK' => 'app1' - } - }, - { - name: 'test 2/4', - instance: 2, - parallel: { total: 4 }, - variables: { - 'PROVIDER' => 'aws', - 'STACK' => 'app2' - } - }, - { - name: 'test 3/4', - instance: 3, - parallel: { total: 4 }, - variables: { - 'PROVIDER' => 'ovh', - 'STACK' => 'app' - } - }, - { - name: 'test 4/4', - instance: 4, - parallel: { total: 4 }, - variables: { - 'PROVIDER' => 'gcp', - 'STACK' => 'app' - } + it 'has attributes' do + expect(subject.map(&:attributes)).to match_array( + [ + { + name: 'test: [aws, app1]', + instance: 1, + parallel: { total: 4 }, + variables: { + 'PROVIDER' => 'aws', + 'STACK' => 'app1' } - ] - ) - end - - it 'has parallelized name' do - expect(subject.map(&:name)).to match_array( - ['test 1/4', 'test 2/4', 'test 3/4', 'test 4/4'] - ) - end + }, + { + name: 'test: [aws, app2]', + instance: 2, + parallel: { total: 4 }, + variables: { + 'PROVIDER' => 'aws', + 'STACK' => 'app2' + } + }, + { + name: 'test: [ovh, app]', + instance: 3, + parallel: { total: 4 }, + variables: { + 'PROVIDER' => 'ovh', + 'STACK' => 'app' + } + }, + { + name: 'test: [gcp, app]', + instance: 4, + parallel: { total: 4 }, + variables: { + 'PROVIDER' => 'gcp', + 'STACK' => 'app' + } + } + ] + ) end - context 'with new_matrix_job_names_enabled ff enabled' do - before do - stub_feature_flags(ci_matrix_job_names: true) - end - - it 'has attributes' do - expect(subject.map(&:attributes)).to match_array( - [ - { - name: 'test: [aws, app1]', - instance: 1, - parallel: { total: 4 }, - variables: { - 'PROVIDER' => 'aws', - 'STACK' => 'app1' - } - }, - { - name: 'test: [aws, app2]', - instance: 2, - parallel: { total: 4 }, - variables: { - 'PROVIDER' => 'aws', - 'STACK' => 'app2' - } - }, - { - name: 'test: [ovh, app]', - instance: 3, - parallel: { total: 4 }, - variables: { - 'PROVIDER' => 'ovh', - 'STACK' => 'app' - } - }, - { - name: 'test: [gcp, app]', - instance: 4, - parallel: { total: 4 }, - variables: { - 'PROVIDER' => 'gcp', - 'STACK' => 'app' - } - } - ] - ) - end - - it 'has parallelized name' do - expect(subject.map(&:name)).to match_array( - ['test: [aws, app1]', 'test: [aws, app2]', 'test: [gcp, app]', 'test: [ovh, app]'] - ) - end + it 'has parallelized name' do + expect(subject.map(&:name)).to match_array( + ['test: [aws, app1]', 'test: [aws, app2]', 'test: [gcp, app]', 'test: [ovh, app]'] + ) end end end diff --git a/spec/lib/gitlab/ci/config/normalizer_spec.rb b/spec/lib/gitlab/ci/config/normalizer_spec.rb index 7d186f37b14..4c19657413c 100644 --- a/spec/lib/gitlab/ci/config/normalizer_spec.rb +++ b/spec/lib/gitlab/ci/config/normalizer_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'spec_helper' +require 'fast_spec_helper' RSpec.describe Gitlab::Ci::Config::Normalizer do let(:job_name) { :rspec } @@ -185,6 +185,13 @@ RSpec.describe Gitlab::Ci::Config::Normalizer do } end + let(:expanded_job_names) do + [ + 'rspec: [A, B]', + 'rspec: [A, C]' + ] + end + it 'does not have original job' do is_expected.not_to include(job_name) end @@ -215,45 +222,12 @@ RSpec.describe Gitlab::Ci::Config::Normalizer do expect(configs).to all(match(a_hash_including(original_config))) end - context 'with new_matrix_job_names_enabled ff enabled' do - let(:expanded_job_names) do - [ - 'rspec: [A, B]', - 'rspec: [A, C]' - ] - end - - before do - stub_feature_flags(ci_matrix_job_names: true) - end - - it 'has parallelized jobs' do - is_expected.to include(*expanded_job_names.map(&:to_sym)) - end - - it_behaves_like 'parallel dependencies' - it_behaves_like 'parallel needs' + it 'has parallelized jobs' do + is_expected.to include(*expanded_job_names.map(&:to_sym)) end - context 'with new_matrix_job_names_enabled ff disabled' do - let(:expanded_job_names) do - [ - 'rspec 1/2', - 'rspec 2/2' - ] - end - - before do - stub_feature_flags(ci_matrix_job_names: false) - end - - it 'has parallelized jobs' do - is_expected.to include(*expanded_job_names.map(&:to_sym)) - end - - it_behaves_like 'parallel dependencies' - it_behaves_like 'parallel needs' - end + it_behaves_like 'parallel dependencies' + it_behaves_like 'parallel needs' end context 'when parallel config does not matches a factory' do diff --git a/spec/lib/gitlab/gl_repository/repo_type_spec.rb b/spec/lib/gitlab/gl_repository/repo_type_spec.rb index e920fc7cd3b..3fa636a1cf0 100644 --- a/spec/lib/gitlab/gl_repository/repo_type_spec.rb +++ b/spec/lib/gitlab/gl_repository/repo_type_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Gitlab::GlRepository::RepoType do let(:expected_identifier) { "project-#{expected_id}" } let(:expected_suffix) { '' } let(:expected_container) { project } - let(:expected_repository) { expected_container.repository } + let(:expected_repository) { ::Repository.new(project.full_path, project, shard: project.repository_storage, disk_path: project.disk_path, repo_type: Gitlab::GlRepository::PROJECT) } end it 'knows its type' do @@ -46,7 +46,7 @@ RSpec.describe Gitlab::GlRepository::RepoType do let(:expected_identifier) { "wiki-#{expected_id}" } let(:expected_suffix) { '.wiki' } let(:expected_container) { project } - let(:expected_repository) { expected_container.wiki.repository } + let(:expected_repository) { ::Repository.new(project.wiki.full_path, project, shard: project.wiki.repository_storage, disk_path: project.wiki.disk_path, repo_type: Gitlab::GlRepository::WIKI) } end it 'knows its type' do @@ -75,7 +75,7 @@ RSpec.describe Gitlab::GlRepository::RepoType do let(:expected_id) { personal_snippet.id } let(:expected_identifier) { "snippet-#{expected_id}" } let(:expected_suffix) { '' } - let(:expected_repository) { personal_snippet.repository } + let(:expected_repository) { ::Repository.new(personal_snippet.full_path, personal_snippet, shard: personal_snippet.repository_storage, disk_path: personal_snippet.disk_path, repo_type: Gitlab::GlRepository::SNIPPET) } let(:expected_container) { personal_snippet } end @@ -104,7 +104,7 @@ RSpec.describe Gitlab::GlRepository::RepoType do let(:expected_id) { project_snippet.id } let(:expected_identifier) { "snippet-#{expected_id}" } let(:expected_suffix) { '' } - let(:expected_repository) { project_snippet.repository } + let(:expected_repository) { ::Repository.new(project_snippet.full_path, project_snippet, shard: project_snippet.repository_storage, disk_path: project_snippet.disk_path, repo_type: Gitlab::GlRepository::SNIPPET) } let(:expected_container) { project_snippet } end @@ -133,10 +133,14 @@ RSpec.describe Gitlab::GlRepository::RepoType do let(:expected_identifier) { "design-#{project.id}" } let(:expected_id) { project.id } let(:expected_suffix) { '.design' } - let(:expected_repository) { project.design_repository } + let(:expected_repository) { ::DesignManagement::Repository.new(project) } let(:expected_container) { project } end + it 'uses the design access checker' do + expect(described_class.access_checker_class).to eq(::Gitlab::GitAccessDesign) + end + it 'knows its type' do aggregate_failures do expect(described_class).to be_design diff --git a/spec/lib/gitlab/gl_repository_spec.rb b/spec/lib/gitlab/gl_repository_spec.rb index f90103ee6f7..3733d545155 100644 --- a/spec/lib/gitlab/gl_repository_spec.rb +++ b/spec/lib/gitlab/gl_repository_spec.rb @@ -31,15 +31,4 @@ RSpec.describe ::Gitlab::GlRepository do expect { described_class.parse("project-foo") }.to raise_error(ArgumentError) end end - - describe 'DESIGN' do - it 'uses the design access checker' do - expect(described_class::DESIGN.access_checker_class).to eq(::Gitlab::GitAccessDesign) - end - - it 'builds a design repository' do - expect(described_class::DESIGN.repository_resolver.call(create(:project))) - .to be_a(::DesignManagement::Repository) - end - end end diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index bb7288ed227..4a0a7c81781 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -1179,7 +1179,7 @@ RSpec.describe API::Internal::Base do let(:gl_repository) { "snippet-#{personal_snippet.id}" } it 'does not try to notify that project moved' do - allow(Gitlab::GlRepository).to receive(:parse).and_return([personal_snippet, nil, Gitlab::GlRepository::PROJECT]) + allow(Gitlab::GlRepository).to receive(:parse).and_return([personal_snippet, nil, Gitlab::GlRepository::SNIPPET]) expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message) diff --git a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb index 212d39caf74..1d50d8c675e 100644 --- a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb +++ b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require 'spec_helper' +require 'fast_spec_helper' require 'rubocop' require 'rubocop/rspec/support' require_relative '../../../../rubocop/cop/migration/update_column_in_batches' -RSpec.describe RuboCop::Cop::Migration::UpdateColumnInBatches do +RSpec.describe RuboCop::Cop::Migration::UpdateColumnInBatches, type: :rubocop do let(:cop) { described_class.new } - let(:tmp_rails_root) { Rails.root.join('tmp', 'rails_root') } + let(:tmp_rails_root) { rails_root_join('tmp', 'rails_root') } let(:migration_code) do <<-END def up @@ -27,7 +27,7 @@ RSpec.describe RuboCop::Cop::Migration::UpdateColumnInBatches do FileUtils.rm_rf(tmp_rails_root) end - let(:spec_filepath) { tmp_rails_root.join('spec', 'migrations', 'my_super_migration_spec.rb') } + let(:spec_filepath) { File.join(tmp_rails_root, 'spec', 'migrations', 'my_super_migration_spec.rb') } context 'outside of a migration' do it 'does not register any offenses' do @@ -83,31 +83,31 @@ RSpec.describe RuboCop::Cop::Migration::UpdateColumnInBatches do end context 'in a migration' do - let(:migration_filepath) { tmp_rails_root.join('db', 'migrate', '20121220064453_my_super_migration.rb') } + let(:migration_filepath) { File.join(tmp_rails_root, 'db', 'migrate', '20121220064453_my_super_migration.rb') } it_behaves_like 'a migration file with no spec file' it_behaves_like 'a migration file with a spec file' end context 'in a post migration' do - let(:migration_filepath) { tmp_rails_root.join('db', 'post_migrate', '20121220064453_my_super_migration.rb') } + let(:migration_filepath) { File.join(tmp_rails_root, 'db', 'post_migrate', '20121220064453_my_super_migration.rb') } it_behaves_like 'a migration file with no spec file' it_behaves_like 'a migration file with a spec file' end context 'EE migrations' do - let(:spec_filepath) { tmp_rails_root.join('ee', 'spec', 'migrations', 'my_super_migration_spec.rb') } + let(:spec_filepath) { File.join(tmp_rails_root, 'ee', 'spec', 'migrations', 'my_super_migration_spec.rb') } context 'in a migration' do - let(:migration_filepath) { tmp_rails_root.join('ee', 'db', 'migrate', '20121220064453_my_super_migration.rb') } + let(:migration_filepath) { File.join(tmp_rails_root, 'ee', 'db', 'migrate', '20121220064453_my_super_migration.rb') } it_behaves_like 'a migration file with no spec file' it_behaves_like 'a migration file with a spec file' end context 'in a post migration' do - let(:migration_filepath) { tmp_rails_root.join('ee', 'db', 'post_migrate', '20121220064453_my_super_migration.rb') } + let(:migration_filepath) { File.join(tmp_rails_root, 'ee', 'db', 'post_migrate', '20121220064453_my_super_migration.rb') } it_behaves_like 'a migration file with no spec file' it_behaves_like 'a migration file with a spec file' diff --git a/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb index 4aeae788114..025f0d5c7ea 100644 --- a/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb @@ -17,5 +17,9 @@ RSpec.shared_examples 'a repo type' do it 'finds the repository for the repo type' do expect(described_class.repository_for(expected_container)).to eq(expected_repository) end + + it 'returns nil when container is nil' do + expect(described_class.repository_for(nil)).to eq(nil) + end end end