Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-03-05 03:09:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-05 03:09:24 +0300
commit7ff2de7c12d71873a3c0e85cc6fbcd5d5f05b5c1 (patch)
treef6b2dc4c2c294d21c99aba8011566d856648d84d
parent6609e5ea75a9e119651e19574c30c11ce19c62d0 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--CHANGELOG.md35
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock1
-rw-r--r--app/assets/javascripts/alert_management/components/alert_management_table.vue2
-rw-r--r--app/assets/javascripts/diffs/components/settings_dropdown.vue4
-rw-r--r--app/assets/javascripts/feature_flags/components/feature_flags.vue8
-rw-r--r--app/assets/javascripts/incidents_settings/constants.js2
-rw-r--r--app/assets/javascripts/repository/index.js19
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue4
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss18
-rw-r--r--app/assets/stylesheets/pages/projects.scss58
-rw-r--r--app/controllers/groups/variables_controller.rb2
-rw-r--r--app/controllers/profiles/active_sessions_controller.rb5
-rw-r--r--app/helpers/active_sessions_helper.rb7
-rw-r--r--app/helpers/wiki_page_version_helper.rb20
-rw-r--r--app/models/active_session.rb50
-rw-r--r--app/models/ci/pipeline.rb10
-rw-r--r--app/models/environment.rb28
-rw-r--r--app/models/project.rb1
-rw-r--r--app/views/projects/_home_panel.html.haml12
-rw-r--r--app/views/projects/buttons/_clone.html.haml10
-rw-r--r--app/views/projects/buttons/_fork.html.haml13
-rw-r--r--app/views/projects/buttons/_star.html.haml18
-rw-r--r--app/views/projects/tree/_tree_header.html.haml2
-rw-r--r--app/views/shared/_mobile_clone_panel.html.haml4
-rw-r--r--app/views/shared/wikis/show.html.haml2
-rw-r--r--changelogs/unreleased/273286-update-project-view-buttons.yml5
-rw-r--r--changelogs/unreleased/322424-btn-confirm.yml5
-rw-r--r--changelogs/unreleased/323474-update-incident-setting-copy.yml5
-rw-r--r--changelogs/unreleased/alert-incident-url-follow.yml5
-rw-r--r--changelogs/unreleased/environment-tier.yml5
-rw-r--r--changelogs/unreleased/move_ci_config_path_project_variable.yml5
-rw-r--r--changelogs/unreleased/nfriend-fix-gl-dropdown-double-scrollbars.yml5
-rw-r--r--changelogs/unreleased/security-jv-workhorse-router.yml5
-rw-r--r--changelogs/unreleased/security-workhorse-validate-path-logging-removal.yml5
-rw-r--r--changelogs/unreleased/tor-defect-mr-cog-checkboxes.yml5
-rw-r--r--config/feature_flags/development/environment_tier.yml8
-rw-r--r--config/initializers/warden.rb3
-rw-r--r--db/migrate/20210301150451_add_tier_to_environments.rb19
-rw-r--r--db/migrate/20210303091651_add_index_to_environments_tier.rb19
-rw-r--r--db/schema_migrations/202103011504511
-rw-r--r--db/schema_migrations/202103030916511
-rw-r--r--db/structure.sql5
-rw-r--r--doc/administration/issue_closing_pattern.md4
-rw-r--r--doc/ci/variables/predefined_variables.md2
-rw-r--r--doc/user/application_security/dast/index.md69
-rw-r--r--lib/api/group_variables.rb3
-rw-r--r--lib/gitlab/diff/inline_diff.rb48
-rw-r--r--lib/gitlab/diff/pair_selector.rb58
-rw-r--r--lib/gitlab/git/wiki_page_version.rb9
-rw-r--r--locale/gitlab.pot2
-rw-r--r--package.json2
-rw-r--r--spec/controllers/groups/variables_controller_spec.rb39
-rw-r--r--spec/controllers/profiles/active_sessions_controller_spec.rb2
-rw-r--r--spec/factories/environments.rb16
-rw-r--r--spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb2
-rw-r--r--spec/features/projects/settings/operations_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/user_searches_in_settings_spec.rb2
-rw-r--r--spec/frontend/alert_management/components/alert_management_table_spec.js2
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap2
-rw-r--r--spec/frontend/vue_shared/alert_details/alert_details_spec.js16
-rw-r--r--spec/helpers/wiki_page_version_helper_spec.rb80
-rw-r--r--spec/lib/gitlab/diff/pair_selector_spec.rb84
-rw-r--r--spec/lib/gitlab/git/wiki_page_version_spec.rb14
-rw-r--r--spec/models/active_session_spec.rb115
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb1
-rw-r--r--spec/models/environment_spec.rb89
-rw-r--r--spec/models/project_spec.rb14
-rw-r--r--spec/requests/api/group_variables_spec.rb51
-rw-r--r--spec/rubocop/code_reuse_helpers_spec.rb1
-rw-r--r--spec/rubocop/cop/active_record_association_reload_spec.rb1
-rw-r--r--spec/rubocop/cop/api/base_spec.rb1
-rw-r--r--spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb1
-rw-r--r--spec/rubocop/cop/avoid_becomes_spec.rb1
-rw-r--r--spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb1
-rw-r--r--spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb1
-rw-r--r--spec/rubocop/cop/avoid_return_from_blocks_spec.rb1
-rw-r--r--spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb1
-rw-r--r--spec/rubocop/cop/ban_catch_throw_spec.rb1
-rw-r--r--spec/rubocop/cop/code_reuse/finder_spec.rb1
-rw-r--r--spec/rubocop/cop/code_reuse/presenter_spec.rb1
-rw-r--r--spec/rubocop/cop/code_reuse/serializer_spec.rb1
-rw-r--r--spec/rubocop/cop/code_reuse/service_class_spec.rb1
-rw-r--r--spec/rubocop/cop/code_reuse/worker_spec.rb1
-rw-r--r--spec/rubocop/cop/default_scope_spec.rb1
-rw-r--r--spec/rubocop/cop/destroy_all_spec.rb1
-rw-r--r--spec/rubocop/cop/filename_length_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/bulk_insert_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/change_timezone_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/except_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/httparty_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/intersect_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/json_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/namespaced_class_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/predicate_memoization_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/rails_logger_spec.rb1
-rw-r--r--spec/rubocop/cop/gitlab/union_spec.rb1
-rw-r--r--spec/rubocop/cop/graphql/authorize_types_spec.rb1
-rw-r--r--spec/rubocop/cop/graphql/descriptions_spec.rb1
-rw-r--r--spec/rubocop/cop/graphql/gid_expected_type_spec.rb1
-rw-r--r--spec/rubocop/cop/graphql/id_type_spec.rb1
-rw-r--r--spec/rubocop/cop/graphql/json_type_spec.rb1
-rw-r--r--spec/rubocop/cop/graphql/resolver_type_spec.rb1
-rw-r--r--spec/rubocop/cop/group_public_or_visible_to_user_spec.rb1
-rw-r--r--spec/rubocop/cop/ignored_columns_spec.rb1
-rw-r--r--spec/rubocop/cop/include_sidekiq_worker_spec.rb1
-rw-r--r--spec/rubocop/cop/inject_enterprise_edition_module_spec.rb1
-rw-r--r--spec/rubocop/cop/lint/last_keyword_argument_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/add_column_with_default_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/add_concurrent_index_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/add_index_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/add_reference_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/add_timestamps_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/datetime_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/drop_table_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/hash_index_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/prevent_strings_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/remove_column_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/remove_concurrent_index_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/remove_index_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/safer_boolean_column_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/schedule_async_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/timestamps_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/update_column_in_batches_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb1
-rw-r--r--spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb1
-rw-r--r--spec/rubocop/cop/performance/ar_count_each_spec.rb1
-rw-r--r--spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb1
-rw-r--r--spec/rubocop/cop/performance/readlines_each_spec.rb1
-rw-r--r--spec/rubocop/cop/prefer_class_methods_over_module_spec.rb1
-rw-r--r--spec/rubocop/cop/project_path_helper_spec.rb1
-rw-r--r--spec/rubocop/cop/put_group_routes_under_scope_spec.rb1
-rw-r--r--spec/rubocop/cop/put_project_routes_under_scope_spec.rb1
-rw-r--r--spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb1
-rw-r--r--spec/rubocop/cop/qa/element_with_pattern_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/be_success_matcher_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/env_assignment_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/timecop_freeze_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/timecop_travel_spec.rb1
-rw-r--r--spec/rubocop/cop/rspec/top_level_describe_path_spec.rb1
-rw-r--r--spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb1
-rw-r--r--spec/rubocop/cop/safe_params_spec.rb1
-rw-r--r--spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb1
-rw-r--r--spec/rubocop/cop/scalability/cron_worker_context_spec.rb1
-rw-r--r--spec/rubocop/cop/scalability/file_uploads_spec.rb1
-rw-r--r--spec/rubocop/cop/scalability/idempotent_worker_spec.rb1
-rw-r--r--spec/rubocop/cop/sidekiq_options_queue_spec.rb1
-rw-r--r--spec/rubocop/cop/static_translation_definition_spec.rb1
-rw-r--r--spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb1
-rw-r--r--spec/rubocop/cop/usage_data/large_table_spec.rb1
-rw-r--r--spec/rubocop/migration_helpers_spec.rb1
-rw-r--r--spec/rubocop/qa_helpers_spec.rb1
-rw-r--r--workhorse/CHANGELOG12
-rw-r--r--workhorse/VERSION2
-rw-r--r--workhorse/internal/staticpages/deploy_page_test.go4
-rw-r--r--workhorse/internal/staticpages/error_pages_test.go14
-rw-r--r--workhorse/internal/staticpages/servefile.go47
-rw-r--r--workhorse/internal/staticpages/servefile_test.go46
-rw-r--r--workhorse/internal/staticpages/static.go1
-rw-r--r--workhorse/internal/staticpages/testdata/file11
-rw-r--r--workhorse/internal/staticpages/testdata/uploads/file21
-rw-r--r--workhorse/internal/upstream/routes.go20
-rw-r--r--workhorse/internal/upstream/upstream.go8
-rw-r--r--workhorse/internal/upstream/upstream_test.go67
-rw-r--r--workhorse/main_test.go11
-rw-r--r--yarn.lock8
186 files changed, 965 insertions, 555 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1bd602f975d..fcea3575788 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 13.9.2 (2021-03-04)
+
+### Security (6 changes)
+
+- Bump thrift gem to 0.14.0.
+- Allow only owners to manage group variables.
+- Do not store marshalled sessions ids in Redis.
+- Fix XSS in wiki author email and name.
+- Workhorse: prevent escaped router path traversal.
+- Fix XSS vulnerability for swagger file viewer.
+
+
## 13.9.1 (2021-02-23)
### Fixed (6 changes, 1 of them is from the community)
@@ -588,6 +600,18 @@ entry.
- Apply new GitLab UI for buttons in pipeline schedules.
+## 13.8.5 (2021-03-04)
+
+### Security (6 changes)
+
+- Fix XSS in wiki author email and name.
+- Bump thrift gem to 0.14.0.
+- Allow only owners to manage group variables.
+- Do not store marshalled sessions ids in Redis.
+- Workhorse: prevent escaped router path traversal.
+- Fix XSS vulnerability for swagger file viewer.
+
+
## 13.8.4 (2021-02-11)
### Security (9 changes)
@@ -988,6 +1012,17 @@ entry.
- Add verbiage + link sast to show it's in core. !51935
+## 13.7.8 (2021-03-04)
+
+### Security (5 changes)
+
+- Bump thrift gem to 0.14.0.
+- Allow only owners to manage group variables.
+- Do not store marshalled sessions ids in Redis.
+- Workhorse: prevent escaped router path traversal.
+- Fix XSS vulnerability for swagger file viewer.
+
+
## 13.7.7 (2021-02-11)
### Security (9 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index d282ad48073..25a1ad60801 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-cdb02af5b322de1f4091b39c349579b2e335b914
+14b985f2d0add8bd3b6fbf01d06e3bbafcb12f6c
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index ca1596d1a61..e0ef5b73743 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-8.64.0
+8.64.2
diff --git a/Gemfile b/Gemfile
index 1a46f0399c7..67cf0f1243d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -314,6 +314,9 @@ gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
gem 'gitlab-labkit', '~> 0.16.0'
+# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
+# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
+gem 'thrift', '>= 0.14.0'
# I18n
gem 'ruby_parser', '~> 3.15', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index d5d700e4721..cf9bf93cb06 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1578,6 +1578,7 @@ DEPENDENCIES
terser (= 1.0.2)
test-prof (~> 0.12.0)
thin (~> 1.8.0)
+ thrift (>= 0.14.0)
timecop (~> 0.9.1)
toml-rb (~> 1.0.0)
truncato (~> 0.7.11)
diff --git a/app/assets/javascripts/alert_management/components/alert_management_table.vue b/app/assets/javascripts/alert_management/components/alert_management_table.vue
index 96279b65452..79a6bac3ba7 100644
--- a/app/assets/javascripts/alert_management/components/alert_management_table.vue
+++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue
@@ -225,7 +225,7 @@ export default {
getIssueMeta({ issue: { iid, state } }) {
return {
state: state === 'closed' ? `(${this.$options.i18n.closed})` : '',
- link: joinPaths('/', this.projectPath, '-', 'issues', iid),
+ link: joinPaths('/', this.projectPath, '-', 'issues/incident', iid),
};
},
tbodyTrClass(item) {
diff --git a/app/assets/javascripts/diffs/components/settings_dropdown.vue b/app/assets/javascripts/diffs/components/settings_dropdown.vue
index b1f8e583c1f..879922f86a2 100644
--- a/app/assets/javascripts/diffs/components/settings_dropdown.vue
+++ b/app/assets/javascripts/diffs/components/settings_dropdown.vue
@@ -85,7 +85,7 @@ export default {
</div>
<gl-form-checkbox
data-testid="show-whitespace"
- class="gl-mt-3 gl-pl-3"
+ class="gl-mt-3 gl-ml-3"
:checked="showWhitespace"
@input="toggleWhitespace"
>
@@ -93,7 +93,7 @@ export default {
</gl-form-checkbox>
<gl-form-checkbox
data-testid="file-by-file"
- class="gl-pl-3 gl-mb-0"
+ class="gl-ml-3 gl-mb-0"
:checked="viewDiffsFileByFile"
@input="toggleFileByFile"
>
diff --git a/app/assets/javascripts/feature_flags/components/feature_flags.vue b/app/assets/javascripts/feature_flags/components/feature_flags.vue
index 348b71dc1c6..115d68de5c9 100644
--- a/app/assets/javascripts/feature_flags/components/feature_flags.vue
+++ b/app/assets/javascripts/feature_flags/components/feature_flags.vue
@@ -213,7 +213,7 @@ export default {
<gl-button
v-if="newUserListPath"
:href="newUserListPath"
- variant="success"
+ variant="confirm"
category="secondary"
class="gl-mb-3"
data-testid="ff-new-list-button"
@@ -224,7 +224,7 @@ export default {
<gl-button
v-if="hasNewPath"
:href="featureFlagsLimitExceeded ? '' : newFeatureFlagPath"
- variant="success"
+ variant="confirm"
data-testid="ff-new-button"
@click="onNewFeatureFlagCLick"
>
@@ -301,7 +301,7 @@ export default {
<gl-button
v-if="newUserListPath"
:href="newUserListPath"
- variant="success"
+ variant="confirm"
category="secondary"
class="gl-mb-0 gl-mr-4"
data-testid="ff-new-list-button"
@@ -312,7 +312,7 @@ export default {
<gl-button
v-if="hasNewPath"
:href="featureFlagsLimitExceeded ? '' : newFeatureFlagPath"
- variant="success"
+ variant="confirm"
data-testid="ff-new-button"
@click="onNewFeatureFlagCLick"
>
diff --git a/app/assets/javascripts/incidents_settings/constants.js b/app/assets/javascripts/incidents_settings/constants.js
index 818af4ecb90..577d8ecb777 100644
--- a/app/assets/javascripts/incidents_settings/constants.js
+++ b/app/assets/javascripts/incidents_settings/constants.js
@@ -40,7 +40,7 @@ export const I18N_ALERT_SETTINGS_FORM = {
label: __('Incident template (optional)'),
},
sendEmail: {
- label: __('Send a separate email notification to Developers.'),
+ label: __('Send a single email notification to Owners and Maintainers for new alerts.'),
},
autoCloseIncidents: {
label: __('Automatically close incidents when the associated Prometheus alert resolves.'),
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index 747b85f5c1c..e6969b7c8b2 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -1,3 +1,4 @@
+import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
import { parseBoolean } from '../lib/utils/common_utils';
@@ -7,7 +8,6 @@ import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue';
import DirectoryDownloadLinks from './components/directory_download_links.vue';
import LastCommit from './components/last_commit.vue';
-import TreeActionLink from './components/tree_action_link.vue';
import apolloProvider from './graphql';
import createRouter from './router';
import { updateFormAction } from './utils/dom';
@@ -101,14 +101,17 @@ export default function setupVueRepositoryList() {
el: treeHistoryLinkEl,
router,
render(h) {
- return h(TreeActionLink, {
- props: {
- path: `${historyLink}/${
- this.$route.params.path ? escapeFileUrl(this.$route.params.path) : ''
- }`,
- text: __('History'),
+ return h(
+ GlButton,
+ {
+ attrs: {
+ href: `${historyLink}/${
+ this.$route.params.path ? escapeFileUrl(this.$route.params.path) : ''
+ }`,
+ },
},
- });
+ [__('History')],
+ );
},
});
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
index 920ed5c6b81..f7b49a85b83 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
@@ -222,7 +222,9 @@ export default {
});
},
incidentPath(issueId) {
- return joinPaths(this.projectIssuesPath, issueId);
+ return this.isThreatMonitoringPage
+ ? joinPaths(this.projectIssuesPath, issueId)
+ : joinPaths(this.projectIssuesPath, 'incident', issueId);
},
trackPageViews() {
const { category, action } = this.trackAlertsDetailsViewsOptions;
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index f56d8f2c2a9..82ef74b475f 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -34,7 +34,14 @@
overflow-y: auto;
&.dropdown-extended-height {
- max-height: 400px;
+ $extended-max-height: 400px;
+
+ max-height: $extended-max-height;
+
+ // See comment below for explanation
+ .gl-new-dropdown-inner {
+ max-height: $extended-max-height - 2px;
+ }
}
@include media-breakpoint-down(xs) {
@@ -46,6 +53,15 @@
overflow-y: initial;
max-height: initial;
}
+
+ // `GlDropdown` specifies the `max-height` of `.gl-new-dropdown-inner`
+ // as `$dropdown-max-height`, but the `max-height` rule above forces
+ // the parent `.dropdown-menu` to be _slightly_ too small because of
+ // the 1px borders. The workaround below overrides the @gitlab/ui style
+ // to avoid a double scroll bar.
+ .gl-new-dropdown-inner {
+ max-height: $dropdown-max-height - 2px;
+ }
}
.dropdown-toggle,
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index be857727cf7..7344f930f2f 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -136,35 +136,11 @@
.project-repo-buttons {
.btn {
- &:last-child {
- margin-left: 0;
- }
-
svg {
- fill: $layout-link-gray;
- }
-
- .notifications-icon {
- top: 1px;
- margin-right: 0;
+ fill: $gray-500;
}
}
- .icon {
- top: 0;
- }
-
- .count-badge,
- .btn-xs {
- height: 24px;
- }
-
- .home-panel-action-button,
- .project-action-button {
- margin: $gl-padding $gl-padding-8 0 0;
- vertical-align: top;
- }
-
.notification-dropdown .dropdown-menu {
@extend .dropdown-menu-right;
}
@@ -175,34 +151,6 @@
}
}
- .count-buttons {
- display: inline-block;
- vertical-align: top;
- margin-top: $gl-padding;
-
- .count-badge-count,
- .count-badge-button {
- border: 1px solid $border-color;
- line-height: 1;
- }
-
- .count,
- .count-badge-button {
- color: $gl-text-color;
- }
-
- .count-badge-count {
- padding: 0 12px;
- background: $gray-light;
- border-radius: 0 $border-radius-base $border-radius-base 0;
- }
-
- .count-badge-button {
- border-right: 0;
- border-radius: $border-radius-base 0 0 $border-radius-base;
- }
- }
-
.project-clone-holder {
display: inline-block;
margin: $gl-padding 0 0;
@@ -887,10 +835,6 @@ pre.light-well {
}
.git-clone-holder {
- .btn-clipboard {
- border: 1px solid $border-color;
- }
-
.clone-options {
display: table-cell;
diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb
index 51670325ce3..a2289b540ec 100644
--- a/app/controllers/groups/variables_controller.rb
+++ b/app/controllers/groups/variables_controller.rb
@@ -2,7 +2,7 @@
module Groups
class VariablesController < Groups::ApplicationController
- before_action :authorize_admin_build!
+ before_action :authorize_admin_group!
skip_cross_project_access_check :show, :update
diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb
index 1233c906406..aafd7c2b65b 100644
--- a/app/controllers/profiles/active_sessions_controller.rb
+++ b/app/controllers/profiles/active_sessions_controller.rb
@@ -8,9 +8,8 @@ class Profiles::ActiveSessionsController < Profiles::ApplicationController
end
def destroy
- # params[:id] can be either an Rack::Session::SessionId#private_id
- # or an encrypted Rack::Session::SessionId#public_id
- ActiveSession.destroy_with_deprecated_encryption(current_user, params[:id])
+ # params[:id] can be an Rack::Session::SessionId#private_id
+ ActiveSession.destroy_session(current_user, params[:id])
current_user.forget_me!
respond_to do |format|
diff --git a/app/helpers/active_sessions_helper.rb b/app/helpers/active_sessions_helper.rb
index 322c5b3b16d..cfe0b747e78 100644
--- a/app/helpers/active_sessions_helper.rb
+++ b/app/helpers/active_sessions_helper.rb
@@ -24,11 +24,6 @@ module ActiveSessionsHelper
end
def revoke_session_path(active_session)
- if active_session.session_private_id
- profile_active_session_path(active_session.session_private_id)
- else
- # TODO: remove in 13.7
- profile_active_session_path(active_session.public_id)
- end
+ profile_active_session_path(active_session.session_private_id)
end
end
diff --git a/app/helpers/wiki_page_version_helper.rb b/app/helpers/wiki_page_version_helper.rb
new file mode 100644
index 00000000000..ae20717ad99
--- /dev/null
+++ b/app/helpers/wiki_page_version_helper.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module WikiPageVersionHelper
+ def wiki_page_version_author_url(wiki_page_version)
+ user = wiki_page_version.author
+ user.nil? ? "mailto:#{wiki_page_version.author_email}" : Gitlab::UrlBuilder.build(user)
+ end
+
+ def wiki_page_version_author_avatar(wiki_page_version)
+ image_tag(avatar_icon_for_email(wiki_page_version.author_email, 24), class: "avatar s24 float-none gl-mr-0!")
+ end
+
+ def wiki_page_version_author_header(wiki_page_version)
+ avatar = wiki_page_version_author_avatar(wiki_page_version)
+ name = "<strong>".html_safe + wiki_page_version.author_name + "</strong>".html_safe
+ link_start = "<a href='".html_safe + wiki_page_version_author_url(wiki_page_version) + "'>".html_safe
+
+ html_escape(_("Last edited by %{link_start}%{avatar} %{name}%{link_end}")) % { avatar: avatar, name: name, link_start: link_start, link_end: '</a>'.html_safe }
+ end
+end
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index 823685f78f4..a0e74c7f48e 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -42,13 +42,6 @@ class ActiveSession
device_type&.titleize
end
- # This is not the same as Rack::Session::SessionId#public_id, but we
- # need to preserve this for backwards compatibility.
- # TODO: remove in 13.7
- def public_id
- Gitlab::CryptoHelper.aes256_gcm_encrypt(session_id)
- end
-
def self.set(user, request)
Gitlab::Redis::SharedState.with do |redis|
session_private_id = request.session.id.private_id
@@ -63,8 +56,6 @@ class ActiveSession
device_type: client.device_type,
created_at: user.current_sign_in_at || timestamp,
updated_at: timestamp,
- # TODO: remove in 13.7
- session_id: request.session.id.public_id,
session_private_id: session_private_id,
is_impersonated: request.session[:impersonator_id].present?
)
@@ -80,20 +71,10 @@ class ActiveSession
lookup_key_name(user.id),
session_private_id
)
-
- # We remove the ActiveSession stored by using public_id to avoid
- # duplicate entries
- remove_deprecated_active_sessions_with_public_id(redis, user.id, request.session.id.public_id)
end
end
end
- # TODO: remove in 13.7
- private_class_method def self.remove_deprecated_active_sessions_with_public_id(redis, user_id, rack_session_public_id)
- redis.srem(lookup_key_name(user_id), rack_session_public_id)
- redis.del(key_name(user_id, rack_session_public_id))
- end
-
def self.list(user)
Gitlab::Redis::SharedState.with do |redis|
cleaned_up_lookup_entries(redis, user).map do |raw_session|
@@ -109,18 +90,6 @@ class ActiveSession
end
end
- # TODO: remove in 13.7
- # After upgrade there might be a duplicate ActiveSessions:
- # - one with the public_id stored in #session_id
- # - another with private_id stored in #session_private_id
- def self.destroy_with_rack_session_id(user, rack_session_id)
- return unless rack_session_id
-
- Gitlab::Redis::SharedState.with do |redis|
- destroy_sessions(redis, user, [rack_session_id.public_id, rack_session_id.private_id])
- end
- end
-
def self.destroy_sessions(redis, user, session_ids)
key_names = session_ids.map { |session_id| key_name(user.id, session_id) }
@@ -132,19 +101,11 @@ class ActiveSession
end
end
- # TODO: remove in 13.7
- # After upgrade, .destroy might be called with the session id encrypted
- # by .public_id.
- def self.destroy_with_deprecated_encryption(user, session_id)
+ def self.destroy_session(user, session_id)
return unless session_id
- decrypted_session_id = decrypt_public_id(session_id)
- rack_session_private_id = if decrypted_session_id
- Rack::Session::SessionId.new(decrypted_session_id).private_id
- end
-
Gitlab::Redis::SharedState.with do |redis|
- destroy_sessions(redis, user, [session_id, decrypted_session_id, rack_session_private_id].compact)
+ destroy_sessions(redis, user, [session_id].compact)
end
end
@@ -275,11 +236,4 @@ class ActiveSession
entries.compact
end
-
- # TODO: remove in 13.7
- private_class_method def self.decrypt_public_id(public_id)
- Gitlab::CryptoHelper.aes256_gcm_decrypt(public_id)
- rescue
- nil
- end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 2a584200baa..7418a091407 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -693,14 +693,6 @@ module Ci
.exists?
end
- # TODO: this logic is duplicate with Pipeline::Chain::Config::Content
- # we should persist this is `ci_pipelines.config_path`
- def config_path
- return unless repository_source? || unknown_source?
-
- project.ci_config_path_or_default
- end
-
def has_yaml_errors?
yaml_errors.present?
end
@@ -786,8 +778,6 @@ module Ci
variables.append(key: 'CI_PIPELINE_IID', value: iid.to_s)
variables.append(key: 'CI_PIPELINE_SOURCE', value: source.to_s)
- variables.append(key: 'CI_CONFIG_PATH', value: config_path)
-
variables.concat(predefined_commit_variables)
if merge_request?
diff --git a/app/models/environment.rb b/app/models/environment.rb
index ea1073c810a..3ac7e63bae3 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -39,6 +39,7 @@ class Environment < ApplicationRecord
before_validation :generate_slug, if: ->(env) { env.slug.blank? }
before_save :set_environment_type
+ before_save :ensure_environment_tier
after_save :clear_reactive_cache!
validates :name,
@@ -87,6 +88,7 @@ class Environment < ApplicationRecord
end
scope :for_project, -> (project) { where(project_id: project) }
+ scope :for_tier, -> (tier) { where(tier: tier).where('tier IS NOT NULL') }
scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
scope :unfoldered, -> { where(environment_type: nil) }
scope :with_rank, -> do
@@ -94,6 +96,14 @@ class Environment < ApplicationRecord
end
scope :for_id, -> (id) { where(id: id) }
+ enum tier: {
+ production: 0,
+ staging: 1,
+ testing: 2,
+ development: 3,
+ other: 4
+ }
+
state_machine :state, initial: :available do
event :start do
transition stopped: :available
@@ -429,6 +439,24 @@ class Environment < ApplicationRecord
def generate_slug
self.slug = Gitlab::Slug::Environment.new(name).generate
end
+
+ def ensure_environment_tier
+ return unless ::Feature.enabled?(:environment_tier, project, default_enabled: :yaml)
+
+ self.tier ||= guess_tier
+ end
+
+ # Guessing the tier of the environment if it's not explicitly specified by users.
+ # See https://en.wikipedia.org/wiki/Deployment_environment for industry standard deployment environments
+ def guess_tier
+ case name
+ when %r{dev|review|trunk}i then self.class.tiers[:development]
+ when %r{test|qc}i then self.class.tiers[:testing]
+ when %r{st(a|)g|mod(e|)l|pre|demo}i then self.class.tiers[:staging]
+ when %r{pr(o|)d|live}i then self.class.tiers[:production]
+ else self.class.tiers[:other]
+ end
+ end
end
Environment.prepend_if_ee('EE::Environment')
diff --git a/app/models/project.rb b/app/models/project.rb
index 7e0b43e69ac..18f22ce7942 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1987,6 +1987,7 @@ class Project < ApplicationRecord
.append(key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: repository_languages.map(&:name).join(',').downcase)
.append(key: 'CI_DEFAULT_BRANCH', value: default_branch)
.append(key: 'CI_PROJECT_CONFIG_PATH', value: ci_config_path_or_default)
+ .append(key: 'CI_CONFIG_PATH', value: ci_config_path_or_default)
end
def predefined_ci_server_variables
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 5085798238e..f5eea9aa9c6 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -4,8 +4,8 @@
- emails_disabled = @project.emails_disabled?
.project-home-panel.js-show-on-project-root.gl-my-5{ class: [("empty-project" if empty_repo)] }
- .row.gl-mb-3
- .home-panel-title-row.col-md-12.col-lg-6.d-flex
+ .gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-sm-flex-direction-column.gl-mb-3
+ .home-panel-title-row.gl-display-flex
.avatar-container.rect-avatar.s64.home-panel-avatar.gl-flex-shrink-0.gl-w-11.gl-h-11.gl-mr-3.float-none
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64, itemprop: 'image')
.d-flex.flex-column.flex-wrap.align-items-baseline
@@ -41,13 +41,13 @@
= _("+ %{count} more") % { count: @project.count_of_extra_topics_not_shown }
- .project-repo-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end
+ .project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5
- if current_user
- .d-inline-flex
+ .gl-display-flex.gl-align-items-start.gl-mr-3
- if @notification_setting
- .js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, container_class: 'gl-mr-3 gl-mt-5 gl-vertical-align-top' } }
+ .js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id } }
- .count-buttons.d-inline-flex
+ .count-buttons.gl-display-flex.gl-align-items-flex-start
= render 'projects/buttons/star'
= render 'projects/buttons/fork'
diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml
index 0ec47744fc9..ee195e69a98 100644
--- a/app/views/projects/buttons/_clone.html.haml
+++ b/app/views/projects/buttons/_clone.html.haml
@@ -2,7 +2,7 @@
- dropdown_class = local_assigns.fetch(:dropdown_class, '')
.git-clone-holder.js-git-clone-holder
- %a#clone-dropdown.gl-button.btn.btn-info.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
+ %a#clone-dropdown.gl-button.btn.btn-confirm.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span.gl-mr-2.js-clone-dropdown-label
= _('Clone')
= sprite_icon("chevron-down", css_class: "icon")
@@ -11,19 +11,19 @@
%li{ class: 'gl-px-4!' }
%label.label-bold
= _('Clone with SSH')
- .input-group
+ .input-group.btn-group
= text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control qa-ssh-clone-url", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
- = clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
+ = clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), class: "input-group-text gl-button btn btn-icon btn-default")
= render_if_exists 'projects/buttons/geo'
- if http_enabled?
%li.pt-2{ class: 'gl-px-4!' }
%label.label-bold
= _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase }
- .input-group
+ .input-group.btn-group
= text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control qa-http-clone-url", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
- = clipboard_button(target: '#http_project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
+ = clipboard_button(target: '#http_project_clone', title: _("Copy URL"), class: "input-group-text gl-button btn btn-icon btn-default")
= render_if_exists 'projects/buttons/geo'
%li.divider.mt-2
%li.pt-2.gl-new-dropdown-item
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index fad168da71e..e4ec2e44298 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -1,17 +1,16 @@
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project)
- .count-badge.d-inline-flex.align-item-stretch.gl-mr-3
+ .count-badge.btn-group
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn' do
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: s_('ProjectOverview|Go to your fork'), class: 'gl-button btn btn-default btn-sm has-tooltip fork-btn' do
= sprite_icon('fork', css_class: 'icon')
%span= s_('ProjectOverview|Fork')
- else
- can_create_fork = current_user.can?(:create_fork)
- disabled_fork_tooltip = s_('ProjectOverview|You have reached your project limit')
- %span.has-tooltip{ title: (disabled_fork_tooltip unless can_create_fork) }
- = link_to new_project_fork_path(@project), class: "btn btn-default btn-xs count-badge-button d-flex align-items-center fork-btn #{' disabled' unless can_create_fork }", 'aria-label' => (disabled_fork_tooltip unless can_create_fork) do
+ %span.btn-group.has-tooltip{ title: (disabled_fork_tooltip unless can_create_fork) }
+ = link_to new_project_fork_path(@project), class: "gl-button btn btn-default btn-sm fork-btn #{' disabled' unless can_create_fork }", 'aria-label' => (disabled_fork_tooltip unless can_create_fork) do
= sprite_icon('fork', css_class: 'icon')
%span= s_('ProjectOverview|Fork')
- %span.fork-count.count-badge-count.d-flex.align-items-center
- = link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do
- = @project.forks_count
+ = link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Forks'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'gl-button btn btn-default btn-sm count has-tooltip' do
+ = @project.forks_count
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 690f0fe10f7..00d518450e9 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,21 +1,19 @@
- if current_user
- .count-badge.d-inline-flex.align-item-stretch.gl-mr-3
- %button.count-badge-button.btn.btn-default.btn-xs.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
+ .count-badge.d-inline-flex.align-item-stretch.gl-mr-3.btn-group
+ %button.gl-button.btn.btn-default.btn-sm.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
- if current_user.starred?(@project)
= sprite_icon('star', css_class: 'icon')
%span.starred= s_('ProjectOverview|Unstar')
- else
= sprite_icon('star-o', css_class: 'icon')
%span= s_('ProjectOverview|Star')
- %span.star-count.count-badge-count.d-flex.align-items-center
- = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'count' do
- = @project.star_count
+ = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'gl-button btn btn-default btn-sm star-count count' do
+ = @project.star_count
- else
- .count-badge.d-inline-flex.align-item-stretch.gl-mr-3
- = link_to new_user_session_path, class: 'btn btn-default btn-xs has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
+ .count-badge.d-inline-flex.align-item-stretch.gl-mr-3.btn-group
+ = link_to new_user_session_path, class: 'gl-button btn btn-default btn-sm has-tooltip star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= sprite_icon('star-o', css_class: 'icon')
%span= s_('ProjectOverview|Star')
- %span.star-count.count-badge-count.d-flex.align-items-center
- = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'count' do
- = @project.star_count
+ = link_to project_starrers_path(@project), title: n_(s_('ProjectOverview|Starrer'), s_('ProjectOverview|Starrers'), @project.star_count), class: 'gl-button btn btn-default btn-sm star-count count' do
+ = @project.star_count
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 6d33fbb535e..340f9811f9a 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -7,7 +7,7 @@
.tree-controls
.d-block.d-sm-flex.flex-wrap.align-items-start.gl-children-ml-sm-3<
= render_if_exists 'projects/tree/lock_link'
- #js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } }
+ #js-tree-history-link{ data: { history_link: project_commits_path(@project, @ref) } }
= render 'projects/find_file_link'
= render 'shared/web_ide_button', blob: nil
diff --git a/app/views/shared/_mobile_clone_panel.html.haml b/app/views/shared/_mobile_clone_panel.html.haml
index 2854b115506..3edfd502f13 100644
--- a/app/views/shared/_mobile_clone_panel.html.haml
+++ b/app/views/shared/_mobile_clone_panel.html.haml
@@ -3,8 +3,8 @@
- http_copy_label = _('Copy %{http_label} clone URL') % { http_label: gitlab_config.protocol.upcase }
.btn-group.mobile-git-clone.js-mobile-git-clone.btn-block
- = clipboard_button(button_text: default_clone_label, text: default_url_to_repo(project), hide_button_icon: true, class: "btn-primary flex-fill bold justify-content-center input-group-text clone-dropdown-btn js-clone-dropdown-label")
- %button.btn.btn-primary.dropdown-toggle.js-dropdown-toggle.flex-grow-0.d-flex-center.w-auto.ml-0{ type: "button", data: { toggle: "dropdown" } }
+ = clipboard_button(button_text: default_clone_label, text: default_url_to_repo(project), hide_button_icon: true, class: "gl-button btn btn-confirm flex-fill input-group-text clone-dropdown-btn js-clone-dropdown-label")
+ %button.gl-button.btn.btn-confirm.btn-icon.dropdown-toggle.js-dropdown-toggle.flex-grow-0.d-flex-center.w-auto.ml-0{ type: "button", data: { toggle: "dropdown" } }
= sprite_icon("chevron-down", css_class: "dropdown-btn-icon icon")
%ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } }
- if ssh_enabled?
diff --git a/app/views/shared/wikis/show.html.haml b/app/views/shared/wikis/show.html.haml
index 6d14ba8fe7b..8a5cd94bde9 100644
--- a/app/views/shared/wikis/show.html.haml
+++ b/app/views/shared/wikis/show.html.haml
@@ -7,7 +7,7 @@
.nav-text.flex-fill
%span.wiki-last-edit-by
- if @page.last_version
- = html_escape(_("Last edited by %{link_start}%{avatar} %{name}%{link_end}")) % { avatar: image_tag(avatar_icon_for_email(@page.last_version.author_email, 24), class: "avatar s24 float-none gl-mr-0!"), name: "<strong>#{@page.last_version.author_name}</strong>".html_safe, link_start: "<a href='#{@page.last_version.author_url}'>".html_safe, link_end: '</a>'.html_safe }
+ = wiki_page_version_author_header(@page.last_version)
= time_ago_with_tooltip(@page.last_version.authored_date)
.nav-controls.pb-md-3.pb-lg-0
diff --git a/changelogs/unreleased/273286-update-project-view-buttons.yml b/changelogs/unreleased/273286-update-project-view-buttons.yml
new file mode 100644
index 00000000000..9717c98bb03
--- /dev/null
+++ b/changelogs/unreleased/273286-update-project-view-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Update project page buttons to conform to design system
+merge_request: 53260
+author:
+type: changed
diff --git a/changelogs/unreleased/322424-btn-confirm.yml b/changelogs/unreleased/322424-btn-confirm.yml
new file mode 100644
index 00000000000..d5d854b9803
--- /dev/null
+++ b/changelogs/unreleased/322424-btn-confirm.yml
@@ -0,0 +1,5 @@
+---
+title: Change the button to Primary Blue
+merge_request: 55204
+author: Yogi (@yo)
+type: changed
diff --git a/changelogs/unreleased/323474-update-incident-setting-copy.yml b/changelogs/unreleased/323474-update-incident-setting-copy.yml
new file mode 100644
index 00000000000..2d3f090c804
--- /dev/null
+++ b/changelogs/unreleased/323474-update-incident-setting-copy.yml
@@ -0,0 +1,5 @@
+---
+title: Update text for email config under Incidents in Settings > Operations
+merge_request: 55753
+author:
+type: changed
diff --git a/changelogs/unreleased/alert-incident-url-follow.yml b/changelogs/unreleased/alert-incident-url-follow.yml
new file mode 100644
index 00000000000..9320520f8ab
--- /dev/null
+++ b/changelogs/unreleased/alert-incident-url-follow.yml
@@ -0,0 +1,5 @@
+---
+title: Allow alert to link to incidents
+merge_request: 55426
+author:
+type: changed
diff --git a/changelogs/unreleased/environment-tier.yml b/changelogs/unreleased/environment-tier.yml
new file mode 100644
index 00000000000..0f3f7e14133
--- /dev/null
+++ b/changelogs/unreleased/environment-tier.yml
@@ -0,0 +1,5 @@
+---
+title: Add tier column to the environments table
+merge_request: 55471
+author:
+type: added
diff --git a/changelogs/unreleased/move_ci_config_path_project_variable.yml b/changelogs/unreleased/move_ci_config_path_project_variable.yml
new file mode 100644
index 00000000000..f24a58a92d9
--- /dev/null
+++ b/changelogs/unreleased/move_ci_config_path_project_variable.yml
@@ -0,0 +1,5 @@
+---
+title: Move CI_CONFIG_PATH as project variable and deprecate CI_PROJECT_CONFIG_PATH
+merge_request: 54498
+author:
+type: changed
diff --git a/changelogs/unreleased/nfriend-fix-gl-dropdown-double-scrollbars.yml b/changelogs/unreleased/nfriend-fix-gl-dropdown-double-scrollbars.yml
new file mode 100644
index 00000000000..7898da24731
--- /dev/null
+++ b/changelogs/unreleased/nfriend-fix-gl-dropdown-double-scrollbars.yml
@@ -0,0 +1,5 @@
+---
+title: Fix double scrollbars in some dropdowns
+merge_request: 54837
+author:
+type: fixed
diff --git a/changelogs/unreleased/security-jv-workhorse-router.yml b/changelogs/unreleased/security-jv-workhorse-router.yml
new file mode 100644
index 00000000000..af17a71086d
--- /dev/null
+++ b/changelogs/unreleased/security-jv-workhorse-router.yml
@@ -0,0 +1,5 @@
+---
+title: 'Workhorse: prevent escaped router path traversal'
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-workhorse-validate-path-logging-removal.yml b/changelogs/unreleased/security-workhorse-validate-path-logging-removal.yml
new file mode 100644
index 00000000000..c8b68da684b
--- /dev/null
+++ b/changelogs/unreleased/security-workhorse-validate-path-logging-removal.yml
@@ -0,0 +1,5 @@
+---
+title: 'Workhorse: Stop logging when path is excluded'
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/tor-defect-mr-cog-checkboxes.yml b/changelogs/unreleased/tor-defect-mr-cog-checkboxes.yml
new file mode 100644
index 00000000000..adb564bfd15
--- /dev/null
+++ b/changelogs/unreleased/tor-defect-mr-cog-checkboxes.yml
@@ -0,0 +1,5 @@
+---
+title: Fix horizontal alignment of MR Changes cog menu dropdown checkboxes
+merge_request: 55591
+author:
+type: fixed
diff --git a/config/feature_flags/development/environment_tier.yml b/config/feature_flags/development/environment_tier.yml
new file mode 100644
index 00000000000..0459d1f212d
--- /dev/null
+++ b/config/feature_flags/development/environment_tier.yml
@@ -0,0 +1,8 @@
+---
+name: environment_tier
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55471
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323166
+milestone: '13.10'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb
index 2517c0cf5c2..88f2a13df60 100644
--- a/config/initializers/warden.rb
+++ b/config/initializers/warden.rb
@@ -42,8 +42,7 @@ Rails.application.configure do |config|
activity = Gitlab::Auth::Activity.new(opts)
tracker = Gitlab::Auth::BlockedUserTracker.new(user, auth)
- # TODO: switch to `auth.request.session.id.private_id` in 13.7
- ActiveSession.destroy_with_rack_session_id(user, auth.request.session.id)
+ ActiveSession.destroy_session(user, auth.request.session.id.private_id) if auth.request.session.id
activity.user_session_destroyed!
##
diff --git a/db/migrate/20210301150451_add_tier_to_environments.rb b/db/migrate/20210301150451_add_tier_to_environments.rb
new file mode 100644
index 00000000000..28592dd2bf6
--- /dev/null
+++ b/db/migrate/20210301150451_add_tier_to_environments.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddTierToEnvironments < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :environments, :tier, :smallint
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :environments, :tier
+ end
+ end
+end
diff --git a/db/migrate/20210303091651_add_index_to_environments_tier.rb b/db/migrate/20210303091651_add_index_to_environments_tier.rb
new file mode 100644
index 00000000000..0ec876945d9
--- /dev/null
+++ b/db/migrate/20210303091651_add_index_to_environments_tier.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddIndexToEnvironmentsTier < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_environments_on_project_id_and_tier'
+
+ DOWNTIME = false
+
+ def up
+ add_concurrent_index :environments, [:project_id, :tier], where: 'tier IS NOT NULL', name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index :environments, :state, name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20210301150451 b/db/schema_migrations/20210301150451
new file mode 100644
index 00000000000..1d644ccb62e
--- /dev/null
+++ b/db/schema_migrations/20210301150451
@@ -0,0 +1 @@
+6c52ab55814241b37014949976c4f3a0c63bea0a4f9a301735cc9f4c509f433d \ No newline at end of file
diff --git a/db/schema_migrations/20210303091651 b/db/schema_migrations/20210303091651
new file mode 100644
index 00000000000..35d2ca084b8
--- /dev/null
+++ b/db/schema_migrations/20210303091651
@@ -0,0 +1 @@
+e1641d84828e3d87aea626dbce6b3b2d231d08fcf1475991fe8d11714cdb0af0 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 2d9a97c9817..65762117a62 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -12163,7 +12163,8 @@ CREATE TABLE environments (
state character varying DEFAULT 'available'::character varying NOT NULL,
slug character varying NOT NULL,
auto_stop_at timestamp with time zone,
- auto_delete_at timestamp with time zone
+ auto_delete_at timestamp with time zone,
+ tier smallint
);
CREATE SEQUENCE environments_id_seq
@@ -22207,6 +22208,8 @@ CREATE UNIQUE INDEX index_environments_on_project_id_and_name ON environments US
CREATE UNIQUE INDEX index_environments_on_project_id_and_slug ON environments USING btree (project_id, slug);
+CREATE INDEX index_environments_on_project_id_and_tier ON environments USING btree (project_id, tier) WHERE (tier IS NOT NULL);
+
CREATE INDEX index_environments_on_project_id_state_environment_type ON environments USING btree (project_id, state, environment_type);
CREATE INDEX index_environments_on_state_and_auto_stop_at ON environments USING btree (state, auto_stop_at) WHERE ((auto_stop_at IS NOT NULL) AND ((state)::text = 'available'::text));
diff --git a/doc/administration/issue_closing_pattern.md b/doc/administration/issue_closing_pattern.md
index 6b1922fe142..3b9ec74e6ee 100644
--- a/doc/administration/issue_closing_pattern.md
+++ b/doc/administration/issue_closing_pattern.md
@@ -12,7 +12,7 @@ This is the administration documentation. There is a separate [user documentatio
on issue closing pattern.
When a commit or merge request resolves one or more issues, it is possible to
-automatically have these issues closed when the commit or merge request lands
+automatically close these issues when the commit or merge request lands
in the project's default branch.
## Change the issue closing pattern
@@ -35,7 +35,7 @@ Because Rubular doesn't understand `%{issue_ref}`, you can replace this by
expression of your liking:
```ruby
- gitlab_rails['gitlab_issue_closing_pattern'] = "\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)"
+ gitlab_rails['gitlab_issue_closing_pattern'] = /\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)/.source
```
1. [Reconfigure](restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect.
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index f34d03b36c4..b060ae696a5 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -72,7 +72,7 @@ There are also [Kubernetes-specific deployment variables](../../user/project/clu
| `CI_PIPELINE_SOURCE` | 10.0 | all | How the pipeline was triggered. Can be `push`, `web`, `schedule`, `api`, `external`, `chat`, `webide`, `merge_request_event`, `external_pull_request_event`, `parent_pipeline`, [`trigger`, or `pipeline`](../triggers/README.md#authentication-tokens). |
| `CI_PIPELINE_TRIGGERED` | all | all | `true` if the job was [triggered](../triggers/README.md). |
| `CI_PIPELINE_URL` | 11.1 | 0.5 | The URL for the pipeline details. |
-| `CI_PROJECT_CONFIG_PATH` | 13.8 | all | The CI/CD configuration path for the project. |
+| `CI_PROJECT_CONFIG_PATH` | 13.8 | all | (Deprecated) The CI configuration path for the project. [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/321334) in GitLab 13.10. [Removal planned](https://gitlab.com/gitlab-org/gitlab/-/issues/322807) for GitLab 14.0. |
| `CI_PROJECT_DIR` | all | all | The full path the repository is cloned to, and where the job runs from. If the GitLab Runner `builds_dir` parameter is set, this variable is set relative to the value of `builds_dir`. For more information, see the [Advanced GitLab Runner configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section). |
| `CI_PROJECT_ID` | all | all | The ID of the current project. This ID is unique across all projects on the GitLab instance. |
| `CI_PROJECT_NAME` | 8.10 | 0.5 | The name of the directory for the project. For example if the project URL is `gitlab.example.com/group-name/project-1`, `CI_PROJECT_NAME` is `project-1`. |
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index 2c07f159f3e..c8d7207672c 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -109,7 +109,7 @@ Running a DAST scan requires a URL. There are two ways to define the URL to be s
If both values are set, the `DAST_WEBSITE` value takes precedence.
The included template creates a `dast` job in your CI/CD pipeline and scans
-your project's source code for possible vulnerabilities.
+your project's running application for possible vulnerabilities.
The results are saved as a
[DAST report artifact](../../../ci/pipelines/job_artifacts.md#artifactsreportsdast)
@@ -127,6 +127,73 @@ image. Using the `DAST_VERSION` variable, you can choose how DAST updates:
Find the latest DAST versions on the [Releases](https://gitlab.com/gitlab-org/security-products/dast/-/releases) page.
+## Deployment options
+
+Depending on the complexity of the target application, there are a few options as to how to deploy and configure
+the DAST template. A set of example applications with their configurations have been made available in our
+[DAST demonstrations](https://gitlab.com/gitlab-org/security-products/demos/dast/) project.
+
+### Review Apps
+
+Review Apps are the most involved method of deploying your DAST target application. To assist in the process,
+we created a Review App deployment using Google Kubernetes Engine (GKE). This example can be found in our
+[Review Apps - GKE](https://gitlab.com/gitlab-org/security-products/demos/dast/review-app-gke) project along with detailed
+instructions in the [README.md](https://gitlab.com/gitlab-org/security-products/demos/dast/review-app-gke/-/blob/master/README.md)
+on how to configure Review Apps for DAST.
+
+### Docker Services
+
+If your application utilizes Docker containers you have another option for deploying and scanning with DAST.
+After your Docker build job completes and your image is added to your container registry, you can utilize the image as a
+[service](../../../ci/docker/using_docker_images.md#what-is-a-service).
+
+By using service definitions in your `gitlab-ci.yml`, you can scan services with the DAST analyzer.
+
+```yaml
+stages:
+ - build
+ - dast
+
+include:
+ - template: DAST.gitlab-ci.yml
+
+# Deploys the container to the GitLab container registry
+deploy:
+ services:
+ - name: docker:dind
+ alias: dind
+ image: docker:19.03.5
+ stage: build
+ script:
+ - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
+ - docker pull $CI_REGISTRY_IMAGE:latest || true
+ - docker build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --tag $CI_REGISTRY_IMAGE:latest .
+ - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
+ - docker push $CI_REGISTRY_IMAGE:latest
+
+services: # use services to link your app container to the dast job
+ - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
+ alias: yourapp
+
+variables:
+ DAST_FULL_SCAN_ENABLED: "true" # do a full scan
+ DAST_ZAP_USE_AJAX_SPIDER: "true" # use the ajax spider
+```
+
+Most applications depend on multiple services such as databases or caching services. By default, services defined in the services fields cannot communicate
+with each another. To allow communication between services, enable the `FF_NETWORK_PER_BUILD` [feature flag](https://docs.gitlab.com/runner/configuration/feature-flags.html#available-feature-flags).
+
+```yaml
+variables:
+ FF_NETWORK_PER_BUILD: "true" # enable network per build so all services can communicate on the same network
+
+services: # use services to link the container to the dast job
+ - name: mongo:latest
+ alias: mongo
+ - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
+ alias: yourapp
+```
+
### DAST application analysis
DAST can analyze applications in two ways:
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
index 0c40db02eb5..09744fbeda2 100644
--- a/lib/api/group_variables.rb
+++ b/lib/api/group_variables.rb
@@ -5,8 +5,7 @@ module API
include PaginationParams
before { authenticate! }
- before { authorize! :admin_build, user_group }
-
+ before { authorize! :admin_group, user_group }
feature_category :continuous_integration
params do
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb
index cf769262958..1d00f502d1c 100644
--- a/lib/gitlab/diff/inline_diff.rb
+++ b/lib/gitlab/diff/inline_diff.rb
@@ -3,22 +3,6 @@
module Gitlab
module Diff
class InlineDiff
- # Regex to find a run of deleted lines followed by the same number of added lines
- LINE_PAIRS_PATTERN = %r{
- # Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
- (?:\A|\s)
-
- # This matches a number of `-`s followed by the same number of `+`s through recursion
- (?<del_ins>
- -
- \g<del_ins>?
- \+
- )
-
- # Runs end at the end of the string (the last line) or before a space (for an unchanged line)
- (?=\s|\z)
- }x.freeze
-
attr_accessor :old_line, :new_line, :offset
def initialize(old_line, new_line, offset: 0)
@@ -40,11 +24,11 @@ module Gitlab
class << self
def for_lines(lines, project: nil)
- changed_line_pairs = find_changed_line_pairs(lines)
+ pair_selector = Gitlab::Diff::PairSelector.new(lines)
inline_diffs = []
- changed_line_pairs.each do |old_index, new_index|
+ pair_selector.each do |old_index, new_index|
old_line = lines[old_index]
new_line = lines[new_index]
@@ -56,34 +40,6 @@ module Gitlab
inline_diffs
end
-
- private
-
- # Finds pairs of old/new line pairs that represent the same line that changed
- # rubocop: disable CodeReuse/ActiveRecord
- def find_changed_line_pairs(lines)
- # Prefixes of all diff lines, indicating their types
- # For example: `" - + -+ ---+++ --+ -++"`
- line_prefixes = lines.each_with_object(+"") { |line, s| s << (line[0] || ' ') }.gsub(/[^ +-]/, ' ')
-
- changed_line_pairs = []
- line_prefixes.scan(LINE_PAIRS_PATTERN) do
- # For `"---+++"`, `begin_index == 0`, `end_index == 6`
- begin_index, end_index = Regexp.last_match.offset(:del_ins)
-
- # For `"---+++"`, `changed_line_count == 3`
- changed_line_count = (end_index - begin_index) / 2
-
- halfway_index = begin_index + changed_line_count
- (begin_index...halfway_index).each do |i|
- # For `"---+++"`, index 1 maps to 1 + 3 = 4
- changed_line_pairs << [i, i + changed_line_count]
- end
- end
-
- changed_line_pairs
- end
- # rubocop: enable CodeReuse/ActiveRecord
end
private
diff --git a/lib/gitlab/diff/pair_selector.rb b/lib/gitlab/diff/pair_selector.rb
new file mode 100644
index 00000000000..2e5ee3a7363
--- /dev/null
+++ b/lib/gitlab/diff/pair_selector.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class PairSelector
+ include Enumerable
+
+ # Regex to find a run of deleted lines followed by the same number of added lines
+ # rubocop: disable Lint/MixedRegexpCaptureTypes
+ LINE_PAIRS_PATTERN = %r{
+ # Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
+ (?:\A|\s)
+
+ # This matches a number of `-`s followed by the same number of `+`s through recursion
+ (?<del_ins>
+ -
+ \g<del_ins>?
+ \+
+ )
+
+ # Runs end at the end of the string (the last line) or before a space (for an unchanged line)
+ (?=\s|\z)
+ }x.freeze
+ # rubocop: enable Lint/MixedRegexpCaptureTypes
+
+ def initialize(lines)
+ @lines = lines
+ end
+
+ # Finds pairs of old/new line pairs that represent the same line that changed
+ # rubocop: disable CodeReuse/ActiveRecord
+ def each
+ # Prefixes of all diff lines, indicating their types
+ # For example: `" - + -+ ---+++ --+ -++"`
+ line_prefixes = lines.each_with_object(+"") { |line, s| s << (line[0] || ' ') }.gsub(/[^ +-]/, ' ')
+
+ line_prefixes.scan(LINE_PAIRS_PATTERN) do
+ # For `"---+++"`, `begin_index == 0`, `end_index == 6`
+ begin_index, end_index = Regexp.last_match.offset(:del_ins)
+
+ # For `"---+++"`, `changed_line_count == 3`
+ changed_line_count = (end_index - begin_index) / 2
+
+ halfway_index = begin_index + changed_line_count
+ (begin_index...halfway_index).each do |i|
+ # For `"---+++"`, index 1 maps to 1 + 3 = 4
+ yield [i, i + changed_line_count]
+ end
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ attr_reader :lines
+ end
+ end
+end
diff --git a/lib/gitlab/git/wiki_page_version.rb b/lib/gitlab/git/wiki_page_version.rb
index efe39fa852c..1de0a0204cf 100644
--- a/lib/gitlab/git/wiki_page_version.rb
+++ b/lib/gitlab/git/wiki_page_version.rb
@@ -3,6 +3,8 @@
module Gitlab
module Git
class WikiPageVersion
+ include Gitlab::Utils::StrongMemoize
+
attr_reader :commit, :format
def initialize(commit, format)
@@ -12,9 +14,10 @@ module Gitlab
delegate :message, :sha, :id, :author_name, :author_email, :authored_date, to: :commit
- def author_url
- user = ::User.find_by_any_email(author_email)
- user.nil? ? "mailto:#{author_email}" : Gitlab::UrlBuilder.build(user)
+ def author
+ strong_memoize(:author) do
+ ::User.find_by_any_email(author_email)
+ end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0ac4f4fbf6e..0834b018323 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -27104,7 +27104,7 @@ msgstr ""
msgid "SelfMonitoring|Self monitoring project has been successfully deleted."
msgstr ""
-msgid "Send a separate email notification to Developers."
+msgid "Send a single email notification to Owners and Maintainers for new alerts."
msgstr ""
msgid "Send confirmation email"
diff --git a/package.json b/package.json
index 4672866478f..4dfa0d50fa6 100644
--- a/package.json
+++ b/package.json
@@ -141,7 +141,7 @@
"sql.js": "^0.4.0",
"string-hash": "1.1.3",
"style-loader": "^1.3.0",
- "swagger-ui-dist": "^3.32.4",
+ "swagger-ui-dist": "^3.43.0",
"three": "^0.84.0",
"three-orbit-controls": "^82.1.0",
"three-stl-loader": "^1.0.4",
diff --git a/spec/controllers/groups/variables_controller_spec.rb b/spec/controllers/groups/variables_controller_spec.rb
index e2a14165cb4..a450a4afb02 100644
--- a/spec/controllers/groups/variables_controller_spec.rb
+++ b/spec/controllers/groups/variables_controller_spec.rb
@@ -5,26 +5,35 @@ require 'spec_helper'
RSpec.describe Groups::VariablesController do
include ExternalAuthorizationServiceHelpers
- let(:group) { create(:group) }
- let(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:variable) { create(:ci_group_variable, group: group) }
+ let(:access_level) { :owner }
before do
sign_in(user)
- group.add_maintainer(user)
+ group.add_user(user, access_level)
end
describe 'GET #show' do
- let!(:variable) { create(:ci_group_variable, group: group) }
-
subject do
get :show, params: { group_id: group }, format: :json
end
include_examples 'GET #show lists all variables'
+
+ context 'when the user is a maintainer' do
+ let(:access_level) { :maintainer }
+
+ it 'returns not found response' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
describe 'PATCH #update' do
- let!(:variable) { create(:ci_group_variable, group: group) }
let(:owner) { group }
subject do
@@ -37,6 +46,19 @@ RSpec.describe Groups::VariablesController do
end
include_examples 'PATCH #update updates variables'
+
+ context 'when the user is a maintainer' do
+ let(:access_level) { :maintainer }
+ let(:variables_attributes) do
+ [{ id: variable.id, key: 'new_key' }]
+ end
+
+ it 'returns not found response' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
context 'with external authorization enabled' do
@@ -45,8 +67,6 @@ RSpec.describe Groups::VariablesController do
end
describe 'GET #show' do
- let!(:variable) { create(:ci_group_variable, group: group) }
-
it 'is successful' do
get :show, params: { group_id: group }, format: :json
@@ -55,9 +75,6 @@ RSpec.describe Groups::VariablesController do
end
describe 'PATCH #update' do
- let!(:variable) { create(:ci_group_variable, group: group) }
- let(:owner) { group }
-
it 'is successful' do
patch :update,
params: {
diff --git a/spec/controllers/profiles/active_sessions_controller_spec.rb b/spec/controllers/profiles/active_sessions_controller_spec.rb
index f54f69d853d..12cf4f982e9 100644
--- a/spec/controllers/profiles/active_sessions_controller_spec.rb
+++ b/spec/controllers/profiles/active_sessions_controller_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Profiles::ActiveSessionsController do
it 'invalidates all remember user tokens' do
ActiveSession.set(user, request)
- session_id = request.session.id.public_id
+ session_id = request.session.id.private_id
user.remember_me!
delete :destroy, params: { id: session_id }
diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb
index 050cb8f8e6c..12710b6dcf3 100644
--- a/spec/factories/environments.rb
+++ b/spec/factories/environments.rb
@@ -15,6 +15,22 @@ FactoryBot.define do
state { :stopped }
end
+ trait :production do
+ tier { :production }
+ end
+
+ trait :staging do
+ tier { :staging }
+ end
+
+ trait :testing do
+ tier { :testing }
+ end
+
+ trait :development do
+ tier { :development }
+ end
+
trait :with_review_app do |environment|
transient do
ref { 'master' }
diff --git a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb
index f5941d0ff15..50fc7bb0753 100644
--- a/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb
+++ b/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb
@@ -104,7 +104,7 @@ RSpec.describe 'User sees feature flag list', :js do
it 'shows empty page' do
expect(page).to have_text 'Get started with feature flags'
- expect(page).to have_selector('.btn-success', text: 'New feature flag')
+ expect(page).to have_selector('.btn-confirm', text: 'New feature flag')
expect(page).to have_selector('[data-qa-selector="configure_feature_flags_button"]', text: 'Configure')
end
end
diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb
index 1d9f256a819..e3b36348f25 100644
--- a/spec/features/projects/settings/operations_settings_spec.rb
+++ b/spec/features/projects/settings/operations_settings_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
describe 'Settings > Operations' do
describe 'Incidents' do
let(:create_issue) { 'Create an incident. Incidents are created for each alert triggered.' }
- let(:send_email) { 'Send a separate email notification to Developers.' }
+ let(:send_email) { 'Send a single email notification to Owners and Maintainers for new alerts.' }
before do
create(:project_incident_management_setting, send_email: true, project: project)
diff --git a/spec/features/projects/settings/user_searches_in_settings_spec.rb b/spec/features/projects/settings/user_searches_in_settings_spec.rb
index 563dff615d6..4c5b39d5282 100644
--- a/spec/features/projects/settings/user_searches_in_settings_spec.rb
+++ b/spec/features/projects/settings/user_searches_in_settings_spec.rb
@@ -37,6 +37,6 @@ RSpec.describe 'User searches project settings', :js do
visit project_settings_operations_path(project)
end
- it_behaves_like 'can search settings', 'Alerts', 'Incidents'
+ it_behaves_like 'can search settings', 'Alerts', 'Error tracking'
end
end
diff --git a/spec/frontend/alert_management/components/alert_management_table_spec.js b/spec/frontend/alert_management/components/alert_management_table_spec.js
index 233585735b6..dece3dfbe5f 100644
--- a/spec/frontend/alert_management/components/alert_management_table_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_table_spec.js
@@ -251,7 +251,7 @@ describe('AlertManagementTable', () => {
const tooltip = getBinding(issueField.element, 'gl-tooltip');
expect(issueField.text()).toBe(`#1 (closed)`);
- expect(issueField.attributes('href')).toBe('/gitlab-org/gitlab/-/issues/1');
+ expect(issueField.attributes('href')).toBe('/gitlab-org/gitlab/-/issues/incident/1');
expect(issueField.attributes('title')).toBe('My test issue');
expect(tooltip).not.toBe(undefined);
});
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
index 6a4999c42db..5796b3fa44e 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
@@ -78,7 +78,7 @@ exports[`Alert integration settings form default state should match the default
>
<gl-form-checkbox-stub>
<span>
- Send a separate email notification to Developers.
+ Send a single email notification to Owners and Maintainers for new alerts.
</span>
</gl-form-checkbox-stub>
</gl-form-group-stub>
diff --git a/spec/frontend/vue_shared/alert_details/alert_details_spec.js b/spec/frontend/vue_shared/alert_details/alert_details_spec.js
index 20301e1bc88..68bcf1dc491 100644
--- a/spec/frontend/vue_shared/alert_details/alert_details_spec.js
+++ b/spec/frontend/vue_shared/alert_details/alert_details_spec.js
@@ -188,6 +188,18 @@ describe('AlertDetails', () => {
});
expect(findMetricsTab().exists()).toBe(false);
});
+
+ it('should display "View incident" button that links the issues page when incident exists', () => {
+ const iid = '3';
+ mountComponent({
+ data: { alert: { ...mockAlert, issue: { iid } }, sidebarStatus: false },
+ provide: { isThreatMonitoringPage: true },
+ });
+
+ expect(findViewIncidentBtn().exists()).toBe(true);
+ expect(findViewIncidentBtn().attributes('href')).toBe(joinPaths(projectIssuesPath, iid));
+ expect(findCreateIncidentBtn().exists()).toBe(false);
+ });
});
describe('Create incident from alert', () => {
@@ -198,7 +210,9 @@ describe('AlertDetails', () => {
});
expect(findViewIncidentBtn().exists()).toBe(true);
- expect(findViewIncidentBtn().attributes('href')).toBe(joinPaths(projectIssuesPath, iid));
+ expect(findViewIncidentBtn().attributes('href')).toBe(
+ joinPaths(projectIssuesPath, 'incident', iid),
+ );
expect(findCreateIncidentBtn().exists()).toBe(false);
});
diff --git a/spec/helpers/wiki_page_version_helper_spec.rb b/spec/helpers/wiki_page_version_helper_spec.rb
new file mode 100644
index 00000000000..bc500c28c5a
--- /dev/null
+++ b/spec/helpers/wiki_page_version_helper_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WikiPageVersionHelper do
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:user) { create(:user, username: 'foo') }
+
+ let(:commit_with_user) { create(:commit, project: project, author: user)}
+ let(:commit_without_user) { create(:commit, project: project, author_name: 'Foo', author_email: 'foo@example.com')}
+ let(:wiki_page_version) { Gitlab::Git::WikiPageVersion.new(commit, nil) }
+
+ describe '#wiki_page_version_author_url' do
+ subject { helper.wiki_page_version_author_url(wiki_page_version) }
+
+ context 'when user exists' do
+ let(:commit) { commit_with_user }
+
+ it 'returns the link to the user profile' do
+ expect(subject).to eq('http://localhost/foo')
+ end
+ end
+
+ context 'when user does not exist' do
+ let(:commit) { commit_without_user }
+
+ it 'returns the mailto link' do
+ expect(subject).to eq "mailto:#{commit_without_user.author_email}"
+ end
+ end
+ end
+
+ describe '#wiki_page_version_author_avatar' do
+ let(:commit) { commit_with_user }
+
+ subject { helper.wiki_page_version_author_avatar(wiki_page_version) }
+
+ it 'returns the user avatar', :aggregate_failures do
+ avatar = Nokogiri::HTML.parse(subject)
+
+ expect(avatar.css('img')[0].attr('class')).to eq('avatar s24 float-none gl-mr-0! lazy')
+ expect(avatar.css('img')[0].attr('data-src')).not_to be_empty
+ expect(avatar.css('img')[0].attr('src')).not_to be_empty
+ end
+ end
+
+ describe '#wiki_page_version_author_header', :aggregate_failures do
+ let(:commit_with_xss) { create(:commit, project: project, author_email: "#' style=animation-name:blinking-dot onanimationstart=alert(document.domain) other", author_name: "<i>foo</i>") }
+ let(:header) { Nokogiri::HTML.parse(subject) }
+
+ subject { helper.wiki_page_version_author_header(wiki_page_version) }
+
+ context 'when user exists' do
+ let(:commit) { commit_with_user }
+
+ it 'renders commit header with user info' do
+ expect(header.css('a')[0].attr('href')).to eq("http://localhost/foo")
+ expect(header.css('a')[0].children[2].to_s).to eq("<strong>#{user.name}</strong>")
+ end
+ end
+
+ context 'when user does not exist' do
+ let(:commit) { commit_without_user }
+
+ it 'renders commit header with info from commit' do
+ expect(header.css('a')[0].attr('href')).to eq("mailto:#{commit.author_email}")
+ expect(header.css('a')[0].children[2].to_s).to eq("<strong>#{wiki_page_version.author_name}</strong>")
+ end
+ end
+
+ context 'when user info has XSS' do
+ let(:commit) { commit_with_xss }
+
+ it 'sets the right href and escapes HTML chars' do
+ expect(header.css('a')[0].attr('href')).to eq("mailto:#{commit.author_email}")
+ expect(header.css('a')[0].children[2].to_s).to eq("<strong>&lt;i&gt;foo&lt;/i&gt;</strong>")
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/pair_selector_spec.rb b/spec/lib/gitlab/diff/pair_selector_spec.rb
new file mode 100644
index 00000000000..da5707bc377
--- /dev/null
+++ b/spec/lib/gitlab/diff/pair_selector_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Diff::PairSelector do
+ subject(:selector) { described_class.new(lines) }
+
+ describe '#to_a' do
+ subject { selector.to_a }
+
+ let(:lines) { diff.lines }
+
+ let(:diff) do
+ <<-EOF.strip_heredoc
+ class Test # 0
+ - def initialize(test = true) # 1
+ + def initialize(test = false) # 2
+ @test = test # 3
+ - if true # 4
+ - @foo = "bar" # 5
+ + unless false # 6
+ + @foo = "baz" # 7
+ end
+ end
+ end
+ EOF
+ end
+
+ it 'finds all pairs' do
+ is_expected.to match_array([[1, 2], [4, 6], [5, 7]])
+ end
+
+ context 'when there are empty lines' do
+ let(:lines) { ['- bar', '+ baz', ''] }
+
+ it { expect { subject }.not_to raise_error }
+ end
+
+ context 'when there are only removals' do
+ let(:diff) do
+ <<-EOF.strip_heredoc
+ - class Test
+ - def initialize(test = true)
+ - end
+ - end
+ EOF
+ end
+
+ it 'returns empty collection' do
+ is_expected.to eq([])
+ end
+ end
+
+ context 'when there are only additions' do
+ let(:diff) do
+ <<-EOF.strip_heredoc
+ + class Test
+ + def initialize(test = true)
+ + end
+ + end
+ EOF
+ end
+
+ it 'returns empty collection' do
+ is_expected.to eq([])
+ end
+ end
+
+ context 'when there are no changes' do
+ let(:diff) do
+ <<-EOF.strip_heredoc
+ class Test
+ def initialize(test = true)
+ end
+ end
+ EOF
+ end
+
+ it 'returns empty collection' do
+ is_expected.to eq([])
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/wiki_page_version_spec.rb b/spec/lib/gitlab/git/wiki_page_version_spec.rb
index 836fa2449ec..b117e757f6e 100644
--- a/spec/lib/gitlab/git/wiki_page_version_spec.rb
+++ b/spec/lib/gitlab/git/wiki_page_version_spec.rb
@@ -4,24 +4,24 @@ require 'spec_helper'
RSpec.describe Gitlab::Git::WikiPageVersion do
let_it_be(:project) { create(:project, :public, :repository) }
- let(:user) { create(:user, username: 'someone') }
+ let_it_be(:user) { create(:user, username: 'someone') }
- describe '#author_url' do
- subject(:author_url) { described_class.new(commit, nil).author_url }
+ describe '#author' do
+ subject(:author) { described_class.new(commit, nil).author }
context 'user exists in gitlab' do
let(:commit) { create(:commit, project: project, author: user) }
- it 'returns the profile link of the user' do
- expect(author_url).to eq('http://localhost/someone')
+ it 'returns the user' do
+ expect(author).to eq user
end
end
context 'user does not exist in gitlab' do
let(:commit) { create(:commit, project: project, author_email: "someone@somewebsite.com") }
- it 'returns a mailto: url' do
- expect(author_url).to eq('mailto:someone@somewebsite.com')
+ it 'returns nil' do
+ expect(author).to be_nil
end
end
end
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb
index 51435cc4342..2fd7b127500 100644
--- a/spec/models/active_session_spec.rb
+++ b/spec/models/active_session_spec.rb
@@ -42,17 +42,6 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end
end
- describe '#public_id' do
- it 'returns an encrypted, url-encoded session id' do
- original_session_id = Rack::Session::SessionId.new("!*'();:@&\n=+$,/?%abcd#123[4567]8")
- active_session = ActiveSession.new(session_id: original_session_id.public_id)
- encrypted_id = active_session.public_id
- derived_session_id = Gitlab::CryptoHelper.aes256_gcm_decrypt(encrypted_id)
-
- expect(original_session_id.public_id).to eq derived_session_id
- end
- end
-
describe '.list' do
it 'returns all sessions by user' do
Gitlab::Redis::SharedState.with do |redis|
@@ -207,89 +196,9 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end
end
end
-
- context 'ActiveSession stored by deprecated rack_session_public_key' do
- let(:active_session) { ActiveSession.new(session_id: rack_session.public_id) }
- let(:deprecated_active_session_lookup_key) { rack_session.public_id }
-
- before do
- Gitlab::Redis::SharedState.with do |redis|
- redis.set("session:user:gitlab:#{user.id}:#{deprecated_active_session_lookup_key}",
- '')
- redis.sadd(described_class.lookup_key_name(user.id),
- deprecated_active_session_lookup_key)
- end
- end
-
- it 'removes deprecated key and stores only new one' do
- expected_session_keys = ["session:user:gitlab:#{user.id}:#{rack_session.private_id}",
- "session:lookup:user:gitlab:#{user.id}"]
-
- ActiveSession.set(user, request)
-
- Gitlab::Redis::SharedState.with do |redis|
- actual_session_keys = redis.scan_each(match: 'session:*').to_a
- expect(actual_session_keys).to(match_array(expected_session_keys))
-
- expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq [rack_session.private_id]
- end
- end
- end
end
- describe '.destroy_with_rack_session_id' do
- it 'gracefully handles a nil session ID' do
- expect(described_class).not_to receive(:destroy_sessions)
-
- ActiveSession.destroy_with_rack_session_id(user, nil)
- end
-
- it 'removes the entry associated with the currently killed user session' do
- Gitlab::Redis::SharedState.with do |redis|
- redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
- redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", '')
- redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
- end
-
- ActiveSession.destroy_with_rack_session_id(user, request.session.id)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.scan_each(match: "session:user:gitlab:*")).to match_array [
- "session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d",
- "session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358"
- ]
- end
- end
-
- it 'removes the lookup entry' do
- Gitlab::Redis::SharedState.with do |redis|
- redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
- redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
- end
-
- ActiveSession.destroy_with_rack_session_id(user, request.session.id)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty
- end
- end
-
- it 'removes the devise session' do
- Gitlab::Redis::SharedState.with do |redis|
- redis.set("session:user:gitlab:#{user.id}:#{rack_session.private_id}", '')
- # Emulate redis-rack: https://github.com/redis-store/redis-rack/blob/c75f7f1a6016ee224e2615017fbfee964f23a837/lib/rack/session/redis.rb#L88
- redis.set("session:gitlab:#{rack_session.private_id}", '')
- end
-
- ActiveSession.destroy_with_rack_session_id(user, request.session.id)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty
- end
- end
- end
-
- describe '.destroy_with_deprecated_encryption' do
+ describe '.destroy_session' do
shared_examples 'removes all session data' do
before do
Gitlab::Redis::SharedState.with do |redis|
@@ -330,7 +239,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end
context 'destroy called with Rack::Session::SessionId#private_id' do
- subject { ActiveSession.destroy_with_deprecated_encryption(user, rack_session.private_id) }
+ subject { ActiveSession.destroy_session(user, rack_session.private_id) }
it 'calls .destroy_sessions' do
expect(ActiveSession).to(
@@ -347,26 +256,6 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
include_examples 'removes all session data'
end
end
-
- context 'destroy called with ActiveSession#public_id (deprecated)' do
- let(:active_session) { ActiveSession.new(session_id: rack_session.public_id) }
- let(:encrypted_active_session_id) { active_session.public_id }
- let(:active_session_lookup_key) { rack_session.public_id }
-
- subject { ActiveSession.destroy_with_deprecated_encryption(user, encrypted_active_session_id) }
-
- it 'calls .destroy_sessions' do
- expect(ActiveSession).to(
- receive(:destroy_sessions)
- .with(anything, user, [encrypted_active_session_id, rack_session.public_id, rack_session.private_id]))
-
- subject
- end
-
- context 'ActiveSession with session_id (deprecated)' do
- include_examples 'removes all session data'
- end
- end
end
describe '.destroy_all_but_current' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 945446ea816..28901c52f76 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2405,6 +2405,7 @@ RSpec.describe Ci::Build do
{ key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: project.repository_languages.map(&:name).join(',').downcase, public: true, masked: false },
{ key: 'CI_DEFAULT_BRANCH', value: project.default_branch, public: true, masked: false },
{ key: 'CI_PROJECT_CONFIG_PATH', value: project.ci_config_path_or_default, public: true, masked: false },
+ { key: 'CI_CONFIG_PATH', value: project.ci_config_path_or_default, public: true, masked: false },
{ key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host, public: true, masked: false },
{ key: 'CI_PAGES_URL', value: project.pages_url, public: true, masked: false },
{ key: 'CI_DEPENDENCY_PROXY_SERVER', value: "#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.port}", public: true, masked: false },
@@ -2415,7 +2416,6 @@ RSpec.describe Ci::Build do
{ key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true, masked: false },
{ key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true, masked: false },
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false },
- { key: 'CI_CONFIG_PATH', value: pipeline.config_path, public: true, masked: false },
{ key: 'CI_COMMIT_SHA', value: build.sha, public: true, masked: false },
{ key: 'CI_COMMIT_SHORT_SHA', value: build.short_sha, public: true, masked: false },
{ key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true, masked: false },
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index a487ba86bbf..60ff2478997 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -807,7 +807,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
expect(keys).to eq %w[
CI_PIPELINE_IID
CI_PIPELINE_SOURCE
- CI_CONFIG_PATH
CI_COMMIT_SHA
CI_COMMIT_SHORT_SHA
CI_COMMIT_BEFORE_SHA
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 0c7d8e2969d..30eb752d2d5 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -34,6 +34,39 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
+ describe '.before_save' do
+ it 'ensures environment tier when a new object is created' do
+ environment = build(:environment, name: 'gprd', tier: nil)
+
+ expect { environment.save }.to change { environment.tier }.from(nil).to('production')
+ end
+
+ it 'ensures environment tier when an existing object is updated' do
+ environment = create(:environment, name: 'gprd')
+ environment.update_column(:tier, nil)
+
+ expect { environment.stop! }.to change { environment.reload.tier }.from(nil).to('production')
+ end
+
+ it 'does not overwrite the existing environment tier' do
+ environment = create(:environment, name: 'gprd', tier: :production)
+
+ expect { environment.update!(name: 'gstg') }.not_to change { environment.reload.tier }
+ end
+
+ context 'when environment_tier feature flag is disabled' do
+ before do
+ stub_feature_flags(environment_tier: false)
+ end
+
+ it 'does not ensure environment tier' do
+ environment = build(:environment, name: 'gprd', tier: nil)
+
+ expect { environment.save }.not_to change { environment.tier }
+ end
+ end
+ end
+
describe '.order_by_last_deployed_at' do
let!(:environment1) { create(:environment, project: project) }
let!(:environment2) { create(:environment, project: project) }
@@ -195,6 +228,62 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
+ describe '.for_tier' do
+ let_it_be(:environment) { create(:environment, :production) }
+
+ it 'returns the production environment when searching for production tier' do
+ expect(described_class.for_tier(:production)).to eq([environment])
+ end
+
+ it 'returns nothing when searching for staging tier' do
+ expect(described_class.for_tier(:staging)).to be_empty
+ end
+ end
+
+ describe '#guess_tier' do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { environment.send(:guess_tier) }
+
+ let(:environment) { build(:environment, name: name) }
+
+ where(:name, :tier) do
+ 'review/feature' | described_class.tiers[:development]
+ 'review/product' | described_class.tiers[:development]
+ 'DEV' | described_class.tiers[:development]
+ 'development' | described_class.tiers[:development]
+ 'trunk' | described_class.tiers[:development]
+ 'test' | described_class.tiers[:testing]
+ 'TEST' | described_class.tiers[:testing]
+ 'testing' | described_class.tiers[:testing]
+ 'testing-prd' | described_class.tiers[:testing]
+ 'acceptance-testing' | described_class.tiers[:testing]
+ 'QC' | described_class.tiers[:testing]
+ 'gstg' | described_class.tiers[:staging]
+ 'staging' | described_class.tiers[:staging]
+ 'stage' | described_class.tiers[:staging]
+ 'Model' | described_class.tiers[:staging]
+ 'MODL' | described_class.tiers[:staging]
+ 'Pre-production' | described_class.tiers[:staging]
+ 'pre' | described_class.tiers[:staging]
+ 'Demo' | described_class.tiers[:staging]
+ 'gprd' | described_class.tiers[:production]
+ 'gprd-cny' | described_class.tiers[:production]
+ 'production' | described_class.tiers[:production]
+ 'Production' | described_class.tiers[:production]
+ 'prod' | described_class.tiers[:production]
+ 'PROD' | described_class.tiers[:production]
+ 'Live' | described_class.tiers[:production]
+ 'canary' | described_class.tiers[:other]
+ 'other' | described_class.tiers[:other]
+ 'EXP' | described_class.tiers[:other]
+ end
+
+ with_them do
+ it { is_expected.to eq(tier) }
+ end
+ end
+
describe '#expire_etag_cache' do
let(:store) { Gitlab::EtagCaching::Store.new }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 59657e68d2b..6d0f01d2c8c 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -4466,7 +4466,11 @@ RSpec.describe Project, factory_default: :keep do
subject { project.predefined_project_variables.to_runner_variables }
specify do
- expect(subject).to include({ key: 'CI_PROJECT_CONFIG_PATH', value: Ci::Pipeline::DEFAULT_CONFIG_PATH, public: true, masked: false })
+ expect(subject).to include
+ [
+ { key: 'CI_PROJECT_CONFIG_PATH', value: Ci::Pipeline::DEFAULT_CONFIG_PATH, public: true, masked: false },
+ { key: 'CI_CONFIG_PATH', value: Ci::Pipeline::DEFAULT_CONFIG_PATH, public: true, masked: false }
+ ]
end
context 'when ci config path is overridden' do
@@ -4474,7 +4478,13 @@ RSpec.describe Project, factory_default: :keep do
project.update!(ci_config_path: 'random.yml')
end
- it { expect(subject).to include({ key: 'CI_PROJECT_CONFIG_PATH', value: 'random.yml', public: true, masked: false }) }
+ it do
+ expect(subject).to include
+ [
+ { key: 'CI_PROJECT_CONFIG_PATH', value: 'random.yml', public: true, masked: false },
+ { key: 'CI_CONFIG_PATH', value: 'random.yml', public: true, masked: false }
+ ]
+ end
end
end
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
index 41b013f49ee..0b6bf65ca44 100644
--- a/spec/requests/api/group_variables_spec.rb
+++ b/spec/requests/api/group_variables_spec.rb
@@ -3,16 +3,19 @@
require 'spec_helper'
RSpec.describe API::GroupVariables do
- let(:group) { create(:group) }
- let(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:variable) { create(:ci_group_variable, group: group) }
- describe 'GET /groups/:id/variables' do
- let!(:variable) { create(:ci_group_variable, group: group) }
+ let(:access_level) {}
+
+ before do
+ group.add_user(user, access_level) if access_level
+ end
+ describe 'GET /groups/:id/variables' do
context 'authorized user with proper permissions' do
- before do
- group.add_maintainer(user)
- end
+ let(:access_level) { :owner }
it 'returns group variables' do
get api("/groups/#{group.id}/variables", user)
@@ -23,6 +26,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
+ let(:access_level) { :maintainer }
+
it 'does not return group variables' do
get api("/groups/#{group.id}/variables", user)
@@ -40,12 +45,8 @@ RSpec.describe API::GroupVariables do
end
describe 'GET /groups/:id/variables/:key' do
- let!(:variable) { create(:ci_group_variable, group: group) }
-
context 'authorized user with proper permissions' do
- before do
- group.add_maintainer(user)
- end
+ let(:access_level) { :owner }
it 'returns group variable details' do
get api("/groups/#{group.id}/variables/#{variable.key}", user)
@@ -64,6 +65,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
+ let(:access_level) { :maintainer }
+
it 'does not return group variable details' do
get api("/groups/#{group.id}/variables/#{variable.key}", user)
@@ -82,11 +85,7 @@ RSpec.describe API::GroupVariables do
describe 'POST /groups/:id/variables' do
context 'authorized user with proper permissions' do
- let!(:variable) { create(:ci_group_variable, group: group) }
-
- before do
- group.add_maintainer(user)
- end
+ let(:access_level) { :owner }
it 'creates variable' do
expect do
@@ -124,6 +123,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
+ let(:access_level) { :maintainer }
+
it 'does not create variable' do
post api("/groups/#{group.id}/variables", user)
@@ -141,12 +142,8 @@ RSpec.describe API::GroupVariables do
end
describe 'PUT /groups/:id/variables/:key' do
- let!(:variable) { create(:ci_group_variable, group: group) }
-
context 'authorized user with proper permissions' do
- before do
- group.add_maintainer(user)
- end
+ let(:access_level) { :owner }
it 'updates variable data' do
initial_variable = group.variables.reload.first
@@ -180,6 +177,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
+ let(:access_level) { :maintainer }
+
it 'does not update variable' do
put api("/groups/#{group.id}/variables/#{variable.key}", user)
@@ -197,12 +196,8 @@ RSpec.describe API::GroupVariables do
end
describe 'DELETE /groups/:id/variables/:key' do
- let!(:variable) { create(:ci_group_variable, group: group) }
-
context 'authorized user with proper permissions' do
- before do
- group.add_maintainer(user)
- end
+ let(:access_level) { :owner }
it 'deletes variable' do
expect do
@@ -224,6 +219,8 @@ RSpec.describe API::GroupVariables do
end
context 'authorized user with invalid permissions' do
+ let(:access_level) { :maintainer }
+
it 'does not delete variable' do
delete api("/groups/#{group.id}/variables/#{variable.key}", user)
diff --git a/spec/rubocop/code_reuse_helpers_spec.rb b/spec/rubocop/code_reuse_helpers_spec.rb
index 4c3dd8f8167..9337df368e3 100644
--- a/spec/rubocop/code_reuse_helpers_spec.rb
+++ b/spec/rubocop/code_reuse_helpers_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require 'parser/current'
require_relative '../../rubocop/code_reuse_helpers'
diff --git a/spec/rubocop/cop/active_record_association_reload_spec.rb b/spec/rubocop/cop/active_record_association_reload_spec.rb
index f28c4e60f3c..1c0518815ee 100644
--- a/spec/rubocop/cop/active_record_association_reload_spec.rb
+++ b/spec/rubocop/cop/active_record_association_reload_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/active_record_association_reload'
RSpec.describe RuboCop::Cop::ActiveRecordAssociationReload do
diff --git a/spec/rubocop/cop/api/base_spec.rb b/spec/rubocop/cop/api/base_spec.rb
index ec646b9991b..547d3f53a08 100644
--- a/spec/rubocop/cop/api/base_spec.rb
+++ b/spec/rubocop/cop/api/base_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/api/base'
RSpec.describe RuboCop::Cop::API::Base do
diff --git a/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb b/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb
index b50866b54b3..01f1fc71f9a 100644
--- a/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb
+++ b/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/api/grape_array_missing_coerce'
RSpec.describe RuboCop::Cop::API::GrapeArrayMissingCoerce do
diff --git a/spec/rubocop/cop/avoid_becomes_spec.rb b/spec/rubocop/cop/avoid_becomes_spec.rb
index 401c694f373..3ab1544b00d 100644
--- a/spec/rubocop/cop/avoid_becomes_spec.rb
+++ b/spec/rubocop/cop/avoid_becomes_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/avoid_becomes'
RSpec.describe RuboCop::Cop::AvoidBecomes do
diff --git a/spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb b/spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb
index ac59d36db3f..cc851045c3c 100644
--- a/spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb
+++ b/spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/avoid_break_from_strong_memoize'
RSpec.describe RuboCop::Cop::AvoidBreakFromStrongMemoize do
diff --git a/spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb b/spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb
index 460a0b13458..90ee5772b66 100644
--- a/spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb
+++ b/spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers'
RSpec.describe RuboCop::Cop::AvoidKeywordArgumentsInSidekiqWorkers do
diff --git a/spec/rubocop/cop/avoid_return_from_blocks_spec.rb b/spec/rubocop/cop/avoid_return_from_blocks_spec.rb
index 526a362447f..86098f1afcc 100644
--- a/spec/rubocop/cop/avoid_return_from_blocks_spec.rb
+++ b/spec/rubocop/cop/avoid_return_from_blocks_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/avoid_return_from_blocks'
RSpec.describe RuboCop::Cop::AvoidReturnFromBlocks do
diff --git a/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb b/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb
index c049528523e..61d6f45b5ba 100644
--- a/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb
+++ b/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/avoid_route_redirect_leading_slash'
RSpec.describe RuboCop::Cop::AvoidRouteRedirectLeadingSlash do
diff --git a/spec/rubocop/cop/ban_catch_throw_spec.rb b/spec/rubocop/cop/ban_catch_throw_spec.rb
index b3c4ad8688c..f255d27e7c7 100644
--- a/spec/rubocop/cop/ban_catch_throw_spec.rb
+++ b/spec/rubocop/cop/ban_catch_throw_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/ban_catch_throw'
diff --git a/spec/rubocop/cop/code_reuse/finder_spec.rb b/spec/rubocop/cop/code_reuse/finder_spec.rb
index 484a1549a89..36f44ca79da 100644
--- a/spec/rubocop/cop/code_reuse/finder_spec.rb
+++ b/spec/rubocop/cop/code_reuse/finder_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/finder'
RSpec.describe RuboCop::Cop::CodeReuse::Finder do
diff --git a/spec/rubocop/cop/code_reuse/presenter_spec.rb b/spec/rubocop/cop/code_reuse/presenter_spec.rb
index 4639854588e..070a7ed760c 100644
--- a/spec/rubocop/cop/code_reuse/presenter_spec.rb
+++ b/spec/rubocop/cop/code_reuse/presenter_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/presenter'
RSpec.describe RuboCop::Cop::CodeReuse::Presenter do
diff --git a/spec/rubocop/cop/code_reuse/serializer_spec.rb b/spec/rubocop/cop/code_reuse/serializer_spec.rb
index 84db2e62b41..d5577caa2b4 100644
--- a/spec/rubocop/cop/code_reuse/serializer_spec.rb
+++ b/spec/rubocop/cop/code_reuse/serializer_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/serializer'
RSpec.describe RuboCop::Cop::CodeReuse::Serializer do
diff --git a/spec/rubocop/cop/code_reuse/service_class_spec.rb b/spec/rubocop/cop/code_reuse/service_class_spec.rb
index b6d94dd749f..353225b2c42 100644
--- a/spec/rubocop/cop/code_reuse/service_class_spec.rb
+++ b/spec/rubocop/cop/code_reuse/service_class_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/service_class'
RSpec.describe RuboCop::Cop::CodeReuse::ServiceClass do
diff --git a/spec/rubocop/cop/code_reuse/worker_spec.rb b/spec/rubocop/cop/code_reuse/worker_spec.rb
index 42c9303a93b..8155791a3e3 100644
--- a/spec/rubocop/cop/code_reuse/worker_spec.rb
+++ b/spec/rubocop/cop/code_reuse/worker_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/code_reuse/worker'
RSpec.describe RuboCop::Cop::CodeReuse::Worker do
diff --git a/spec/rubocop/cop/default_scope_spec.rb b/spec/rubocop/cop/default_scope_spec.rb
index 506843e030e..4fac0d465e0 100644
--- a/spec/rubocop/cop/default_scope_spec.rb
+++ b/spec/rubocop/cop/default_scope_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/default_scope'
RSpec.describe RuboCop::Cop::DefaultScope do
diff --git a/spec/rubocop/cop/destroy_all_spec.rb b/spec/rubocop/cop/destroy_all_spec.rb
index f6850a00238..468b10c3816 100644
--- a/spec/rubocop/cop/destroy_all_spec.rb
+++ b/spec/rubocop/cop/destroy_all_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/destroy_all'
RSpec.describe RuboCop::Cop::DestroyAll do
diff --git a/spec/rubocop/cop/filename_length_spec.rb b/spec/rubocop/cop/filename_length_spec.rb
index 2411c8dbc7b..ee128cb2781 100644
--- a/spec/rubocop/cop/filename_length_spec.rb
+++ b/spec/rubocop/cop/filename_length_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../rubocop/cop/filename_length'
diff --git a/spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb b/spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb
index f96e25c59e7..6d69eb5456f 100644
--- a/spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb
+++ b/spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/avoid_uploaded_file_from_params'
RSpec.describe RuboCop::Cop::Gitlab::AvoidUploadedFileFromParams do
diff --git a/spec/rubocop/cop/gitlab/bulk_insert_spec.rb b/spec/rubocop/cop/gitlab/bulk_insert_spec.rb
index c280ab8fa8b..7c60518f890 100644
--- a/spec/rubocop/cop/gitlab/bulk_insert_spec.rb
+++ b/spec/rubocop/cop/gitlab/bulk_insert_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/bulk_insert'
RSpec.describe RuboCop::Cop::Gitlab::BulkInsert do
diff --git a/spec/rubocop/cop/gitlab/change_timezone_spec.rb b/spec/rubocop/cop/gitlab/change_timezone_spec.rb
index 9cb822ec4f2..f3c07e44cc7 100644
--- a/spec/rubocop/cop/gitlab/change_timezone_spec.rb
+++ b/spec/rubocop/cop/gitlab/change_timezone_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/change_timzone'
RSpec.describe RuboCop::Cop::Gitlab::ChangeTimezone do
diff --git a/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb b/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb
index 19e5fe946be..1d99ec93e25 100644
--- a/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb
+++ b/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/const_get_inherit_false'
RSpec.describe RuboCop::Cop::Gitlab::ConstGetInheritFalse do
diff --git a/spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb b/spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb
index a207155f432..3b3d5b01a30 100644
--- a/spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb
+++ b/spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/duplicate_spec_location'
diff --git a/spec/rubocop/cop/gitlab/except_spec.rb b/spec/rubocop/cop/gitlab/except_spec.rb
index 7a122e3cf53..04cfe261cf2 100644
--- a/spec/rubocop/cop/gitlab/except_spec.rb
+++ b/spec/rubocop/cop/gitlab/except_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/except'
RSpec.describe RuboCop::Cop::Gitlab::Except do
diff --git a/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb b/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb
index 03d7fc5e8b1..d2cd06d77c5 100644
--- a/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb
+++ b/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/finder_with_find_by'
diff --git a/spec/rubocop/cop/gitlab/httparty_spec.rb b/spec/rubocop/cop/gitlab/httparty_spec.rb
index fcd18b0eb9b..98b1aa36586 100644
--- a/spec/rubocop/cop/gitlab/httparty_spec.rb
+++ b/spec/rubocop/cop/gitlab/httparty_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/httparty'
RSpec.describe RuboCop::Cop::Gitlab::HTTParty do # rubocop:disable RSpec/FilePath
diff --git a/spec/rubocop/cop/gitlab/intersect_spec.rb b/spec/rubocop/cop/gitlab/intersect_spec.rb
index 6f0367591cd..f3cb1412f35 100644
--- a/spec/rubocop/cop/gitlab/intersect_spec.rb
+++ b/spec/rubocop/cop/gitlab/intersect_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/intersect'
RSpec.describe RuboCop::Cop::Gitlab::Intersect do
diff --git a/spec/rubocop/cop/gitlab/json_spec.rb b/spec/rubocop/cop/gitlab/json_spec.rb
index 29c3b96cc1a..66b2c675e80 100644
--- a/spec/rubocop/cop/gitlab/json_spec.rb
+++ b/spec/rubocop/cop/gitlab/json_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/json'
RSpec.describe RuboCop::Cop::Gitlab::Json do
diff --git a/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb b/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb
index 08634d5753a..d46dec3b2e3 100644
--- a/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb
+++ b/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/module_with_instance_variables'
RSpec.describe RuboCop::Cop::Gitlab::ModuleWithInstanceVariables do
diff --git a/spec/rubocop/cop/gitlab/namespaced_class_spec.rb b/spec/rubocop/cop/gitlab/namespaced_class_spec.rb
index d1f61aa5afb..824a1b8cef5 100644
--- a/spec/rubocop/cop/gitlab/namespaced_class_spec.rb
+++ b/spec/rubocop/cop/gitlab/namespaced_class_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/gitlab/namespaced_class'
diff --git a/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb b/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb
index 6dbbcdd8324..f73fc71b601 100644
--- a/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb
+++ b/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/policy_rule_boolean'
RSpec.describe RuboCop::Cop::Gitlab::PolicyRuleBoolean do
diff --git a/spec/rubocop/cop/gitlab/predicate_memoization_spec.rb b/spec/rubocop/cop/gitlab/predicate_memoization_spec.rb
index 071ddcf8b7d..903c02ba194 100644
--- a/spec/rubocop/cop/gitlab/predicate_memoization_spec.rb
+++ b/spec/rubocop/cop/gitlab/predicate_memoization_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/predicate_memoization'
RSpec.describe RuboCop::Cop::Gitlab::PredicateMemoization do
diff --git a/spec/rubocop/cop/gitlab/rails_logger_spec.rb b/spec/rubocop/cop/gitlab/rails_logger_spec.rb
index 7258b047191..24f49bf3044 100644
--- a/spec/rubocop/cop/gitlab/rails_logger_spec.rb
+++ b/spec/rubocop/cop/gitlab/rails_logger_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/rails_logger'
RSpec.describe RuboCop::Cop::Gitlab::RailsLogger do
diff --git a/spec/rubocop/cop/gitlab/union_spec.rb b/spec/rubocop/cop/gitlab/union_spec.rb
index 04a3db8e7dd..ce84c75338d 100644
--- a/spec/rubocop/cop/gitlab/union_spec.rb
+++ b/spec/rubocop/cop/gitlab/union_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/gitlab/union'
RSpec.describe RuboCop::Cop::Gitlab::Union do
diff --git a/spec/rubocop/cop/graphql/authorize_types_spec.rb b/spec/rubocop/cop/graphql/authorize_types_spec.rb
index cd376bf458c..6c521789e34 100644
--- a/spec/rubocop/cop/graphql/authorize_types_spec.rb
+++ b/spec/rubocop/cop/graphql/authorize_types_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/graphql/authorize_types'
diff --git a/spec/rubocop/cop/graphql/descriptions_spec.rb b/spec/rubocop/cop/graphql/descriptions_spec.rb
index 276e4e03c15..af660aee165 100644
--- a/spec/rubocop/cop/graphql/descriptions_spec.rb
+++ b/spec/rubocop/cop/graphql/descriptions_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/graphql/descriptions'
RSpec.describe RuboCop::Cop::Graphql::Descriptions do
diff --git a/spec/rubocop/cop/graphql/gid_expected_type_spec.rb b/spec/rubocop/cop/graphql/gid_expected_type_spec.rb
index d9a129244d6..47a6ce24d53 100644
--- a/spec/rubocop/cop/graphql/gid_expected_type_spec.rb
+++ b/spec/rubocop/cop/graphql/gid_expected_type_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/graphql/gid_expected_type'
diff --git a/spec/rubocop/cop/graphql/id_type_spec.rb b/spec/rubocop/cop/graphql/id_type_spec.rb
index 93c01cd7f06..a566488b118 100644
--- a/spec/rubocop/cop/graphql/id_type_spec.rb
+++ b/spec/rubocop/cop/graphql/id_type_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/graphql/id_type'
diff --git a/spec/rubocop/cop/graphql/json_type_spec.rb b/spec/rubocop/cop/graphql/json_type_spec.rb
index 91838c1708e..50437953c1d 100644
--- a/spec/rubocop/cop/graphql/json_type_spec.rb
+++ b/spec/rubocop/cop/graphql/json_type_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/graphql/json_type'
RSpec.describe RuboCop::Cop::Graphql::JSONType do
diff --git a/spec/rubocop/cop/graphql/resolver_type_spec.rb b/spec/rubocop/cop/graphql/resolver_type_spec.rb
index 11c0ad284a9..06bf90a8a07 100644
--- a/spec/rubocop/cop/graphql/resolver_type_spec.rb
+++ b/spec/rubocop/cop/graphql/resolver_type_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/graphql/resolver_type'
diff --git a/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb b/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
index b3ec426dc07..2348552f9e4 100644
--- a/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
+++ b/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/group_public_or_visible_to_user'
RSpec.describe RuboCop::Cop::GroupPublicOrVisibleToUser do
diff --git a/spec/rubocop/cop/ignored_columns_spec.rb b/spec/rubocop/cop/ignored_columns_spec.rb
index 37fec7cb62a..1c72fedbf31 100644
--- a/spec/rubocop/cop/ignored_columns_spec.rb
+++ b/spec/rubocop/cop/ignored_columns_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/ignored_columns'
RSpec.describe RuboCop::Cop::IgnoredColumns do
diff --git a/spec/rubocop/cop/include_sidekiq_worker_spec.rb b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
index bdd622d4894..8c706925ab9 100644
--- a/spec/rubocop/cop/include_sidekiq_worker_spec.rb
+++ b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/include_sidekiq_worker'
RSpec.describe RuboCop::Cop::IncludeSidekiqWorker do
diff --git a/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb b/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb
index 2d293fd0a05..8bfa57031d7 100644
--- a/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb
+++ b/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/inject_enterprise_edition_module'
RSpec.describe RuboCop::Cop::InjectEnterpriseEditionModule do
diff --git a/spec/rubocop/cop/lint/last_keyword_argument_spec.rb b/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
index aac59f0db4c..b1b4c88e0f6 100644
--- a/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
+++ b/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/lint/last_keyword_argument'
RSpec.describe RuboCop::Cop::Lint::LastKeywordArgument do
diff --git a/spec/rubocop/cop/migration/add_column_with_default_spec.rb b/spec/rubocop/cop/migration/add_column_with_default_spec.rb
index 68f219e01f9..3f47613280f 100644
--- a/spec/rubocop/cop/migration/add_column_with_default_spec.rb
+++ b/spec/rubocop/cop/migration/add_column_with_default_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_column_with_default'
RSpec.describe RuboCop::Cop::Migration::AddColumnWithDefault do
diff --git a/spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb b/spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb
index 70d9a577728..b78ec971245 100644
--- a/spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb
+++ b/spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_columns_to_wide_tables'
RSpec.describe RuboCop::Cop::Migration::AddColumnsToWideTables do
diff --git a/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb b/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
index 2da29606024..572c0d414b3 100644
--- a/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
+++ b/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_concurrent_foreign_key'
RSpec.describe RuboCop::Cop::Migration::AddConcurrentForeignKey do
diff --git a/spec/rubocop/cop/migration/add_concurrent_index_spec.rb b/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
index bc5fd2de4fd..52b3a5769ff 100644
--- a/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
+++ b/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_concurrent_index'
RSpec.describe RuboCop::Cop::Migration::AddConcurrentIndex do
diff --git a/spec/rubocop/cop/migration/add_index_spec.rb b/spec/rubocop/cop/migration/add_index_spec.rb
index 894fbc25eb8..088bfe434f4 100644
--- a/spec/rubocop/cop/migration/add_index_spec.rb
+++ b/spec/rubocop/cop/migration/add_index_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_index'
RSpec.describe RuboCop::Cop::Migration::AddIndex do
diff --git a/spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb b/spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb
index 4f107f7600f..f4695ff8d2d 100644
--- a/spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb
+++ b/spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_limit_to_text_columns'
RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
diff --git a/spec/rubocop/cop/migration/add_reference_spec.rb b/spec/rubocop/cop/migration/add_reference_spec.rb
index 35386f14e26..9445780e9ed 100644
--- a/spec/rubocop/cop/migration/add_reference_spec.rb
+++ b/spec/rubocop/cop/migration/add_reference_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_reference'
RSpec.describe RuboCop::Cop::Migration::AddReference do
diff --git a/spec/rubocop/cop/migration/add_timestamps_spec.rb b/spec/rubocop/cop/migration/add_timestamps_spec.rb
index 6492c93d4e6..ef5a856722f 100644
--- a/spec/rubocop/cop/migration/add_timestamps_spec.rb
+++ b/spec/rubocop/cop/migration/add_timestamps_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/add_timestamps'
RSpec.describe RuboCop::Cop::Migration::AddTimestamps do
diff --git a/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb b/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb
index 0a95bd5d78c..15e947a1e53 100644
--- a/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb
+++ b/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
#
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/complex_indexes_require_name'
RSpec.describe RuboCop::Cop::Migration::ComplexIndexesRequireName do
diff --git a/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb b/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb
index d046fd913d4..7bcaf36b014 100644
--- a/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb
+++ b/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/create_table_with_foreign_keys'
RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys do
diff --git a/spec/rubocop/cop/migration/datetime_spec.rb b/spec/rubocop/cop/migration/datetime_spec.rb
index 24be3bea8c3..3854ddfe99c 100644
--- a/spec/rubocop/cop/migration/datetime_spec.rb
+++ b/spec/rubocop/cop/migration/datetime_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/datetime'
RSpec.describe RuboCop::Cop::Migration::Datetime do
diff --git a/spec/rubocop/cop/migration/drop_table_spec.rb b/spec/rubocop/cop/migration/drop_table_spec.rb
index 7a2e7929f11..f1bd710f5e6 100644
--- a/spec/rubocop/cop/migration/drop_table_spec.rb
+++ b/spec/rubocop/cop/migration/drop_table_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/drop_table'
RSpec.describe RuboCop::Cop::Migration::DropTable do
diff --git a/spec/rubocop/cop/migration/hash_index_spec.rb b/spec/rubocop/cop/migration/hash_index_spec.rb
index 08c803ee79e..6da27af39b6 100644
--- a/spec/rubocop/cop/migration/hash_index_spec.rb
+++ b/spec/rubocop/cop/migration/hash_index_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/hash_index'
RSpec.describe RuboCop::Cop::Migration::HashIndex do
diff --git a/spec/rubocop/cop/migration/prevent_strings_spec.rb b/spec/rubocop/cop/migration/prevent_strings_spec.rb
index d8d4058363e..a9b62f23a77 100644
--- a/spec/rubocop/cop/migration/prevent_strings_spec.rb
+++ b/spec/rubocop/cop/migration/prevent_strings_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/prevent_strings'
RSpec.describe RuboCop::Cop::Migration::PreventStrings do
diff --git a/spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb b/spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb
index 864d2325d53..b3e66492d83 100644
--- a/spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb
+++ b/spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
#
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/refer_to_index_by_name'
RSpec.describe RuboCop::Cop::Migration::ReferToIndexByName do
diff --git a/spec/rubocop/cop/migration/remove_column_spec.rb b/spec/rubocop/cop/migration/remove_column_spec.rb
index a8a828bc62a..f72a5b048d5 100644
--- a/spec/rubocop/cop/migration/remove_column_spec.rb
+++ b/spec/rubocop/cop/migration/remove_column_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/remove_column'
RSpec.describe RuboCop::Cop::Migration::RemoveColumn do
diff --git a/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb b/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
index 8e29e51992c..10ca0353b0f 100644
--- a/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
+++ b/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/remove_concurrent_index'
RSpec.describe RuboCop::Cop::Migration::RemoveConcurrentIndex do
diff --git a/spec/rubocop/cop/migration/remove_index_spec.rb b/spec/rubocop/cop/migration/remove_index_spec.rb
index c9797443276..5d1ffef2589 100644
--- a/spec/rubocop/cop/migration/remove_index_spec.rb
+++ b/spec/rubocop/cop/migration/remove_index_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/remove_index'
RSpec.describe RuboCop::Cop::Migration::RemoveIndex do
diff --git a/spec/rubocop/cop/migration/safer_boolean_column_spec.rb b/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
index 11661be2bb6..cf9bdbeef91 100644
--- a/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
+++ b/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/safer_boolean_column'
RSpec.describe RuboCop::Cop::Migration::SaferBooleanColumn do
diff --git a/spec/rubocop/cop/migration/schedule_async_spec.rb b/spec/rubocop/cop/migration/schedule_async_spec.rb
index 008a23c7b8f..b89acb6db41 100644
--- a/spec/rubocop/cop/migration/schedule_async_spec.rb
+++ b/spec/rubocop/cop/migration/schedule_async_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/schedule_async'
RSpec.describe RuboCop::Cop::Migration::ScheduleAsync do
diff --git a/spec/rubocop/cop/migration/timestamps_spec.rb b/spec/rubocop/cop/migration/timestamps_spec.rb
index 4da3dffbfc6..91bb5c1b05b 100644
--- a/spec/rubocop/cop/migration/timestamps_spec.rb
+++ b/spec/rubocop/cop/migration/timestamps_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/timestamps'
RSpec.describe RuboCop::Cop::Migration::Timestamps do
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 5e33e11b13f..a12ae94c22b 100644
--- a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
+++ b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/update_column_in_batches'
RSpec.describe RuboCop::Cop::Migration::UpdateColumnInBatches do
diff --git a/spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb b/spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb
index 429490294be..298ca273256 100644
--- a/spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb
+++ b/spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/with_lock_retries_disallowed_method'
RSpec.describe RuboCop::Cop::Migration::WithLockRetriesDisallowedMethod do
diff --git a/spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb b/spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb
index cf2d46d52c9..f2e84a8697c 100644
--- a/spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb
+++ b/spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/migration/with_lock_retries_with_change'
RSpec.describe RuboCop::Cop::Migration::WithLockRetriesWithChange do
diff --git a/spec/rubocop/cop/performance/ar_count_each_spec.rb b/spec/rubocop/cop/performance/ar_count_each_spec.rb
index 402e3e93147..fa7a1aba426 100644
--- a/spec/rubocop/cop/performance/ar_count_each_spec.rb
+++ b/spec/rubocop/cop/performance/ar_count_each_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/performance/ar_count_each.rb'
RSpec.describe RuboCop::Cop::Performance::ARCountEach do
diff --git a/spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb b/spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb
index 8497ff0e909..127c858a549 100644
--- a/spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb
+++ b/spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/performance/ar_exists_and_present_blank.rb'
RSpec.describe RuboCop::Cop::Performance::ARExistsAndPresentBlank do
diff --git a/spec/rubocop/cop/performance/readlines_each_spec.rb b/spec/rubocop/cop/performance/readlines_each_spec.rb
index 5a30107722a..0a8b168ce5d 100644
--- a/spec/rubocop/cop/performance/readlines_each_spec.rb
+++ b/spec/rubocop/cop/performance/readlines_each_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/performance/readlines_each'
RSpec.describe RuboCop::Cop::Performance::ReadlinesEach do
diff --git a/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb b/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb
index 5a557ec26d6..1261ca7891c 100644
--- a/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb
+++ b/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/prefer_class_methods_over_module'
RSpec.describe RuboCop::Cop::PreferClassMethodsOverModule do
diff --git a/spec/rubocop/cop/project_path_helper_spec.rb b/spec/rubocop/cop/project_path_helper_spec.rb
index 16782802a27..b3c920f9d25 100644
--- a/spec/rubocop/cop/project_path_helper_spec.rb
+++ b/spec/rubocop/cop/project_path_helper_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/project_path_helper'
RSpec.describe RuboCop::Cop::ProjectPathHelper do
diff --git a/spec/rubocop/cop/put_group_routes_under_scope_spec.rb b/spec/rubocop/cop/put_group_routes_under_scope_spec.rb
index f9e64d4d68f..366fc4b5657 100644
--- a/spec/rubocop/cop/put_group_routes_under_scope_spec.rb
+++ b/spec/rubocop/cop/put_group_routes_under_scope_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/put_group_routes_under_scope'
RSpec.describe RuboCop::Cop::PutGroupRoutesUnderScope do
diff --git a/spec/rubocop/cop/put_project_routes_under_scope_spec.rb b/spec/rubocop/cop/put_project_routes_under_scope_spec.rb
index eb783d22129..9d226db09ef 100644
--- a/spec/rubocop/cop/put_project_routes_under_scope_spec.rb
+++ b/spec/rubocop/cop/put_project_routes_under_scope_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/put_project_routes_under_scope'
RSpec.describe RuboCop::Cop::PutProjectRoutesUnderScope do
diff --git a/spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb b/spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb
index 9332ab4186e..9335b8d01ee 100644
--- a/spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb
+++ b/spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/qa/ambiguous_page_object_name'
diff --git a/spec/rubocop/cop/qa/element_with_pattern_spec.rb b/spec/rubocop/cop/qa/element_with_pattern_spec.rb
index 28c351ccf1e..d3e79525c62 100644
--- a/spec/rubocop/cop/qa/element_with_pattern_spec.rb
+++ b/spec/rubocop/cop/qa/element_with_pattern_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/qa/element_with_pattern'
diff --git a/spec/rubocop/cop/rspec/be_success_matcher_spec.rb b/spec/rubocop/cop/rspec/be_success_matcher_spec.rb
index 050f0396fac..678e62048b8 100644
--- a/spec/rubocop/cop/rspec/be_success_matcher_spec.rb
+++ b/spec/rubocop/cop/rspec/be_success_matcher_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/be_success_matcher'
RSpec.describe RuboCop::Cop::RSpec::BeSuccessMatcher do
diff --git a/spec/rubocop/cop/rspec/env_assignment_spec.rb b/spec/rubocop/cop/rspec/env_assignment_spec.rb
index cc132d1532a..da6bb2fa2fb 100644
--- a/spec/rubocop/cop/rspec/env_assignment_spec.rb
+++ b/spec/rubocop/cop/rspec/env_assignment_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/env_assignment'
RSpec.describe RuboCop::Cop::RSpec::EnvAssignment do
diff --git a/spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb b/spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb
index d1ce8d01e0b..e36feecdd66 100644
--- a/spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb
+++ b/spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/expect_gitlab_tracking'
RSpec.describe RuboCop::Cop::RSpec::ExpectGitlabTracking do
diff --git a/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb b/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb
index 8beec53375e..74c1521fa0e 100644
--- a/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb
+++ b/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/factories_in_migration_specs'
RSpec.describe RuboCop::Cop::RSpec::FactoriesInMigrationSpecs do
diff --git a/spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb b/spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb
index 0e6af71ea3e..194e2436ff2 100644
--- a/spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb
+++ b/spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
require 'rspec-parameterized'
-require 'rubocop'
require_relative '../../../../../rubocop/cop/rspec/factory_bot/inline_association'
diff --git a/spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb b/spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb
index c2d97c8992a..9bdbe145f4c 100644
--- a/spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb
+++ b/spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb
@@ -3,7 +3,6 @@
require 'fast_spec_helper'
require 'rspec-parameterized'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/have_gitlab_http_status'
RSpec.describe RuboCop::Cop::RSpec::HaveGitlabHttpStatus do
diff --git a/spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb b/spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb
index ffabbae90dc..7a2b7c92bd1 100644
--- a/spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb
+++ b/spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/modify_sidekiq_middleware'
RSpec.describe RuboCop::Cop::RSpec::ModifySidekiqMiddleware do
diff --git a/spec/rubocop/cop/rspec/timecop_freeze_spec.rb b/spec/rubocop/cop/rspec/timecop_freeze_spec.rb
index 939623f8299..b8d16d58d9e 100644
--- a/spec/rubocop/cop/rspec/timecop_freeze_spec.rb
+++ b/spec/rubocop/cop/rspec/timecop_freeze_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/timecop_freeze'
RSpec.describe RuboCop::Cop::RSpec::TimecopFreeze do
diff --git a/spec/rubocop/cop/rspec/timecop_travel_spec.rb b/spec/rubocop/cop/rspec/timecop_travel_spec.rb
index 476e45e69a6..16e09fb8c45 100644
--- a/spec/rubocop/cop/rspec/timecop_travel_spec.rb
+++ b/spec/rubocop/cop/rspec/timecop_travel_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/timecop_travel'
RSpec.describe RuboCop::Cop::RSpec::TimecopTravel do
diff --git a/spec/rubocop/cop/rspec/top_level_describe_path_spec.rb b/spec/rubocop/cop/rspec/top_level_describe_path_spec.rb
index 23531cd0201..78e6bec51d4 100644
--- a/spec/rubocop/cop/rspec/top_level_describe_path_spec.rb
+++ b/spec/rubocop/cop/rspec/top_level_describe_path_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/rspec/top_level_describe_path'
RSpec.describe RuboCop::Cop::RSpec::TopLevelDescribePath do
diff --git a/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
index cacf0a1b67d..c21999be917 100644
--- a/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
+++ b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/ruby_interpolation_in_translation'
diff --git a/spec/rubocop/cop/safe_params_spec.rb b/spec/rubocop/cop/safe_params_spec.rb
index 62f8e542d86..9a064b93b16 100644
--- a/spec/rubocop/cop/safe_params_spec.rb
+++ b/spec/rubocop/cop/safe_params_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/safe_params'
RSpec.describe RuboCop::Cop::SafeParams do
diff --git a/spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb b/spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb
index a19ddf9dbe6..01afaf3acb6 100644
--- a/spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb
+++ b/spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/scalability/bulk_perform_with_context'
RSpec.describe RuboCop::Cop::Scalability::BulkPerformWithContext do
diff --git a/spec/rubocop/cop/scalability/cron_worker_context_spec.rb b/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
index 11b2b82d2f5..28db12fd075 100644
--- a/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
+++ b/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/scalability/cron_worker_context'
RSpec.describe RuboCop::Cop::Scalability::CronWorkerContext do
diff --git a/spec/rubocop/cop/scalability/file_uploads_spec.rb b/spec/rubocop/cop/scalability/file_uploads_spec.rb
index bda5c056b03..ca25b0246f0 100644
--- a/spec/rubocop/cop/scalability/file_uploads_spec.rb
+++ b/spec/rubocop/cop/scalability/file_uploads_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/scalability/file_uploads'
RSpec.describe RuboCop::Cop::Scalability::FileUploads do
diff --git a/spec/rubocop/cop/scalability/idempotent_worker_spec.rb b/spec/rubocop/cop/scalability/idempotent_worker_spec.rb
index 729f2613697..53c0c06f6c9 100644
--- a/spec/rubocop/cop/scalability/idempotent_worker_spec.rb
+++ b/spec/rubocop/cop/scalability/idempotent_worker_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/scalability/idempotent_worker'
RSpec.describe RuboCop::Cop::Scalability::IdempotentWorker do
diff --git a/spec/rubocop/cop/sidekiq_options_queue_spec.rb b/spec/rubocop/cop/sidekiq_options_queue_spec.rb
index e65118ca192..346a8d82475 100644
--- a/spec/rubocop/cop/sidekiq_options_queue_spec.rb
+++ b/spec/rubocop/cop/sidekiq_options_queue_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../rubocop/cop/sidekiq_options_queue'
RSpec.describe RuboCop::Cop::SidekiqOptionsQueue do
diff --git a/spec/rubocop/cop/static_translation_definition_spec.rb b/spec/rubocop/cop/static_translation_definition_spec.rb
index 8656b07a6e4..b2b04cbcbde 100644
--- a/spec/rubocop/cop/static_translation_definition_spec.rb
+++ b/spec/rubocop/cop/static_translation_definition_spec.rb
@@ -2,7 +2,6 @@
require 'fast_spec_helper'
-require 'rubocop'
require 'rspec-parameterized'
require_relative '../../../rubocop/cop/static_translation_definition'
diff --git a/spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb b/spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb
index b6711effe9e..f377dfe36d8 100644
--- a/spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb
+++ b/spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/usage_data/distinct_count_by_large_foreign_key'
diff --git a/spec/rubocop/cop/usage_data/large_table_spec.rb b/spec/rubocop/cop/usage_data/large_table_spec.rb
index 26bd4e61625..a6b22fd7f0d 100644
--- a/spec/rubocop/cop/usage_data/large_table_spec.rb
+++ b/spec/rubocop/cop/usage_data/large_table_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require_relative '../../../../rubocop/cop/usage_data/large_table'
diff --git a/spec/rubocop/migration_helpers_spec.rb b/spec/rubocop/migration_helpers_spec.rb
index f0be21c9d70..997d4071c29 100644
--- a/spec/rubocop/migration_helpers_spec.rb
+++ b/spec/rubocop/migration_helpers_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require 'rspec-parameterized'
require_relative '../../rubocop/migration_helpers'
diff --git a/spec/rubocop/qa_helpers_spec.rb b/spec/rubocop/qa_helpers_spec.rb
index cf6d2f1a845..4b5566609e3 100644
--- a/spec/rubocop/qa_helpers_spec.rb
+++ b/spec/rubocop/qa_helpers_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'rubocop'
require 'parser/current'
require_relative '../../rubocop/qa_helpers'
diff --git a/workhorse/CHANGELOG b/workhorse/CHANGELOG
index 21d5bc727c0..45d793d8b3f 100644
--- a/workhorse/CHANGELOG
+++ b/workhorse/CHANGELOG
@@ -1,5 +1,17 @@
# Changelog for gitlab-workhorse
+## v8.64.2
+
+### Security
+- Stop logging when path is excluded
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+## v8.64.1
+
+### Security
+- Use URL.EscapePath() in upstream router
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
## v8.64.0
### Other
diff --git a/workhorse/VERSION b/workhorse/VERSION
index ca1596d1a61..e0ef5b73743 100644
--- a/workhorse/VERSION
+++ b/workhorse/VERSION
@@ -1 +1 @@
-8.64.0
+8.64.2
diff --git a/workhorse/internal/staticpages/deploy_page_test.go b/workhorse/internal/staticpages/deploy_page_test.go
index 4b081e73a97..9bc0a082144 100644
--- a/workhorse/internal/staticpages/deploy_page_test.go
+++ b/workhorse/internal/staticpages/deploy_page_test.go
@@ -23,7 +23,7 @@ func TestIfNoDeployPageExist(t *testing.T) {
w := httptest.NewRecorder()
executed := false
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.DeployPage(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
executed = true
})).ServeHTTP(w, nil)
@@ -45,7 +45,7 @@ func TestIfDeployPageExist(t *testing.T) {
w := httptest.NewRecorder()
executed := false
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.DeployPage(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
executed = true
})).ServeHTTP(w, nil)
diff --git a/workhorse/internal/staticpages/error_pages_test.go b/workhorse/internal/staticpages/error_pages_test.go
index 05ec06cd429..ab29e187c8e 100644
--- a/workhorse/internal/staticpages/error_pages_test.go
+++ b/workhorse/internal/staticpages/error_pages_test.go
@@ -32,7 +32,7 @@ func TestIfErrorPageIsPresented(t *testing.T) {
require.NoError(t, err)
require.Equal(t, len(upstreamBody), n, "bytes written")
})
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ErrorPagesUnless(false, ErrorFormatHTML, h).ServeHTTP(w, nil)
w.Flush()
@@ -54,7 +54,7 @@ func TestIfErrorPassedIfNoErrorPageIsFound(t *testing.T) {
w.WriteHeader(404)
fmt.Fprint(w, errorResponse)
})
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ErrorPagesUnless(false, ErrorFormatHTML, h).ServeHTTP(w, nil)
w.Flush()
@@ -78,7 +78,7 @@ func TestIfErrorPageIsIgnoredInDevelopment(t *testing.T) {
w.WriteHeader(500)
fmt.Fprint(w, serverError)
})
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ErrorPagesUnless(true, ErrorFormatHTML, h).ServeHTTP(w, nil)
w.Flush()
require.Equal(t, 500, w.Code)
@@ -102,7 +102,7 @@ func TestIfErrorPageIsIgnoredIfCustomError(t *testing.T) {
w.WriteHeader(500)
fmt.Fprint(w, serverError)
})
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ErrorPagesUnless(false, ErrorFormatHTML, h).ServeHTTP(w, nil)
w.Flush()
require.Equal(t, 500, w.Code)
@@ -137,7 +137,7 @@ func TestErrorPageInterceptedByContentType(t *testing.T) {
w.WriteHeader(500)
fmt.Fprint(w, serverError)
})
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ErrorPagesUnless(false, ErrorFormatHTML, h).ServeHTTP(w, nil)
w.Flush()
require.Equal(t, 500, w.Code)
@@ -161,7 +161,7 @@ func TestIfErrorPageIsPresentedJSON(t *testing.T) {
require.NoError(t, err)
require.Equal(t, len(upstreamBody), n, "bytes written")
})
- st := &Static{""}
+ st := &Static{}
st.ErrorPagesUnless(false, ErrorFormatJSON, h).ServeHTTP(w, nil)
w.Flush()
@@ -181,7 +181,7 @@ func TestIfErrorPageIsPresentedText(t *testing.T) {
require.NoError(t, err)
require.Equal(t, len(upstreamBody), n, "bytes written")
})
- st := &Static{""}
+ st := &Static{}
st.ErrorPagesUnless(false, ErrorFormatText, h).ServeHTTP(w, nil)
w.Flush()
diff --git a/workhorse/internal/staticpages/servefile.go b/workhorse/internal/staticpages/servefile.go
index c98bc030bc2..7e39f919fa6 100644
--- a/workhorse/internal/staticpages/servefile.go
+++ b/workhorse/internal/staticpages/servefile.go
@@ -1,16 +1,18 @@
package staticpages
import (
+ "errors"
+ "fmt"
"net/http"
"os"
"path/filepath"
"strings"
"time"
- "gitlab.com/gitlab-org/labkit/log"
"gitlab.com/gitlab-org/labkit/mask"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/log"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/urlprefix"
)
@@ -26,21 +28,28 @@ const (
// upstream.
func (s *Static) ServeExisting(prefix urlprefix.Prefix, cache CacheMode, notFoundHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- file := filepath.Join(s.DocumentRoot, prefix.Strip(r.URL.Path))
+ if notFoundHandler == nil {
+ notFoundHandler = http.HandlerFunc(http.NotFound)
+ }
+
+ // We intentionally use r.URL.Path instead of r.URL.EscaptedPath() below.
+ // This is to make it possible to serve static files with e.g. a space
+ // %20 in their name.
+ relativePath, err := s.validatePath(prefix.Strip(r.URL.Path))
+ if err != nil {
+ notFoundHandler.ServeHTTP(w, r)
+ return
+ }
- // The filepath.Join does Clean traversing directories up
+ file := filepath.Join(s.DocumentRoot, relativePath)
if !strings.HasPrefix(file, s.DocumentRoot) {
- helper.Fail500(w, r, &os.PathError{
- Op: "open",
- Path: file,
- Err: os.ErrInvalid,
- })
+ log.WithRequest(r).WithError(errPathTraversal).Error()
+ notFoundHandler.ServeHTTP(w, r)
return
}
var content *os.File
var fi os.FileInfo
- var err error
// Serve pre-gzipped assets
if acceptEncoding := r.Header.Get("Accept-Encoding"); strings.Contains(acceptEncoding, "gzip") {
@@ -55,11 +64,7 @@ func (s *Static) ServeExisting(prefix urlprefix.Prefix, cache CacheMode, notFoun
content, fi, err = helper.OpenFile(file)
}
if err != nil {
- if notFoundHandler != nil {
- notFoundHandler.ServeHTTP(w, r)
- } else {
- http.NotFound(w, r)
- }
+ notFoundHandler.ServeHTTP(w, r)
return
}
defer content.Close()
@@ -82,3 +87,17 @@ func (s *Static) ServeExisting(prefix urlprefix.Prefix, cache CacheMode, notFoun
http.ServeContent(w, r, filepath.Base(file), fi.ModTime(), content)
})
}
+
+var errPathTraversal = errors.New("path traversal")
+
+func (s *Static) validatePath(filename string) (string, error) {
+ filename = filepath.Clean(filename)
+
+ for _, exc := range s.Exclude {
+ if strings.HasPrefix(filename, exc) {
+ return "", fmt.Errorf("file is excluded: %s", exc)
+ }
+ }
+
+ return filename, nil
+}
diff --git a/workhorse/internal/staticpages/servefile_test.go b/workhorse/internal/staticpages/servefile_test.go
index e136b876298..314547b8a57 100644
--- a/workhorse/internal/staticpages/servefile_test.go
+++ b/workhorse/internal/staticpages/servefile_test.go
@@ -20,7 +20,7 @@ func TestServingNonExistingFile(t *testing.T) {
httpRequest, _ := http.NewRequest("GET", "/file", nil)
w := httptest.NewRecorder()
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
require.Equal(t, 404, w.Code)
}
@@ -34,7 +34,7 @@ func TestServingDirectory(t *testing.T) {
httpRequest, _ := http.NewRequest("GET", "/file", nil)
w := httptest.NewRecorder()
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
require.Equal(t, 404, w.Code)
}
@@ -44,7 +44,7 @@ func TestServingMalformedUri(t *testing.T) {
httpRequest, _ := http.NewRequest("GET", "/../../../static/file", nil)
w := httptest.NewRecorder()
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
require.Equal(t, 404, w.Code)
}
@@ -54,7 +54,7 @@ func TestExecutingHandlerWhenNoFileFound(t *testing.T) {
httpRequest, _ := http.NewRequest("GET", "/file", nil)
executed := false
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ServeExisting("/", CacheDisabled, http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
executed = (r == httpRequest)
})).ServeHTTP(nil, httpRequest)
@@ -76,7 +76,7 @@ func TestServingTheActualFile(t *testing.T) {
ioutil.WriteFile(filepath.Join(dir, "file"), []byte(fileContent), 0600)
w := httptest.NewRecorder()
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
require.Equal(t, 200, w.Code)
if w.Body.String() != fileContent {
@@ -84,6 +84,40 @@ func TestServingTheActualFile(t *testing.T) {
}
}
+func TestExcludedPaths(t *testing.T) {
+ testCases := []struct {
+ desc string
+ path string
+ found bool
+ contents string
+ }{
+ {"allowed file", "/file1", true, "contents1"},
+ {"path traversal is allowed", "/uploads/../file1", true, "contents1"},
+ {"files in /uploads/ are invisible", "/uploads/file2", false, ""},
+ {"cannot use path traversal to get to /uploads/", "/foobar/../uploads/file2", false, ""},
+ {"cannot use escaped path traversal to get to /uploads/", "/foobar%2f%2e%2e%2fuploads/file2", false, ""},
+ {"cannot use double escaped path traversal to get to /uploads/", "/foobar%252f%252e%252e%252fuploads/file2", false, ""},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ httpRequest, err := http.NewRequest("GET", tc.path, nil)
+ require.NoError(t, err)
+
+ w := httptest.NewRecorder()
+ st := &Static{DocumentRoot: "testdata", Exclude: []string{"/uploads/"}}
+ st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
+
+ if tc.found {
+ require.Equal(t, 200, w.Code)
+ require.Equal(t, tc.contents, w.Body.String())
+ } else {
+ require.Equal(t, 404, w.Code)
+ }
+ })
+ }
+}
+
func testServingThePregzippedFile(t *testing.T, enableGzip bool) {
dir, err := ioutil.TempDir("", "deploy")
if err != nil {
@@ -108,7 +142,7 @@ func testServingThePregzippedFile(t *testing.T, enableGzip bool) {
ioutil.WriteFile(filepath.Join(dir, "file"), []byte(fileContent), 0600)
w := httptest.NewRecorder()
- st := &Static{dir}
+ st := &Static{DocumentRoot: dir}
st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
require.Equal(t, 200, w.Code)
if enableGzip {
diff --git a/workhorse/internal/staticpages/static.go b/workhorse/internal/staticpages/static.go
index b42351f15f5..5b804e4d644 100644
--- a/workhorse/internal/staticpages/static.go
+++ b/workhorse/internal/staticpages/static.go
@@ -2,4 +2,5 @@ package staticpages
type Static struct {
DocumentRoot string
+ Exclude []string
}
diff --git a/workhorse/internal/staticpages/testdata/file1 b/workhorse/internal/staticpages/testdata/file1
new file mode 100644
index 00000000000..146edcbe0a3
--- /dev/null
+++ b/workhorse/internal/staticpages/testdata/file1
@@ -0,0 +1 @@
+contents1 \ No newline at end of file
diff --git a/workhorse/internal/staticpages/testdata/uploads/file2 b/workhorse/internal/staticpages/testdata/uploads/file2
new file mode 100644
index 00000000000..c061beb8592
--- /dev/null
+++ b/workhorse/internal/staticpages/testdata/uploads/file2
@@ -0,0 +1 @@
+contents2 \ No newline at end of file
diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go
index edcbfa88a67..fb8a07a8031 100644
--- a/workhorse/internal/upstream/routes.go
+++ b/workhorse/internal/upstream/routes.go
@@ -62,6 +62,14 @@ const (
importPattern = `^/import/`
)
+var (
+ // For legacy reasons, user uploads are stored in public/uploads. To
+ // prevent anybody who knows/guesses the URL of a user-uploaded file
+ // from downloading it we configure static.ServeExisting to treat files
+ // under public/uploads/ as if they do not exist.
+ staticExclude = []string{"/uploads/"}
+)
+
func compileRegexp(regexpStr string) *regexp.Regexp {
if len(regexpStr) == 0 {
return nil
@@ -181,20 +189,20 @@ func buildProxy(backend *url.URL, version string, rt http.RoundTripper, cfg conf
// We match against URI not containing the relativeUrlRoot:
// see upstream.ServeHTTP
-func (u *upstream) configureRoutes() {
+func configureRoutes(u *upstream) {
api := apipkg.NewAPI(
u.Backend,
u.Version,
u.RoundTripper,
)
- static := &staticpages.Static{DocumentRoot: u.DocumentRoot}
+ static := &staticpages.Static{DocumentRoot: u.DocumentRoot, Exclude: staticExclude}
proxy := buildProxy(u.Backend, u.Version, u.RoundTripper, u.Config)
cableProxy := proxypkg.NewProxy(u.CableBackend, u.Version, u.CableRoundTripper)
assetsNotFoundHandler := NotFoundUnless(u.DevelopmentMode, proxy)
if u.AltDocumentRoot != "" {
- altStatic := &staticpages.Static{DocumentRoot: u.AltDocumentRoot}
+ altStatic := &staticpages.Static{DocumentRoot: u.AltDocumentRoot, Exclude: staticExclude}
assetsNotFoundHandler = altStatic.ServeExisting(
u.URLPrefix,
staticpages.CacheExpireMax,
@@ -306,12 +314,6 @@ func (u *upstream) configureRoutes() {
u.route("POST", snippetUploadPattern, upload.Accelerate(api, signingProxy, preparers.uploads)),
u.route("POST", userUploadPattern, upload.Accelerate(api, signingProxy, preparers.uploads)),
- // For legacy reasons, user uploads are stored under the document root.
- // To prevent anybody who knows/guesses the URL of a user-uploaded file
- // from downloading it we make sure requests to /uploads/ do _not_ pass
- // through static.ServeExisting.
- u.route("", `^/uploads/`, static.ErrorPagesUnless(u.DevelopmentMode, staticpages.ErrorFormatHTML, proxy)),
-
// health checks don't intercept errors and go straight to rails
// TODO: We should probably not return a HTML deploy page?
// https://gitlab.com/gitlab-org/gitlab-workhorse/issues/230
diff --git a/workhorse/internal/upstream/upstream.go b/workhorse/internal/upstream/upstream.go
index c81a21c0ecd..80e7d4056b6 100644
--- a/workhorse/internal/upstream/upstream.go
+++ b/workhorse/internal/upstream/upstream.go
@@ -40,6 +40,10 @@ type upstream struct {
}
func NewUpstream(cfg config.Config, accessLogger *logrus.Logger) http.Handler {
+ return newUpstream(cfg, accessLogger, configureRoutes)
+}
+
+func newUpstream(cfg config.Config, accessLogger *logrus.Logger, routesCallback func(*upstream)) http.Handler {
up := upstream{
Config: cfg,
accessLogger: accessLogger,
@@ -56,7 +60,7 @@ func NewUpstream(cfg config.Config, accessLogger *logrus.Logger) http.Handler {
up.RoundTripper = roundtripper.NewBackendRoundTripper(up.Backend, up.Socket, up.ProxyHeadersTimeout, cfg.DevelopmentMode)
up.CableRoundTripper = roundtripper.NewBackendRoundTripper(up.CableBackend, up.CableSocket, up.ProxyHeadersTimeout, cfg.DevelopmentMode)
up.configureURLPrefix()
- up.configureRoutes()
+ routesCallback(&up)
var correlationOpts []correlation.InboundHandlerOption
if cfg.PropagateCorrelationID {
@@ -95,7 +99,7 @@ func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// Check URL Root
- URIPath := urlprefix.CleanURIPath(r.URL.Path)
+ URIPath := urlprefix.CleanURIPath(r.URL.EscapedPath())
prefix := u.URLPrefix
if !prefix.Match(URIPath) {
helper.HTTPError(w, r, fmt.Sprintf("Not found %q", URIPath), http.StatusNotFound)
diff --git a/workhorse/internal/upstream/upstream_test.go b/workhorse/internal/upstream/upstream_test.go
new file mode 100644
index 00000000000..3afc62a7384
--- /dev/null
+++ b/workhorse/internal/upstream/upstream_test.go
@@ -0,0 +1,67 @@
+package upstream
+
+import (
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+)
+
+func TestRouting(t *testing.T) {
+ handle := func(u *upstream, regex string) routeEntry {
+ handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ io.WriteString(w, regex)
+ })
+ return u.route("", regex, handler)
+ }
+
+ const (
+ foobar = `\A/foobar\z`
+ quxbaz = `\A/quxbaz\z`
+ main = ""
+ )
+
+ u := newUpstream(config.Config{}, logrus.StandardLogger(), func(u *upstream) {
+ u.Routes = []routeEntry{
+ handle(u, foobar),
+ handle(u, quxbaz),
+ handle(u, main),
+ }
+ })
+
+ ts := httptest.NewServer(u)
+ defer ts.Close()
+
+ testCases := []struct {
+ desc string
+ path string
+ route string
+ }{
+ {"main route works", "/", main},
+ {"foobar route works", "/foobar", foobar},
+ {"quxbaz route works", "/quxbaz", quxbaz},
+ {"path traversal works, ends up in quxbaz", "/foobar/../quxbaz", quxbaz},
+ {"escaped path traversal does not match any route", "/foobar%2f%2e%2e%2fquxbaz", main},
+ {"double escaped path traversal does not match any route", "/foobar%252f%252e%252e%252fquxbaz", main},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ resp, err := http.Get(ts.URL + tc.path)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+
+ require.Equal(t, 200, resp.StatusCode, "response code")
+ require.Equal(t, tc.route, string(body))
+ })
+ }
+}
diff --git a/workhorse/main_test.go b/workhorse/main_test.go
index b725f36a68a..4e169b5112f 100644
--- a/workhorse/main_test.go
+++ b/workhorse/main_test.go
@@ -222,12 +222,15 @@ func TestDeniedPublicUploadsFile(t *testing.T) {
for _, resource := range []string{
"/uploads/static.txt",
"/uploads%2Fstatic.txt",
+ "/foobar%2F%2E%2E%2Fuploads/static.txt",
} {
- resp, body := httpGet(t, ws.URL+resource, nil)
+ t.Run(resource, func(t *testing.T) {
+ resp, body := httpGet(t, ws.URL+resource, nil)
- require.Equal(t, 404, resp.StatusCode, "GET %q: status code", resource)
- require.Equal(t, "", body, "GET %q: response body", resource)
- require.True(t, proxied, "GET %q: never made it to backend", resource)
+ require.Equal(t, 404, resp.StatusCode, "GET %q: status code", resource)
+ require.Equal(t, "", body, "GET %q: response body", resource)
+ require.True(t, proxied, "GET %q: never made it to backend", resource)
+ })
}
}
diff --git a/yarn.lock b/yarn.lock
index 768b29ab5ba..8d1332a9c59 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11415,10 +11415,10 @@ svg-tags@^1.0.0:
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
-swagger-ui-dist@^3.32.4:
- version "3.32.4"
- resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-3.32.4.tgz#6fa920a99e38eaaf129580ac158cf730494a2190"
- integrity sha512-3qUqK131a5nqGdDJhLflTNzvrjZgjBlINYNx+Jm5lw/Va88Lcu5iyjUupY3Js/Kf326z1XtXkrr6TbvE6r925g==
+swagger-ui-dist@^3.43.0:
+ version "3.43.0"
+ resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-3.43.0.tgz#b064a2cec1d27776f9a124bc70423cfa0bbc0d3f"
+ integrity sha512-PtE+g23bNbYv8qqAVoPBqNQth8hU5Sl5ZsQ7gHXlO5jlCt31dVTiKI9ArHIT1b23ZzUYTnKsFgPYYFoiWyNCAw==
symbol-observable@^1.0.2:
version "1.2.0"