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