summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/issue_templates/Geo Replicate a new Git repository type.md41
-rw-r--r--.gitlab/issue_templates/Geo Replicate a new blob type.md52
-rw-r--r--.rubocop.yml3
-rw-r--r--.rubocop_todo/style/special_global_vars.yml4
-rw-r--r--GITLAB_ELASTICSEARCH_INDEXER_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--app/assets/javascripts/search/index.js10
-rw-r--r--app/assets/javascripts/search/sidebar/components/app.vue25
-rw-r--r--app/assets/javascripts/search/sidebar/index.js9
-rw-r--r--app/assets/javascripts/search/topbar/components/app.vue47
-rw-r--r--app/controllers/boards/application_controller.rb23
-rw-r--r--app/controllers/boards/issues_controller.rb163
-rw-r--r--app/controllers/boards/lists_controller.rb103
-rw-r--r--app/controllers/concerns/boards_actions.rb20
-rw-r--r--app/controllers/concerns/boards_responses.rb65
-rw-r--r--app/controllers/groups/boards_controller.rb5
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/projects/boards_controller.rb6
-rw-r--r--app/helpers/boards_helper.rb15
-rw-r--r--app/helpers/issues_helper.rb4
-rw-r--r--app/helpers/search_helper.rb17
-rw-r--r--app/models/project.rb2
-rw-r--r--app/services/boards/lists/generate_service.rb39
-rw-r--r--app/services/import/github_service.rb28
-rw-r--r--app/views/import/github/status.html.haml5
-rw-r--r--app/views/search/_results.html.haml37
-rw-r--r--app/views/search/_results_list.html.haml18
-rw-r--r--app/views/search/_results_status.html.haml26
-rw-r--r--app/views/search/_results_status_horiz_nav.html.haml22
-rw-r--r--app/views/search/_results_status_vert_nav.html.haml23
-rw-r--r--app/views/search/show.html.haml3
-rw-r--r--app/workers/concerns/gitlab/github_import/stage_methods.rb4
-rw-r--r--app/workers/gitlab/github_import/stage/import_attachments_worker.rb2
-rw-r--r--app/workers/gitlab/github_import/stage/import_issue_events_worker.rb14
-rw-r--r--app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb2
-rw-r--r--app/workers/gitlab/github_import/stage/import_notes_worker.rb2
-rw-r--r--config/feature_flags/development/search_page_vertical_nav.yml2
-rw-r--r--config/feature_flags/ops/github_importer_attachments_import.yml8
-rw-r--r--config/feature_flags/ops/github_importer_issue_events_import.yml8
-rw-r--r--config/feature_flags/ops/github_importer_single_endpoint_issue_events_import.yml8
-rw-r--r--config/feature_flags/ops/github_importer_single_endpoint_notes_import.yml8
-rw-r--r--config/routes.rb22
-rw-r--r--doc/administration/geo/replication/version_specific_upgrades.md10
-rw-r--r--doc/administration/integration/mailgun.md2
-rw-r--r--doc/api/import.md30
-rw-r--r--doc/ci/pipelines/merge_trains.md2
-rw-r--r--doc/development/code_review.md6
-rw-r--r--doc/development/database/table_partitioning.md24
-rw-r--r--doc/development/github_importer.md6
-rw-r--r--doc/user/admin_area/settings/deprecated_api_rate_limits.md2
-rw-r--r--doc/user/admin_area/settings/files_api_rate_limits.md2
-rw-r--r--doc/user/application_security/dependency_scanning/index.md76
-rw-r--r--doc/user/application_security/policies/scan-execution-policies.md31
-rw-r--r--doc/user/project/import/github.md38
-rw-r--r--doc/user/project/integrations/webhook_events.md1
-rw-r--r--lib/api/import_github.rb1
-rw-r--r--lib/gitlab/data_builder/pipeline.rb1
-rw-r--r--lib/gitlab/database/migration_helpers.rb11
-rw-r--r--lib/gitlab/github_import/issuable_finder.rb6
-rw-r--r--lib/gitlab/github_import/settings.rb72
-rw-r--r--lib/gitlab/github_import/single_endpoint_notes_importing.rb4
-rw-r--r--lib/gitlab/runtime.rb4
-rw-r--r--lib/prometheus/pid_provider.rb2
-rw-r--r--locale/gitlab.pot3
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock4
-rw-r--r--qa/chemlab-library-gitlab.gemspec2
-rwxr-xr-xscripts/api/cancel_pipeline.rb2
-rwxr-xr-xscripts/api/download_job_artifact.rb2
-rwxr-xr-xscripts/api/get_job_id.rb2
-rwxr-xr-xscripts/changed-feature-flags2
-rwxr-xr-xscripts/failed_tests.rb2
-rwxr-xr-xscripts/generate-failed-pipeline-slack-message.rb13
-rwxr-xr-xscripts/perf/query_limiting_report.rb2
-rwxr-xr-xscripts/pipeline_test_report_builder.rb2
-rwxr-xr-xscripts/rubocop-parse2
-rwxr-xr-xscripts/setup/find-jh-branch.rb2
-rwxr-xr-xscripts/static-analysis2
-rwxr-xr-xscripts/trigger-build.rb2
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb613
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb333
-rw-r--r--spec/controllers/projects/boards_controller_spec.rb12
-rw-r--r--spec/fast_spec_helper.rb2
-rw-r--r--spec/features/global_search_spec.rb1
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_comments_spec.rb1
-rw-r--r--spec/features/search/user_searches_for_commits_spec.rb1
-rw-r--r--spec/features/search/user_searches_for_issues_spec.rb3
-rw-r--r--spec/features/search/user_searches_for_merge_requests_spec.rb1
-rw-r--r--spec/features/search/user_searches_for_milestones_spec.rb1
-rw-r--r--spec/features/search/user_searches_for_projects_spec.rb1
-rw-r--r--spec/features/search/user_searches_for_users_spec.rb1
-rw-r--r--spec/features/search/user_searches_for_wiki_pages_spec.rb5
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb1
-rw-r--r--spec/features/snippets/search_snippets_spec.rb4
-rw-r--r--spec/frontend/search/sidebar/components/app_spec.js41
-rw-r--r--spec/helpers/boards_helper_spec.rb4
-rw-r--r--spec/helpers/search_helper_spec.rb5
-rw-r--r--spec/lib/gitlab/data_builder/pipeline_spec.rb1
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb117
-rw-r--r--spec/lib/gitlab/github_import/issuable_finder_spec.rb67
-rw-r--r--spec/lib/gitlab/github_import/settings_spec.rb82
-rw-r--r--spec/models/ci/resource_group_spec.rb6
-rw-r--r--spec/requests/api/import_github_spec.rb3
-rw-r--r--spec/requests/boards/lists_controller_spec.rb25
-rw-r--r--spec/services/boards/lists/generate_service_spec.rb45
-rw-r--r--spec/services/import/github_service_spec.rb40
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb1
-rw-r--r--spec/views/search/_results.html.haml_spec.rb9
-rw-r--r--spec/views/search/show.html.haml_spec.rb135
-rw-r--r--spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb15
-rw-r--r--spec/workers/gitlab/github_import/stage/import_issue_events_worker_spec.rb46
-rw-r--r--spec/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker_spec.rb44
-rw-r--r--spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb45
115 files changed, 1040 insertions, 1953 deletions
diff --git a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
index b86b871867e..6c9b8bb6d78 100644
--- a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
@@ -60,7 +60,7 @@ Geo secondary sites have a [Geo tracking database](https://gitlab.com/gitlab-org
disable_ddl_transaction!
def up
- ApplicationRecord.transaction do
+ Geo::TrackingBase.transaction do
create_table :cool_widget_registry, id: :bigserial, force: :cascade do |t|
t.bigint :cool_widget_id, null: false
t.datetime_with_timezone :created_at, null: false
@@ -269,7 +269,6 @@ That's all of the required database changes.
def pool_repository
nil
end
- ...
def cool_widget_state
super || build_cool_widget_state
@@ -389,14 +388,16 @@ That's all of the required database changes.
```ruby
# frozen_string_literal: true
- class Geo::CoolWidgetRegistry < Geo::BaseRegistry
- include ::Geo::ReplicableRegistry
- include ::Geo::VerifiableRegistry
+ module Geo
+ class CoolWidgetRegistry < Geo::BaseRegistry
+ include ::Geo::ReplicableRegistry
+ include ::Geo::VerifiableRegistry
- MODEL_CLASS = ::CoolWidget
- MODEL_FOREIGN_KEY = :cool_widget_id
+ MODEL_CLASS = ::CoolWidget
+ MODEL_FOREIGN_KEY = :cool_widget_id
- belongs_to :cool_widget, class_name: 'CoolWidget'
+ belongs_to :cool_widget, class_name: 'CoolWidget'
+ end
end
```
@@ -463,13 +464,13 @@ That's all of the required database changes.
- [ ] Add the following to `spec/factories/cool_widgets.rb`:
```ruby
- trait(:verification_succeeded) do
+ trait :verification_succeeded do
with_file
verification_checksum { 'abc' }
verification_state { CoolWidget.verification_state_value(:verification_succeeded) }
end
- trait(:verification_failed) do
+ trait :verification_failed do
with_file
verification_failure { 'Could not calculate the checksum' }
verification_state { CoolWidget.verification_state_value(:verification_failed) }
@@ -507,11 +508,11 @@ That's all of the required database changes.
factory :geo_cool_widget_state, class: 'Geo::CoolWidgetState' do
cool_widget
- trait(:checksummed) do
+ trait :checksummed do
verification_checksum { 'abc' }
end
- trait(:checksum_failure) do
+ trait :checksum_failure do
verification_failure { 'Could not calculate the checksum' }
end
end
@@ -561,8 +562,9 @@ The GraphQL API is used by `Admin > Geo > Replication Details` views, and is dir
field :cool_widget_registries, ::Types::Geo::CoolWidgetRegistryType.connection_type,
null: true,
resolver: ::Resolvers::Geo::CoolWidgetRegistriesResolver,
- description: 'Find Cool Widget registries on this Geo node',
- feature_flag: :geo_cool_widget_replication
+ description: 'Find Cool Widget registries on this Geo node. '\
+ 'Ignored if `geo_cool_widget_replication` feature flag is disabled.',
+ alpha: { milestone: '15.5' } # Update the milestone
```
- [ ] Add the new `cool_widget_registries` field name to the `expected_fields` array in `ee/spec/graphql/types/geo/geo_node_type_spec.rb`.
@@ -627,13 +629,15 @@ The GraphQL API is used by `Admin > Geo > Replication Details` views, and is dir
module Geo
# rubocop:disable Graphql/AuthorizeTypes because it is included
class CoolWidgetRegistryType < BaseObject
+ graphql_name 'CoolWidgetRegistry'
+
include ::Types::Geo::RegistryType
- graphql_name 'CoolWidgetRegistry'
description 'Represents the Geo replication and verification state of a cool_widget'
field :cool_widget_id, GraphQL::Types::ID, null: false, description: 'ID of the Cool Widget.'
end
+ # rubocop:enable Graphql/AuthorizeTypes
end
end
```
@@ -717,14 +721,15 @@ As illustrated by the above two examples, batch destroy logic cannot be handled
- [ ] Add a step to `Test replication and verification of Cool Widgets on a non-GDK-deployment. For example, using GitLab Environment Toolkit`.
- [ ] Add a step to `Ping the Geo PM and EM to coordinate testing`. For example, you might add steps to generate Cool Widgets, and then a Geo engineer may take it from there.
- [ ] In `ee/config/feature_flags/development/geo_cool_widget_replication.yml`, set `default_enabled: true`
-- [ ] In `ee/app/graphql/types/geo/geo_node_type.rb`, remove the `feature_flag` option for the released type:
+- [ ] In `ee/app/graphql/types/geo/geo_node_type.rb`, remove the `alpha` option for the released type:
```ruby
field :cool_widget_registries, ::Types::Geo::CoolWidgetRegistryType.connection_type,
null: true,
resolver: ::Resolvers::Geo::CoolWidgetRegistriesResolver,
- description: 'Find Cool Widget registries on this Geo node',
- feature_flag: :geo_cool_widget_replication # REMOVE THIS LINE
+ description: 'Find Cool Widget registries on this Geo node. '\
+ 'Ignored if `geo_cool_widget_replication` feature flag is disabled.',
+ alpha: { milestone: '15.5' } # Update the milestone
```
- [ ] Run `bundle exec rake gitlab:graphql:compile_docs` after the step above to regenerate the GraphQL docs.
diff --git a/.gitlab/issue_templates/Geo Replicate a new blob type.md b/.gitlab/issue_templates/Geo Replicate a new blob type.md
index c6bf1fae52c..76fe1772921 100644
--- a/.gitlab/issue_templates/Geo Replicate a new blob type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new blob type.md
@@ -58,11 +58,11 @@ Geo secondary sites have a [Geo tracking database](https://gitlab.com/gitlab-org
```ruby
# frozen_string_literal: true
- class CreateCoolWidgetRegistry < Gitlab::Database::Migration[1.0]
+ class CreateCoolWidgetRegistry < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
def up
- ApplicationRecord.transaction do
+ Geo::TrackingBase.transaction do
create_table :cool_widget_registry, id: :bigserial, force: :cascade do |t|
t.bigint :cool_widget_id, null: false
t.datetime_with_timezone :created_at, null: false
@@ -247,7 +247,8 @@ That's all of the required database changes.
# we want to know which records to replicate. This is not easy to automate
# because for example:
#
- # * The "selective sync" feature allows admins to choose which namespaces # to replicate, per secondary site. Most Models are scoped to a
+ # * The "selective sync" feature allows admins to choose which namespaces
+ # to replicate, per secondary site. Most Models are scoped to a
# namespace, but the nature of the relationship to a namespace varies
# between Models.
# * The "selective sync" feature allows admins to choose which shards to
@@ -265,7 +266,6 @@ That's all of the required database changes.
CoolWidgetState
end
end
- ...
def cool_widget_state
super || build_cool_widget_state
@@ -317,7 +317,7 @@ That's all of the required database changes.
end
```
-- [ ] Generate the feature flag definition fileы by running the feature flag commands and following the command prompts:
+- [ ] Generate the feature flag definition file by running the feature flag commands and following the command prompts:
```shell
bin/feature-flag --ee geo_cool_widget_replication --type development --group 'group::geo'
@@ -355,14 +355,16 @@ That's all of the required database changes.
```ruby
# frozen_string_literal: true
- class Geo::CoolWidgetRegistry < Geo::BaseRegistry
- include ::Geo::ReplicableRegistry
- include ::Geo::VerifiableRegistry
+ module Geo
+ class CoolWidgetRegistry < Geo::BaseRegistry
+ include ::Geo::ReplicableRegistry
+ include ::Geo::VerifiableRegistry
- MODEL_CLASS = ::CoolWidget
- MODEL_FOREIGN_KEY = :cool_widget_id
+ MODEL_CLASS = ::CoolWidget
+ MODEL_FOREIGN_KEY = :cool_widget_id
- belongs_to :cool_widget, class_name: 'CoolWidget'
+ belongs_to :cool_widget, class_name: 'CoolWidget'
+ end
end
```
@@ -429,13 +431,13 @@ That's all of the required database changes.
- [ ] Add the following to `spec/factories/cool_widgets.rb`:
```ruby
- trait(:verification_succeeded) do
+ trait :verification_succeeded do
with_file
verification_checksum { 'abc' }
verification_state { CoolWidget.verification_state_value(:verification_succeeded) }
end
- trait(:verification_failed) do
+ trait :verification_failed do
with_file
verification_failure { 'Could not calculate the checksum' }
verification_state { CoolWidget.verification_state_value(:verification_failed) }
@@ -447,6 +449,8 @@ That's all of the required database changes.
- [ ] Following [the example of Merge Request Diffs](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63309) add a `Geo::CoolWidgetState` model in `ee/app/models/ee/geo/cool_widget_state.rb`:
``` ruby
+ # frozen_string_literal: true
+
module Geo
class CoolWidgetState < ApplicationRecord
include EachBatch
@@ -471,11 +475,11 @@ That's all of the required database changes.
factory :geo_cool_widget_state, class: 'Geo::CoolWidgetState' do
cool_widget
- trait(:checksummed) do
+ trait :checksummed do
verification_checksum { 'abc' }
end
- trait(:checksum_failure) do
+ trait :checksum_failure do
verification_failure { 'Could not calculate the checksum' }
end
end
@@ -525,8 +529,9 @@ The GraphQL API is used by `Admin > Geo > Replication Details` views, and is dir
field :cool_widget_registries, ::Types::Geo::CoolWidgetRegistryType.connection_type,
null: true,
resolver: ::Resolvers::Geo::CoolWidgetRegistriesResolver,
- description: 'Find Cool Widget registries on this Geo node',
- feature_flag: :geo_cool_widget_replication
+ description: 'Find Cool Widget registries on this Geo node. '\
+ 'Ignored if `geo_cool_widget_replication` feature flag is disabled.',
+ alpha: { milestone: '15.5' } # Update the milestone
```
- [ ] Add the new `cool_widget_registries` field name to the `expected_fields` array in `ee/spec/graphql/types/geo/geo_node_type_spec.rb`.
@@ -591,13 +596,15 @@ The GraphQL API is used by `Admin > Geo > Replication Details` views, and is dir
module Geo
# rubocop:disable Graphql/AuthorizeTypes because it is included
class CoolWidgetRegistryType < BaseObject
+ graphql_name 'CoolWidgetRegistry'
+
include ::Types::Geo::RegistryType
- graphql_name 'CoolWidgetRegistry'
description 'Represents the Geo replication and verification state of a cool_widget'
- field :cool_widget_id, GraphQL::ID_TYPE, null: false, description: 'ID of the Cool Widget'
+ field :cool_widget_id, GraphQL::Types::ID, null: false, description: 'ID of the Cool Widget.'
end
+ # rubocop:enable Graphql/AuthorizeTypes
end
end
```
@@ -682,14 +689,15 @@ As illustrated by the above two examples, batch destroy logic cannot be handled
- [ ] Add a step to `Test replication and verification of Cool Widgets on a non-GDK-deployment. For example, using GitLab Environment Toolkit`.
- [ ] Add a step to `Ping the Geo PM and EM to coordinate testing`. For example, you might add steps to generate Cool Widgets, and then a Geo engineer may take it from there.
- [ ] In `ee/config/feature_flags/development/geo_cool_widget_replication.yml`, set `default_enabled: true`
-- [ ] In `ee/app/graphql/types/geo/geo_node_type.rb`, remove the `feature_flag` option for the released type:
+- [ ] In `ee/app/graphql/types/geo/geo_node_type.rb`, remove the `alpha` option for the released type:
```ruby
field :cool_widget_registries, ::Types::Geo::CoolWidgetRegistryType.connection_type,
null: true,
resolver: ::Resolvers::Geo::CoolWidgetRegistriesResolver,
- description: 'Find Cool Widget registries on this Geo node',
- feature_flag: :geo_cool_widget_replication # REMOVE THIS LINE
+ description: 'Find Cool Widget registries on this Geo node. '\
+ 'Ignored if `geo_cool_widget_replication` feature flag is disabled.',
+ alpha: { milestone: '15.5' } # Update the milestone
```
- [ ] Run `bundle exec rake gitlab:graphql:compile_docs` after the step above to regenerate the GraphQL docs.
diff --git a/.rubocop.yml b/.rubocop.yml
index 5aca299eedc..a0f2e762575 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -116,8 +116,7 @@ Style/FrozenStringLiteralComment:
EnforcedStyle: always_true
Style/SpecialGlobalVars:
- # https://gitlab.com/gitlab-org/gitlab/-/issues/358427
- EnforcedStyle: use_perl_names
+ EnforcedStyle: use_builtin_english_names
RSpec/FilePath:
Exclude:
diff --git a/.rubocop_todo/style/special_global_vars.yml b/.rubocop_todo/style/special_global_vars.yml
new file mode 100644
index 00000000000..df688872d71
--- /dev/null
+++ b/.rubocop_todo/style/special_global_vars.yml
@@ -0,0 +1,4 @@
+---
+# Cop supports --auto-correct.
+Style/SpecialGlobalVars:
+ Details: grace period
diff --git a/GITLAB_ELASTICSEARCH_INDEXER_VERSION b/GITLAB_ELASTICSEARCH_INDEXER_VERSION
index b5021469305..75a22a26ac4 100644
--- a/GITLAB_ELASTICSEARCH_INDEXER_VERSION
+++ b/GITLAB_ELASTICSEARCH_INDEXER_VERSION
@@ -1 +1 @@
-3.0.2
+3.0.3
diff --git a/Gemfile b/Gemfile
index 71e537ab13b..ac38fbfd530 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,7 +3,7 @@
source 'https://rubygems.org'
if ENV['BUNDLER_CHECKSUM_VERIFICATION_OPT_IN'] # this verification is still experimental
- $:.unshift(File.expand_path("vendor/gems/bundler-checksum/lib", __dir__))
+ $LOAD_PATH.unshift(File.expand_path("vendor/gems/bundler-checksum/lib", __dir__))
require 'bundler-checksum'
Bundler::Checksum.patch!
end
diff --git a/app/assets/javascripts/search/index.js b/app/assets/javascripts/search/index.js
index 446ab7f433c..ba12f31ef87 100644
--- a/app/assets/javascripts/search/index.js
+++ b/app/assets/javascripts/search/index.js
@@ -1,7 +1,7 @@
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
import { queryToObject } from '~/lib/utils/url_utility';
import refreshCounts from '~/pages/search/show/refresh_counts';
-import { initSidebar } from './sidebar';
+import { initSidebar, sidebarInitState } from './sidebar';
import { initSearchSort } from './sort';
import createStore from './store';
import { initTopbar } from './topbar';
@@ -9,14 +9,18 @@ import { initBlobRefSwitcher } from './under_topbar';
export const initSearchApp = () => {
const query = queryToObject(window.location.search);
+ const navigation = sidebarInitState();
- const store = createStore({ query });
+ const store = createStore({ query, navigation });
initTopbar(store);
initSidebar(store);
initSearchSort(store);
setHighlightClass(query.search); // Code Highlighting
- refreshCounts(); // Other Scope Tab Counts
initBlobRefSwitcher(); // Code Search Branch Picker
+
+ if (!gon.features?.searchPageVerticalNav) {
+ refreshCounts(); // Other Scope Tab Counts
+ }
};
diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue
index 5c7cbeac5b2..789efc8f09d 100644
--- a/app/assets/javascripts/search/sidebar/components/app.vue
+++ b/app/assets/javascripts/search/sidebar/components/app.vue
@@ -17,6 +17,9 @@ export default {
showReset() {
return this.urlQuery.state || this.urlQuery.confidential;
},
+ showSidebar() {
+ return this.urlQuery.scope === 'issues' || this.urlQuery.scope === 'merge_requests';
+ },
},
methods: {
...mapActions(['applyQuery', 'resetQuery']),
@@ -29,15 +32,17 @@ export default {
class="search-sidebar gl-display-flex gl-flex-direction-column gl-mr-4 gl-mb-6 gl-mt-5"
@submit.prevent="applyQuery"
>
- <status-filter />
- <confidentiality-filter />
- <div class="gl-display-flex gl-align-items-center gl-mt-3">
- <gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty">
- {{ __('Apply') }}
- </gl-button>
- <gl-link v-if="showReset" class="gl-ml-auto" @click="resetQuery">{{
- __('Reset filters')
- }}</gl-link>
- </div>
+ <template v-if="showSidebar">
+ <status-filter />
+ <confidentiality-filter />
+ <div class="gl-display-flex gl-align-items-center gl-mt-3">
+ <gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty">
+ {{ __('Apply') }}
+ </gl-button>
+ <gl-link v-if="showReset" class="gl-ml-auto" @click="resetQuery">{{
+ __('Reset filters')
+ }}</gl-link>
+ </div>
+ </template>
</form>
</template>
diff --git a/app/assets/javascripts/search/sidebar/index.js b/app/assets/javascripts/search/sidebar/index.js
index 1414adcac27..c6b1257c4ef 100644
--- a/app/assets/javascripts/search/sidebar/index.js
+++ b/app/assets/javascripts/search/sidebar/index.js
@@ -4,6 +4,15 @@ import GlobalSearchSidebar from './components/app.vue';
Vue.use(Translate);
+export const sidebarInitState = () => {
+ const el = document.getElementById('js-search-sidebar');
+
+ if (!el) return {};
+
+ const { navigation } = el.dataset;
+ return JSON.parse(navigation);
+};
+
export const initSidebar = (store) => {
const el = document.getElementById('js-search-sidebar');
diff --git a/app/assets/javascripts/search/topbar/components/app.vue b/app/assets/javascripts/search/topbar/components/app.vue
index f27dae8249d..d0fcbb0d83b 100644
--- a/app/assets/javascripts/search/topbar/components/app.vue
+++ b/app/assets/javascripts/search/topbar/components/app.vue
@@ -1,7 +1,9 @@
<script>
import { GlSearchBoxByClick } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { s__ } from '~/locale';
+import { parseBoolean } from '~/lib/utils/common_utils';
import GroupFilter from './group_filter.vue';
import ProjectFilter from './project_filter.vue';
@@ -16,6 +18,7 @@ export default {
GroupFilter,
ProjectFilter,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
groupInitialData: {
type: Object,
@@ -39,7 +42,10 @@ export default {
},
},
showFilters() {
- return !this.query.snippets || this.query.snippets === 'false';
+ return !parseBoolean(this.query.snippets);
+ },
+ hasVerticalNav() {
+ return this.glFeatures.searchPageVerticalNav;
},
},
created() {
@@ -52,24 +58,27 @@ export default {
</script>
<template>
- <section class="search-page-form gl-lg-display-flex gl-align-items-flex-end">
- <div class="gl-flex-grow-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
- <label>{{ $options.i18n.searchLabel }}</label>
- <gl-search-box-by-click
- id="dashboard_search"
- v-model="search"
- name="search"
- :placeholder="$options.i18n.searchPlaceholder"
- @submit="applyQuery"
- />
- </div>
- <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
- <label class="gl-display-block">{{ __('Group') }}</label>
- <group-filter :initial-data="groupInitialData" />
- </div>
- <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
- <label class="gl-display-block">{{ __('Project') }}</label>
- <project-filter :initial-data="projectInitialData" />
+ <section class="search-page-form gl-lg-display-flex gl-flex-direction-column">
+ <div class="gl-lg-display-flex gl-flex-direction-row gl-align-items-flex-end">
+ <div class="gl-flex-grow-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
+ <label>{{ $options.i18n.searchLabel }}</label>
+ <gl-search-box-by-click
+ id="dashboard_search"
+ v-model="search"
+ name="search"
+ :placeholder="$options.i18n.searchPlaceholder"
+ @submit="applyQuery"
+ />
+ </div>
+ <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
+ <label class="gl-display-block">{{ __('Group') }}</label>
+ <group-filter :initial-data="groupInitialData" />
+ </div>
+ <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
+ <label class="gl-display-block">{{ __('Project') }}</label>
+ <project-filter :initial-data="projectInitialData" />
+ </div>
</div>
+ <hr v-if="hasVerticalNav" class="gl-mt-5 gl-mb-0 gl-border-gray-100" />
</section>
</template>
diff --git a/app/controllers/boards/application_controller.rb b/app/controllers/boards/application_controller.rb
deleted file mode 100644
index 15ef6698472..00000000000
--- a/app/controllers/boards/application_controller.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-module Boards
- class ApplicationController < ::ApplicationController
- respond_to :json
-
- rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
-
- private
-
- def board
- @board ||= Board.find(params[:board_id])
- end
-
- def board_parent
- @board_parent ||= board.resource_parent
- end
-
- def record_not_found(exception)
- render json: { error: exception.message }, status: :not_found
- end
- end
-end
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
deleted file mode 100644
index 8bd7cd496e5..00000000000
--- a/app/controllers/boards/issues_controller.rb
+++ /dev/null
@@ -1,163 +0,0 @@
-# frozen_string_literal: true
-
-module Boards
- class IssuesController < Boards::ApplicationController
- # This is the maximum amount of issues which can be moved by one request to
- # bulk_move for now. This is temporary and might be removed in future by
- # introducing an alternative (async?) approach.
- # (related: https://gitlab.com/groups/gitlab-org/-/epics/382)
- MAX_MOVE_ISSUES_COUNT = 50
-
- include BoardsResponses
- include ControllerWithCrossProjectAccessCheck
-
- requires_cross_project_access if: -> { board&.group_board? }
-
- before_action :disable_query_limiting, only: [:bulk_move]
- before_action :authorize_read_issue, only: [:index]
- before_action :authorize_create_issue, only: [:create]
- before_action :authorize_update_issue, only: [:update]
- skip_before_action :authenticate_user!, only: [:index]
- before_action :validate_id_list, only: [:bulk_move]
- before_action :can_move_issues?, only: [:bulk_move]
-
- feature_category :team_planning
- urgency :low
-
- def index
- list_service = Boards::Issues::ListService.new(board_parent, current_user, filter_params)
- issues = issues_from(list_service)
-
- ::Boards::Issues::ListService.initialize_relative_positions(board, current_user, issues)
-
- render_issues(issues, list_service.metadata)
- end
-
- def create
- result = Boards::Issues::CreateService.new(board_parent, project, current_user, issue_params).execute
-
- if result.success?
- render json: serialize_as_json(result[:issue])
- elsif result[:issue]
- render json: result[:issue].errors, status: :unprocessable_entity
- else
- render json: result.errors, status: result.http_status || 422
- end
- end
-
- def bulk_move
- service = Boards::Issues::MoveService.new(board_parent, current_user, move_params(true))
-
- issues = Issue.find(params[:ids])
-
- render json: service.execute_multiple(issues)
- end
-
- def update
- service = Boards::Issues::MoveService.new(board_parent, current_user, move_params)
-
- if service.execute(issue)
- head :ok
- else
- head :unprocessable_entity
- end
- end
-
- private
-
- def issues_from(list_service)
- issues = list_service.execute
- issues.page(params[:page]).per(params[:per] || 20)
- .without_count
- .preload(associations_to_preload) # rubocop: disable CodeReuse/ActiveRecord
- .load
- end
-
- def associations_to_preload
- [
- :milestone,
- :assignees,
- project: [
- :route,
- {
- namespace: [:route]
- }
- ],
- labels: [:priorities],
- notes: [:award_emoji, :author]
- ]
- end
-
- def can_move_issues?
- head(:forbidden) unless can?(current_user, :admin_issue, board)
- end
-
- def serializer_options(issues)
- {}
- end
-
- def render_issues(issues, metadata)
- data = { issues: serialize_as_json(issues, opts: serializer_options(issues)) }
- data.merge!(metadata)
-
- render json: data
- end
-
- def issue
- @issue ||= issues_finder.find(params[:id])
- end
-
- def filter_params
- params.permit(*Boards::Issues::ListService.valid_params).merge(board_id: params[:board_id], id: params[:list_id])
- .reject { |_, value| value.nil? }
- end
-
- def issues_finder
- if board.group_board?
- IssuesFinder.new(current_user, group_id: board_parent.id)
- else
- IssuesFinder.new(current_user, project_id: board_parent.id)
- end
- end
-
- def project
- @project ||= if board.group_board?
- Project.find(issue_params[:project_id])
- else
- board_parent
- end
- end
-
- def move_params(multiple = false)
- id_param = multiple ? :ids : :id
- params.permit(id_param, :board_id, :from_list_id, :to_list_id, :move_before_id, :move_after_id)
- end
-
- def issue_params
- params.require(:issue)
- .permit(:title, :milestone_id, :project_id)
- .merge(board_id: params[:board_id], list_id: params[:list_id])
- end
-
- def serializer
- IssueSerializer.new(current_user: current_user)
- end
-
- def serialize_as_json(resource, opts: {})
- opts.merge!(include_full_project_path: board.group_board?, serializer: 'board')
-
- serializer.represent(resource, opts)
- end
-
- def disable_query_limiting
- Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/35174')
- end
-
- def validate_id_list
- head(:bad_request) unless params[:ids].is_a?(Array)
- head(:unprocessable_entity) if params[:ids].size > MAX_MOVE_ISSUES_COUNT
- end
- end
-end
-
-Boards::IssuesController.prepend_mod_with('Boards::IssuesController')
diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb
deleted file mode 100644
index c3b5a887920..00000000000
--- a/app/controllers/boards/lists_controller.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-# frozen_string_literal: true
-
-module Boards
- class ListsController < Boards::ApplicationController
- include BoardsResponses
-
- before_action :authorize_admin_list, only: [:create, :destroy, :generate]
- before_action :authorize_read_list, only: [:index]
- skip_before_action :authenticate_user!, only: [:index]
-
- feature_category :team_planning
- urgency :low
-
- def index
- lists = Boards::Lists::ListService.new(board.resource_parent, current_user).execute(board)
-
- List.preload_preferences_for_user(lists, current_user)
-
- render json: serialize_as_json(lists)
- end
-
- def create
- response = Boards::Lists::CreateService.new(board.resource_parent, current_user, create_list_params).execute(board)
-
- if response.success?
- render json: serialize_as_json(response.payload[:list])
- else
- render json: { errors: response.errors }, status: :unprocessable_entity
- end
- end
-
- def update
- list = board.lists.find(params[:id])
- service = Boards::Lists::UpdateService.new(board_parent, current_user, update_list_params)
- result = service.execute(list)
-
- if result.success?
- head :ok
- else
- head result.http_status
- end
- end
-
- def destroy
- list = board.lists.destroyable.find(params[:id])
- service = Boards::Lists::DestroyService.new(board_parent, current_user)
-
- if service.execute(list).success?
- head :ok
- else
- head :unprocessable_entity
- end
- end
-
- def generate
- service = Boards::Lists::GenerateService.new(board_parent, current_user)
-
- if service.execute(board)
- lists = board.lists.movable.preload_associated_models
-
- List.preload_preferences_for_user(lists, current_user)
-
- render json: serialize_as_json(lists)
- else
- head :unprocessable_entity
- end
- end
-
- private
-
- def list_creation_attrs
- %i[label_id]
- end
-
- def list_update_attrs
- %i[collapsed position]
- end
-
- def create_list_params
- params.require(:list).permit(list_creation_attrs)
- end
-
- def update_list_params
- params.require(:list).permit(list_update_attrs)
- end
-
- def serialize_as_json(resource)
- resource.as_json(serialization_attrs)
- end
-
- def serialization_attrs
- {
- only: [:id, :list_type, :position],
- methods: [:title],
- label: true,
- collapsed: true,
- current_user: current_user
- }
- end
- end
-end
-
-Boards::ListsController.prepend_mod_with('Boards::ListsController')
diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb
index 7b056c228e6..42bf6c68aa7 100644
--- a/app/controllers/concerns/boards_actions.rb
+++ b/app/controllers/concerns/boards_actions.rb
@@ -5,8 +5,6 @@ module BoardsActions
extend ActiveSupport::Concern
included do
- include BoardsResponses
-
before_action :authorize_read_board!, only: [:index, :show]
before_action :redirect_to_recent_board, only: [:index]
before_action :board, only: [:index, :show]
@@ -50,6 +48,24 @@ module BoardsActions
def board_visit_service
Boards::Visits::CreateService
end
+
+ def parent
+ strong_memoize(:parent) do
+ group? ? group : project
+ end
+ end
+
+ def board_path(board)
+ if group?
+ group_board_path(parent, board)
+ else
+ project_board_path(parent, board)
+ end
+ end
+
+ def group?
+ instance_variable_defined?(:@group)
+ end
end
BoardsActions.prepend_mod_with('BoardsActions')
diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb
deleted file mode 100644
index a1e7eaa7f60..00000000000
--- a/app/controllers/concerns/boards_responses.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-module BoardsResponses
- include Gitlab::Utils::StrongMemoize
-
- # Overridden on EE module
- def board_params
- params.require(:board).permit(:name)
- end
-
- def parent
- strong_memoize(:parent) do
- group? ? group : project
- end
- end
-
- def boards_path
- if group?
- group_boards_path(parent)
- else
- project_boards_path(parent)
- end
- end
-
- def board_path(board)
- if group?
- group_board_path(parent, board)
- else
- project_board_path(parent, board)
- end
- end
-
- def group?
- instance_variable_defined?(:@group)
- end
-
- def authorize_read_list
- authorize_action_for!(board, :read_issue_board_list)
- end
-
- def authorize_read_issue
- authorize_action_for!(board, :read_issue)
- end
-
- def authorize_update_issue
- authorize_action_for!(issue, :admin_issue)
- end
-
- def authorize_create_issue
- list = List.find(issue_params[:list_id])
- action = list.backlog? ? :create_issue : :admin_issue
-
- authorize_action_for!(project, action)
- end
-
- def authorize_admin_list
- authorize_action_for!(board, :admin_issue_board_list)
- end
-
- def authorize_action_for!(resource, ability)
- return render_403 unless can?(current_user, ability, resource)
- end
-end
-
-BoardsResponses.prepend_mod_with('BoardsResponses')
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index 4fa642f7446..14b70df0feb 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -5,7 +5,6 @@ class Groups::BoardsController < Groups::ApplicationController
include RecordUserLastActivity
include Gitlab::Utils::StrongMemoize
- before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:board_multi_select, group)
push_frontend_feature_flag(:realtime_labels, group)
@@ -32,10 +31,6 @@ class Groups::BoardsController < Groups::ApplicationController
end
end
- def assign_endpoint_vars
- @boards_endpoint = group_boards_path(group)
- end
-
def authorize_read_board!
access_denied! unless can?(current_user, :read_issue_board, group)
end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index ab81fdfddb2..92763e09ba3 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -113,7 +113,7 @@ class Import::GithubController < Import::BaseController
end
def permitted_import_params
- [:repo_id, :new_name, :target_namespace]
+ [:repo_id, :new_name, :target_namespace, { optional_stages: {} }]
end
def serialized_imported_projects(projects = already_added_projects)
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 082ebca40a3..6a6701ead15 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -5,7 +5,6 @@ class Projects::BoardsController < Projects::ApplicationController
include IssuableCollections
before_action :check_issues_available!
- before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:board_multi_select, project)
push_frontend_feature_flag(:realtime_labels, project&.group)
@@ -32,11 +31,6 @@ class Projects::BoardsController < Projects::ApplicationController
end
end
- def assign_endpoint_vars
- @boards_endpoint = project_boards_path(project)
- @bulk_issues_path = bulk_update_project_issues_path(project)
- end
-
def authorize_read_board!
access_denied! unless can?(current_user, :read_issue_board, project)
end
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index a14b51d42bf..db6cf27566f 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -7,13 +7,10 @@ module BoardsHelper
def board_data
{
- boards_endpoint: @boards_endpoint,
- lists_endpoint: board_lists_path(board),
board_id: board.id,
disabled: board.disabled_for?(current_user).to_s,
root_path: root_path,
full_path: full_path,
- bulk_update_path: @bulk_issues_path,
can_update: can_update?.to_s,
can_admin_list: can_admin_list?.to_s,
can_admin_board: can_admin_board?.to_s,
@@ -94,14 +91,6 @@ module BoardsHelper
!multiple_boards_available? && current_board_parent.boards.size > 1
end
- def current_board_path(board)
- @current_board_path ||= if board.group_board?
- group_board_path(current_board_parent, board)
- else
- project_board_path(current_board_parent, board)
- end
- end
-
def current_board_parent
@current_board_parent ||= @group || @project
end
@@ -121,10 +110,6 @@ module BoardsHelper
def can_admin_board?
can?(current_user, :admin_issue_board, current_board_parent)
end
-
- def can_admin_issue?
- can?(current_user, :admin_issue, current_board_parent)
- end
end
BoardsHelper.prepend_mod_with('BoardsHelper')
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index a2ad510b4f1..115cdd432e3 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -3,6 +3,10 @@
module IssuesHelper
include Issues::IssueTypeHelpers
+ def can_admin_issue?
+ can?(current_user, :admin_issue, @group || @project)
+ end
+
def issue_css_classes(issue)
classes = ["issue"]
classes << "closed" if issue.closed?
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 9f906d7b218..f2b88287277 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -381,11 +381,12 @@ module SearchHelper
end
def search_filter_link_json(scope, label, data, search)
- search_params = params.merge(search).merge({ scope: scope }).permit(SEARCH_GENERIC_PARAMS)
- active_scope = @scope == scope
+ scope_name = scope.to_s
+ search_params = params.merge(search).merge({ scope: scope_name }).permit(SEARCH_GENERIC_PARAMS)
+ active_scope = @scope == scope_name
- result = { label: label, scope: scope, data: data, link: search_path(search_params), active: active_scope }
- result[:count] = @search_results.formatted_count(scope) if active_scope && !@timeout
+ result = { label: label, scope: scope_name, data: data, link: search_path(search_params), active: active_scope }
+ result[:count] = @search_results.formatted_count(scope_name) if active_scope && !@timeout
result[:count_link] = search_count_path(search_params) unless active_scope
result
@@ -408,11 +409,9 @@ module SearchHelper
end
def search_navigation_json
- result = {}
- search_navigation.each do |scope, nav|
- result[scope] = search_filter_link_json(scope.to_s, nav[:label], nav[:data], nav[:search]) if nav[:condition]
- end
- result.to_json
+ search_navigation.each_with_object({}) do |(key, value), hash|
+ hash[key] = search_filter_link_json(key, value[:label], value[:data], value[:search]) if value[:condition]
+ end.to_json
end
def search_filter_input_options(type, placeholder = _('Search or filter results...'))
diff --git a/app/models/project.rb b/app/models/project.rb
index 5bface252f1..a5589c71f73 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1286,6 +1286,8 @@ class Project < ApplicationRecord
valid?(:import_url) || errors.messages[:import_url].nil?
end
+ # TODO: rename to build_or_assign_import_data as it doesn't save record
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/377319
def create_or_update_import_data(data: nil, credentials: nil)
return if data.nil? && credentials.nil?
diff --git a/app/services/boards/lists/generate_service.rb b/app/services/boards/lists/generate_service.rb
deleted file mode 100644
index d74320e92a3..00000000000
--- a/app/services/boards/lists/generate_service.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-module Boards
- module Lists
- class GenerateService < Boards::BaseService
- def execute(board)
- return false unless board.lists.movable.empty?
-
- List.transaction do
- label_params.each do |params|
- response = create_list(board, params)
-
- raise ActiveRecord::Rollback unless response.success?
- end
- end
-
- true
- end
-
- private
-
- def create_list(board, params)
- label = find_or_create_label(params)
- Lists::CreateService.new(parent, current_user, label_id: label.id).execute(board)
- end
-
- def find_or_create_label(params)
- ::Labels::FindOrCreateService.new(current_user, parent, params).execute
- end
-
- def label_params
- [
- { name: 'To Do', color: '#F0AD4E' },
- { name: 'Doing', color: '#5CB85C' }
- ]
- end
- end
- end
-end
diff --git a/app/services/import/github_service.rb b/app/services/import/github_service.rb
index 53297d2412c..a60963e28c7 100644
--- a/app/services/import/github_service.rb
+++ b/app/services/import/github_service.rb
@@ -9,21 +9,13 @@ module Import
attr_reader :params, :current_user
def execute(access_params, provider)
- if blocked_url?
- return log_and_return_error("Invalid URL: #{url}", _("Invalid URL: %{url}") % { url: url }, :bad_request)
- end
-
- unless authorized?
- return error(_('This namespace has already been taken! Please choose another one.'), :unprocessable_entity)
- end
-
- if oversized?
- return error(oversize_error_message, :unprocessable_entity)
- end
+ context_error = validate_context
+ return context_error if context_error
project = create_project(access_params, provider)
if project.persisted?
+ store_import_settings(project)
success(project)
elsif project.errors[:import_source_disabled].present?
error(project.errors[:import_source_disabled], :forbidden)
@@ -108,6 +100,16 @@ module Import
private
+ def validate_context
+ if blocked_url?
+ log_and_return_error("Invalid URL: #{url}", _("Invalid URL: %{url}") % { url: url }, :bad_request)
+ elsif !authorized?
+ error(_('This namespace has already been taken. Choose a different one.'), :unprocessable_entity)
+ elsif oversized?
+ error(oversize_error_message, :unprocessable_entity)
+ end
+ end
+
def log_error(exception)
Gitlab::GithubImport::Logger.error(
message: 'Import failed due to a GitHub error',
@@ -126,6 +128,10 @@ module Import
error(translated_message, http_status)
end
+
+ def store_import_settings(project)
+ Gitlab::GithubImport::Settings.new(project).write(params[:optional_stages])
+ end
end
end
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index 1b556cd0f7f..25afe9a7b1b 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -7,4 +7,7 @@
- paginatable = Feature.enabled?(:remove_legacy_github_client)
-= render 'import/githubish_status', provider: 'github', paginatable: paginatable, default_namespace: @namespace
+= render 'import/githubish_status',
+ provider: 'github', paginatable: paginatable,
+ default_namespace: @namespace,
+ optional_stages: Gitlab::GithubImport::Settings.stages_array
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 168f4ca10bc..8262c3c90e1 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -1,26 +1,19 @@
- search_bar_classes = 'search-sidebar gl-display-flex gl-flex-direction-column gl-mr-4'
+
= render_if_exists 'shared/promotions/promote_advanced_search'
-= render partial: 'search/results_status', locals: { search_service: @search_service } unless @search_objects.to_a.empty?
+- if Feature.enabled?(:search_page_vertical_nav, current_user) && %w[issues merge_requests].include?(@scope)
+ .results.gl-md-display-flex.gl-mt-0
+ #js-search-sidebar{ class: search_bar_classes, data: { navigation: search_navigation_json } }
+ .gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
+ = render partial: 'search/results_status', locals: { search_service: @search_service } unless @search_objects.to_a.empty?
+ = render partial: 'search/results_list'
+
+- else
+ = render partial: 'search/results_status', locals: { search_service: @search_service } unless @search_objects.to_a.empty?
-.results.gl-md-display-flex.gl-mt-3
- - if %w[issues merge_requests].include?(@scope)
- #js-search-sidebar{ class: search_bar_classes }
- .gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
- - if @timeout
- = render partial: "search/results/timeout"
- - elsif @search_objects.to_a.empty?
- = render partial: "search/results/empty"
- - else
- - if @scope == 'commits'
- %ul.content-list.commit-list
- = render partial: "search/results/commit", collection: @search_objects
- - else
- .search-results
- - if @scope == 'projects'
- .term
- = render 'shared/projects/list', projects: @search_objects, pipeline_status: false
- - else
- = render_if_exists partial: "search/results/#{@scope.singularize}", collection: @search_objects
+ .results.gl-md-display-flex.gl-mt-3
+ - if %w[issues merge_requests].include?(@scope)
+ #js-search-sidebar{ class: search_bar_classes, data: { navigation: search_navigation_json } }
- - if @scope != 'projects'
- = paginate_collection(@search_objects)
+ .gl-w-full.gl-flex-grow-1.gl-overflow-x-hidden
+ = render partial: 'search/results_list'
diff --git a/app/views/search/_results_list.html.haml b/app/views/search/_results_list.html.haml
new file mode 100644
index 00000000000..cf910402ad4
--- /dev/null
+++ b/app/views/search/_results_list.html.haml
@@ -0,0 +1,18 @@
+- if @timeout
+ = render partial: "search/results/timeout"
+- elsif @search_objects.to_a.empty?
+ = render partial: "search/results/empty"
+- else
+ - if @scope == 'commits'
+ %ul.content-list.commit-list
+ = render partial: "search/results/commit", collection: @search_objects
+ - else
+ .search-results
+ - if @scope == 'projects'
+ .term
+ = render 'shared/projects/list', projects: @search_objects, pipeline_status: false
+ - else
+ = render_if_exists partial: "search/results/#{@scope.singularize}", collection: @search_objects
+
+ - if @scope != 'projects'
+ = paginate_collection(@search_objects)
diff --git a/app/views/search/_results_status.html.haml b/app/views/search/_results_status.html.haml
index ef5e3e83103..e6bb0c18b90 100644
--- a/app/views/search/_results_status.html.haml
+++ b/app/views/search/_results_status.html.haml
@@ -2,24 +2,8 @@
- return unless search_service.show_results_status?
-.search-results-status
- .row-content-block.gl-display-flex
- .gl-md-display-flex.gl-text-left.gl-align-items-center.gl-flex-grow-1
- - unless search_service.without_count?
- = search_entries_info(search_service.search_objects, search_service.scope, params[:search])
- - unless search_service.show_snippets?
- - if search_service.project
- - link_to_project = link_to(search_service.project.full_name, search_service.project, class: 'ml-md-1')
- - if search_service.scope == 'blobs'
- = _("in")
- .mx-md-1
- #js-blob-ref-switcher{ data: { "project-id" => search_service.project.id, "ref" => repository_ref(search_service.project), "field-name": "repository_ref" } }
- = s_('SearchCodeResults|of %{link_to_project}').html_safe % { link_to_project: link_to_project }
- - else
- = _("in project %{link_to_project}").html_safe % { link_to_project: link_to_project }
- - elsif search_service.group
- - link_to_group = link_to(search_service.group.name, search_service.group, class: 'ml-md-1')
- = _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group }
- - if search_service.show_sort_dropdown?
- .gl-md-display-flex.gl-flex-direction-column
- #js-search-sort{ data: { "search-sort-options" => search_sort_options.to_json } }
+- if Feature.enabled?(:search_page_vertical_nav, current_user)
+ = render partial: 'search/results_status_vert_nav', locals: { search_service: @search_service }
+
+- else
+ = render partial: 'search/results_status_horiz_nav', locals: { search_service: @search_service }
diff --git a/app/views/search/_results_status_horiz_nav.html.haml b/app/views/search/_results_status_horiz_nav.html.haml
new file mode 100644
index 00000000000..fe6ee0f12ec
--- /dev/null
+++ b/app/views/search/_results_status_horiz_nav.html.haml
@@ -0,0 +1,22 @@
+.search-results-status
+ .row-content-block.gl-display-flex
+ .gl-md-display-flex.gl-text-left.gl-align-items-center.gl-flex-grow-1
+ - unless search_service.without_count?
+ = search_entries_info(search_service.search_objects, search_service.scope, params[:search])
+ - unless search_service.show_snippets?
+ - if search_service.project
+ - link_to_project = link_to(search_service.project.full_name, search_service.project, class: 'ml-md-1')
+ - if search_service.scope == 'blobs'
+ = _("in")
+ .mx-md-1
+ #js-blob-ref-switcher{ data: { "project-id" => search_service.project.id, "ref" => repository_ref(search_service.project), "field-name": "repository_ref" } }
+ = s_('SearchCodeResults|of %{link_to_project}').html_safe % { link_to_project: link_to_project }
+ - else
+ = _("in project %{link_to_project}").html_safe % { link_to_project: link_to_project }
+ - elsif search_service.group
+ - link_to_group = link_to(search_service.group.name, search_service.group, class: 'ml-md-1')
+ = _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group }
+ - if search_service.show_sort_dropdown?
+ .gl-md-display-flex.gl-flex-direction-column
+ #js-search-sort{ data: { "search-sort-options" => search_sort_options.to_json } }
+
diff --git a/app/views/search/_results_status_vert_nav.html.haml b/app/views/search/_results_status_vert_nav.html.haml
new file mode 100644
index 00000000000..03916911f43
--- /dev/null
+++ b/app/views/search/_results_status_vert_nav.html.haml
@@ -0,0 +1,23 @@
+.search-results-status
+ .gl-display-flex.gl-flex-direction-column
+ .gl-p-5.gl-display-flex
+ .gl-md-display-flex.gl-text-left.gl-align-items-center.gl-flex-grow-1
+ - unless search_service.without_count?
+ = search_entries_info(search_service.search_objects, search_service.scope, params[:search])
+ - unless search_service.show_snippets?
+ - if search_service.project
+ - link_to_project = link_to(search_service.project.full_name, search_service.project, class: 'ml-md-1')
+ - if search_service.scope == 'blobs'
+ = _("in")
+ .mx-md-1
+ #js-blob-ref-switcher{ data: { "project-id" => search_service.project.id, "ref" => repository_ref(search_service.project), "field-name": "repository_ref" } }
+ = s_('SearchCodeResults|of %{link_to_project}').html_safe % { link_to_project: link_to_project }
+ - else
+ = _("in project %{link_to_project}").html_safe % { link_to_project: link_to_project }
+ - elsif search_service.group
+ - link_to_group = link_to(search_service.group.name, search_service.group, class: 'ml-md-1')
+ = _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group }
+ - if search_service.show_sort_dropdown?
+ .gl-md-display-flex.gl-flex-direction-column
+ #js-search-sort{ data: { "search-sort-options" => search_sort_options.to_json } }
+ %hr.gl-mb-5.gl-mt-0.gl-border-gray-100.gl-w-full
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index 5a45e512579..9d812e77ad4 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -22,5 +22,6 @@
.gl-mt-3
#js-search-topbar{ data: { "group-initial-data": group_attributes.to_json, "project-initial-data": project_attributes.to_json } }
- if @search_term
- = render 'search/category'
+ - if Feature.disabled?(:search_page_vertical_nav, current_user)
+ = render 'search/category'
= render 'search/results'
diff --git a/app/workers/concerns/gitlab/github_import/stage_methods.rb b/app/workers/concerns/gitlab/github_import/stage_methods.rb
index b12c2311ea8..1feaaf917b2 100644
--- a/app/workers/concerns/gitlab/github_import/stage_methods.rb
+++ b/app/workers/concerns/gitlab/github_import/stage_methods.rb
@@ -63,6 +63,10 @@ module Gitlab
import_stage: self.class.name
)
end
+
+ def import_settings(project)
+ Gitlab::GithubImport::Settings.new(project)
+ end
end
end
end
diff --git a/app/workers/gitlab/github_import/stage/import_attachments_worker.rb b/app/workers/gitlab/github_import/stage/import_attachments_worker.rb
index 727de632df3..00901548514 100644
--- a/app/workers/gitlab/github_import/stage/import_attachments_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_attachments_worker.rb
@@ -53,7 +53,7 @@ module Gitlab
end
def feature_disabled?(project)
- Feature.disabled?(:github_importer_attachments_import, project.group, type: :ops)
+ import_settings(project).disabled?(:attachments_import)
end
end
end
diff --git a/app/workers/gitlab/github_import/stage/import_issue_events_worker.rb b/app/workers/gitlab/github_import/stage/import_issue_events_worker.rb
index 0ec0a1b58d2..54ed4c47e78 100644
--- a/app/workers/gitlab/github_import/stage/import_issue_events_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_issue_events_worker.rb
@@ -15,9 +15,9 @@ module Gitlab
# client - An instance of Gitlab::GithubImport::Client.
# project - An instance of Project.
def import(client, project)
- importer = importer_class(project)
- return skip_to_next_stage(project) if importer.nil?
+ return skip_to_next_stage(project) if import_settings(project).disabled?(:single_endpoint_issue_events_import)
+ importer = ::Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
info(project.id, message: "starting importer", importer: importer.name)
waiter = importer.new(project, client).execute
move_to_next_stage(project, { waiter.key => waiter.jobs_remaining })
@@ -25,16 +25,6 @@ module Gitlab
private
- def importer_class(project)
- if Feature.enabled?(:github_importer_single_endpoint_issue_events_import, project.group, type: :ops)
- ::Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
- elsif Feature.enabled?(:github_importer_issue_events_import, project.group, type: :ops)
- ::Gitlab::GithubImport::Importer::IssueEventsImporter
- else
- nil
- end
- end
-
def skip_to_next_stage(project)
info(project.id, message: "skipping importer", importer: "IssueEventsImporter")
move_to_next_stage(project)
diff --git a/app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb b/app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb
index 7922c1113c4..3d1a8437da2 100644
--- a/app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb
@@ -36,7 +36,7 @@ module Gitlab
private
def diff_notes_importer(project)
- if project.group.present? && Feature.enabled?(:github_importer_single_endpoint_notes_import, project.group, type: :ops)
+ if import_settings(project).enabled?(:single_endpoint_notes_import)
Importer::SingleEndpointDiffNotesImporter
else
Importer::DiffNotesImporter
diff --git a/app/workers/gitlab/github_import/stage/import_notes_worker.rb b/app/workers/gitlab/github_import/stage/import_notes_worker.rb
index b53e31ce40e..40ca12b130f 100644
--- a/app/workers/gitlab/github_import/stage/import_notes_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_notes_worker.rb
@@ -25,7 +25,7 @@ module Gitlab
end
def importers(project)
- if project.group.present? && Feature.enabled?(:github_importer_single_endpoint_notes_import, project.group, type: :ops)
+ if import_settings(project).enabled?(:single_endpoint_notes_import)
[
Importer::SingleEndpointMergeRequestNotesImporter,
Importer::SingleEndpointIssueNotesImporter
diff --git a/config/feature_flags/development/search_page_vertical_nav.yml b/config/feature_flags/development/search_page_vertical_nav.yml
index d33c80e5f2f..58088cee802 100644
--- a/config/feature_flags/development/search_page_vertical_nav.yml
+++ b/config/feature_flags/development/search_page_vertical_nav.yml
@@ -1,7 +1,7 @@
---
name: search_page_vertical_nav
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97784
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342621
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373613
milestone: '15.5'
type: development
group: group::global search
diff --git a/config/feature_flags/ops/github_importer_attachments_import.yml b/config/feature_flags/ops/github_importer_attachments_import.yml
deleted file mode 100644
index ec4fe144933..00000000000
--- a/config/feature_flags/ops/github_importer_attachments_import.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: github_importer_attachments_import
-introduced_by_url:
-rollout_issue_url:
-milestone: '15.4'
-type: ops
-group: group::import
-default_enabled: false
diff --git a/config/feature_flags/ops/github_importer_issue_events_import.yml b/config/feature_flags/ops/github_importer_issue_events_import.yml
deleted file mode 100644
index 58660ceb287..00000000000
--- a/config/feature_flags/ops/github_importer_issue_events_import.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: github_importer_issue_events_import
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89134
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365977
-milestone: '15.3'
-type: ops
-group: group::import
-default_enabled: false
diff --git a/config/feature_flags/ops/github_importer_single_endpoint_issue_events_import.yml b/config/feature_flags/ops/github_importer_single_endpoint_issue_events_import.yml
deleted file mode 100644
index 88e9db6721f..00000000000
--- a/config/feature_flags/ops/github_importer_single_endpoint_issue_events_import.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: github_importer_single_endpoint_issue_events_import
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89134
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365977
-milestone: '15.3'
-type: ops
-group: group::import
-default_enabled: false
diff --git a/config/feature_flags/ops/github_importer_single_endpoint_notes_import.yml b/config/feature_flags/ops/github_importer_single_endpoint_notes_import.yml
deleted file mode 100644
index 7bbc6fba9e0..00000000000
--- a/config/feature_flags/ops/github_importer_single_endpoint_notes_import.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: github_importer_single_endpoint_notes_import
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67150
-rollout_issue_url:
-milestone: '14.2'
-type: ops
-group: group::import
-default_enabled: false
diff --git a/config/routes.rb b/config/routes.rb
index f5e398aa986..28c08e9bbe7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -136,28 +136,6 @@ InitializerConnections.with_disabled_database_connections do
get 'runner_setup/platforms' => 'runner_setup#platforms'
- # Boards resources shared between group and projects
- resources :boards, only: [] do
- resources :lists, module: :boards, only: [:index, :create, :update, :destroy] do
- collection do
- post :generate
- end
-
- resources :issues, only: [:index, :create, :update]
- end
-
- resources :issues, module: :boards, only: [:index, :update] do
- collection do
- put :bulk_move, format: :json
- end
- end
-
- Gitlab.ee do
- resources :users, module: :boards, only: [:index]
- resources :milestones, module: :boards, only: [:index]
- end
- end
-
get 'acme-challenge/' => 'acme_challenges#show'
scope :ide, as: :ide, format: false do
diff --git a/doc/administration/geo/replication/version_specific_upgrades.md b/doc/administration/geo/replication/version_specific_upgrades.md
index a7ae3acc82e..9aad5cdeaa7 100644
--- a/doc/administration/geo/replication/version_specific_upgrades.md
+++ b/doc/administration/geo/replication/version_specific_upgrades.md
@@ -10,6 +10,12 @@ Review this page for upgrade instructions for your version. These steps
accompany the [general steps](upgrading_the_geo_sites.md#general-upgrade-steps)
for upgrading Geo sites.
+## Upgrading to 15.1
+
+[Geo proxying](../secondary_proxy/index.md) was [enabled by default for different URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/346112) in 15.1. This may be a breaking change. If needed, you may [disable Geo proxying](../secondary_proxy/index.md#disable-geo-proxying).
+
+If you are using SAML with different URLs, there is a [known issue which requires proxying to be disabled](https://gitlab.com/gitlab-org/gitlab/-/issues/377372).
+
## Upgrading to 14.9
**Do not** upgrade to GitLab 14.9.0. Instead, use 14.9.1 or later.
@@ -33,6 +39,10 @@ results in a loop that consistently fails for all objects stored in object stora
For information on how to fix this, see
[Troubleshooting - Failed syncs with GitLab-managed object storage replication](troubleshooting.md#failed-syncs-with-gitlab-managed-object-storage-replication).
+## Upgrading to 14.6
+
+[Geo proxying](../secondary_proxy/index.md) was [enabled by default for unified URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/325732) in 14.6. This may be a breaking change. If needed, you may [disable Geo proxying](../secondary_proxy/index.md#disable-geo-proxying).
+
## Upgrading to 14.4
There is [an issue in GitLab 14.4.0 through 14.4.2](../../../update/index.md#1440) that can affect Geo and other features that rely on cronjobs. We recommend upgrading to GitLab 14.4.3 or later.
diff --git a/doc/administration/integration/mailgun.md b/doc/administration/integration/mailgun.md
index 612b5b08e8a..baf9e8c8a3b 100644
--- a/doc/administration/integration/mailgun.md
+++ b/doc/administration/integration/mailgun.md
@@ -45,7 +45,7 @@ you're ready to enable the Mailgun integration:
1. Sign in to GitLab as an [Administrator](../../user/permissions.md) user.
1. On the top bar, select **Main menu >** **{admin}** **Admin**.
1. On the left sidebar, go to **Settings > General** and expand the **Mailgun** section.
-1. Select the **Enable Mailgun** check box.
+1. Select the **Enable Mailgun** checkbox.
1. Enter the Mailgun HTTP webhook signing key as described in
[the Mailgun documentation](https://documentation.mailgun.com/en/latest/user_manual.html#webhooks-1) and
shown in the [API security](https://app.mailgun.com/app/account/security/api_keys) section for your Mailgun account.
diff --git a/doc/api/import.md b/doc/api/import.md
index 9d9c00fb402..2ef04dc597f 100644
--- a/doc/api/import.md
+++ b/doc/api/import.md
@@ -14,13 +14,14 @@ Import your projects from GitHub to GitLab via the API.
POST /import/github
```
-| Attribute | Type | Required | Description |
-|------------|---------|----------|---------------------|
-| `personal_access_token` | string | yes | GitHub personal access token |
-| `repo_id` | integer | yes | GitHub repository ID |
-| `new_name` | string | no | New repository name |
-| `target_namespace` | string | yes | Namespace to import repository into. Supports subgroups like `/namespace/subgroup`. |
-| `github_hostname` | string | no | Custom GitHub Enterprise hostname. Do not set for GitHub.com. |
+| Attribute | Type | Required | Description |
+|-------------------------|---------|----------|-------------------------------------------------------------------------------------|
+| `personal_access_token` | string | yes | GitHub personal access token |
+| `repo_id` | integer | yes | GitHub repository ID |
+| `new_name` | string | no | New repository name |
+| `target_namespace` | string | yes | Namespace to import repository into. Supports subgroups like `/namespace/subgroup`. |
+| `github_hostname` | string | no | Custom GitHub Enterprise hostname. Do not set for GitHub.com. |
+| `optional_stages` | object | no | [Additional items to import](../user/project/import/github.md#select-additional-items-to-import)|
```shell
curl --request POST \
@@ -32,10 +33,23 @@ curl --request POST \
"repo_id": "12345",
"target_namespace": "group/subgroup",
"new_name": "NEW-NAME",
- "github_hostname": "https://github.example.com"
+ "github_hostname": "https://github.example.com",
+ "optional_stages": {
+ "single_endpoint_issue_events_import": true,
+ "single_endpoint_notes_import": true,
+ "attachments_import": true
+ }
}'
```
+The following keys are available for `optional_stages`:
+
+- `single_endpoint_issue_events_import`, for issue and pull request events import.
+- `single_endpoint_notes_import`, for an alternative and more thorough comments import.
+- `attachments_import`, for Markdown attachments import.
+
+For more information, see [Select additional items to import](../user/project/import/github.md#select-additional-items-to-import).
+
Example response:
```json
diff --git a/doc/ci/pipelines/merge_trains.md b/doc/ci/pipelines/merge_trains.md
index ac31a62c9f1..c501d2a7904 100644
--- a/doc/ci/pipelines/merge_trains.md
+++ b/doc/ci/pipelines/merge_trains.md
@@ -91,7 +91,7 @@ In GitLab 13.5 and earlier, there is only one checkbox, named
**Enable merge trains and pipelines for merged results**.
WARNING:
-If you select the check box but don't configure your CI/CD to use
+If you select the checkbox but don't configure your CI/CD to use
merge request pipelines, your merge requests may become stuck in an
unresolved state or your pipelines may be dropped.
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 6d919b486d5..3bde903b8d2 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -75,9 +75,9 @@ NOTE:
Reviewer roulette is an internal tool for use on GitLab.com, and not available for use on customer installations.
The [Danger bot](dangerbot.md) randomly picks a reviewer and a maintainer for
-each area of the codebase that your merge request seems to touch. It only makes
-**recommendations** and you should override it if you think someone else is a better
-fit!
+each area of the codebase that your merge request seems to touch. It makes
+**recommendations** for developer reviewers and you should override it if you think someone else is a better
+fit. User-facing changes are required to have a UX review, too. Default to the recommended UX reviewer suggested.
It picks reviewers and maintainers from the list at the
[engineering projects](https://about.gitlab.com/handbook/engineering/projects/)
diff --git a/doc/development/database/table_partitioning.md b/doc/development/database/table_partitioning.md
index 2350d5d0e9e..bf12329473d 100644
--- a/doc/development/database/table_partitioning.md
+++ b/doc/development/database/table_partitioning.md
@@ -376,22 +376,28 @@ Enforce foreign keys including the partitioning key column. For example, in a ra
class PrepareForeignKeyForPartitioning < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
- REFERENCED_TABLE_NAME = :references_table_name
- FOREIGN_KEY_COLUMN = :foreign_key_id
- FOREIGN_KEY_NAME = :fk_365d1db505_p
- TABLE_NAME = :table_name
+ SOURCE_TABLE_NAME = :source_table_name
+ TARGET_TABLE_NAME = :target_table_name
+ COLUMN = :foreign_key_id
+ TARGET_COLUMN = :id
+ CONSTRAINT_NAME = :fk_365d1db505_p
PARTITION_COLUMN = :partition_id
def up
- execute("ALTER TABLE #{REFERENCED_TABLE_NAME} ADD CONSTRAINT #{FOREIGN_KEY_NAME} " \
- "FOREIGN KEY (#{FOREIGN_KEY_COLUMN}, #{PARTITION_COLUMN}) " \
- "REFERENCES #{TABLE_NAME}(id, #{PARTITION_COLUMN}) ON DELETE CASCADE NOT VALID")
+ add_concurrent_foreign_key(
+ SOURCE_TABLE_NAME,
+ TARGET_TABLE_NAME,
+ column: [PARTITION_COLUMN, COLUMN],
+ target_column: [PARTITION_COLUMN, TARGET_COLUMN],
+ validate: false
+ name: CONSTRAINT_NAME
+ )
- execute("ALTER TABLE #{TABLE_NAME} VALIDATE CONSTRAINT #{FOREIGN_KEY_NAME}")
+ validate_foreign_key(TARGET_TABLE_NAME, CONSTRAINT_NAME)
end
def down
- execute("ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT #{FOREIGN_KEY_NAME}")
+ drop_constraint(TARGET_TABLE_NAME, CONSTRAINT_NAME)
end
end
```
diff --git a/doc/development/github_importer.md b/doc/development/github_importer.md
index 6095c9c7cf6..ae8a47b0d3a 100644
--- a/doc/development/github_importer.md
+++ b/doc/development/github_importer.md
@@ -114,6 +114,9 @@ GitHub are stored in a single table. Therefore, they have globally-unique IDs an
Therefore, both issues and pull requests have a common API for most related things.
+NOTE:
+This stage is optional and can consume significant extra import time (controlled by `Gitlab::GithubImport::Settings`).
+
### 9. Stage::ImportNotesWorker
This worker imports regular comments for both issues and pull requests. For
@@ -139,6 +142,9 @@ Each job:
1. Downloads the attachment.
1. Replaces the old link with a newly-generated link to GitLab.
+NOTE:
+It's an optional stage that could consume significant extra import time (controlled by `Gitlab::GithubImport::Settings`).
+
### 11. Stage::ImportProtectedBranchesWorker
This worker imports protected branch rules.
diff --git a/doc/user/admin_area/settings/deprecated_api_rate_limits.md b/doc/user/admin_area/settings/deprecated_api_rate_limits.md
index 3a1f72b9b39..279cac95fc9 100644
--- a/doc/user/admin_area/settings/deprecated_api_rate_limits.md
+++ b/doc/user/admin_area/settings/deprecated_api_rate_limits.md
@@ -37,7 +37,7 @@ To override the general user and IP rate limits for requests to deprecated API e
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Deprecated API Rate Limits**.
-1. Select the check boxes for the types of rate limits you want to enable:
+1. Select the checkboxes for the types of rate limits you want to enable:
- **Unauthenticated API request rate limit**
- **Authenticated API request rate limit**
1. _If you enabled unauthenticated API request rate limits:_
diff --git a/doc/user/admin_area/settings/files_api_rate_limits.md b/doc/user/admin_area/settings/files_api_rate_limits.md
index 35ede209c71..e5f5064304c 100644
--- a/doc/user/admin_area/settings/files_api_rate_limits.md
+++ b/doc/user/admin_area/settings/files_api_rate_limits.md
@@ -33,7 +33,7 @@ To override the general user and IP rate limits for requests to the Repository f
1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Files API Rate Limits**.
-1. Select the check boxes for the types of rate limits you want to enable:
+1. Select the checkboxes for the types of rate limits you want to enable:
- **Unauthenticated API request rate limit**
- **Authenticated API request rate limit**
1. _If you enabled unauthenticated API request rate limits:_
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 819eaeea43e..f6ae8ff72fa 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -187,7 +187,12 @@ table.supported-languages ul {
<td>Go</td>
<td>All versions</td>
<td><a href="https://go.dev/">Go</a></td>
- <td><code>go.sum</code></td>
+ <td>
+ <ul>
+ <li><code>go.mod</code></li>
+ <li><code>go.sum</code></li>
+ </ul>
+ </td>
<td>Y</td>
</tr>
<tr>
@@ -353,12 +358,24 @@ The following package managers use lockfiles that GitLab analyzers are capable o
| Bundler | Not applicable | [1.17.3](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/ruby-bundler/default/Gemfile.lock#L118), [2.1.4](https://gitlab.com/gitlab-org/security-products/tests/ruby-bundler/-/blob/bundler2-FREEZE/Gemfile.lock#L118) |
| Composer | Not applicable | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/php-composer/default/composer.lock) |
| Conan | 0.4 | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/c-conan/default/conan.lock) |
-| Go | Not applicable | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/go-modules/default/go.sum) |
+| Go | Not applicable | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/go-modules/default/go.sum) <sup><strong><a href="#notes-regarding-parsing-lockfiles-1">1</a></strong></sup> |
| NuGet | v1 | [4.9](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/csharp-nuget-dotnetcore/default/src/web.api/packages.lock.json#L2) |
| npm | v1, v2 | [6.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-npm/default/package-lock.json#L4), [7.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-npm/lockfileVersion2/package-lock.json#L4) |
| yarn | v1 | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-yarn/default/yarn.lock#L2) |
| Poetry | v1 | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-python/-/blob/v3/qa/fixtures/python-poetry/default/poetry.lock) |
+<!-- markdownlint-disable MD044 -->
+<ol>
+ <li>
+ <a id="notes-regarding-parsing-lockfiles-1"></a>
+ <p>
+ Dependency Scanning will only parse <code>go.sum</code> if it's unable to generate the build list
+ used by the Go project.
+ </p>
+ </li>
+</ol>
+<!-- markdownlint-enable MD044 -->
+
#### Obtaining dependency information by running a package manager to generate a parsable file
To support the following package managers, the GitLab analyzers proceed in two steps:
@@ -374,6 +391,7 @@ To support the following package managers, the GitLab analyzers proceed in two s
| setuptools | [50.3.2](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/v2.29.9/Dockerfile#L27) | [57.5.0](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-python/-/blob/v2.22.0/spec/image_spec.rb#L224-247) |
| pip | [20.2.4](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/v2.29.9/Dockerfile#L26) | [20.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-python/-/blob/v2.22.0/spec/image_spec.rb#L77-91) |
| Pipenv | [2018.11.26](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-python/-/blob/v2.18.4/requirements.txt#L13) | [2018.11.26](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-python/-/blob/v2.22.0/spec/image_spec.rb#L168-191)<sup><b><a href="#exported-dependency-information-notes-3">3</a></b></sup>, [2018.11.26](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-python/-/blob/v2.22.0/spec/image_spec.rb#L143-166) |
+| Go | [1.17](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/7dc7a892b564abfcb160189f46b2ae6415e0dffa/build/gemnasium/alpine/Dockerfile#L88-91) | [1.17](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/7dc7a892b564abfcb160189f46b2ae6415e0dffa/build/gemnasium/alpine/Dockerfile#L88-91)<sup><strong><a href="#exported-dependency-information-notes-4">4</a></strong></sup> |
<!-- markdownlint-disable MD044 -->
<ol>
@@ -416,6 +434,13 @@ To support the following package managers, the GitLab analyzers proceed in two s
This test confirms that if a <code>Pipfile.lock</code> file is found, it will be used by <a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium">Gemnasium</a> to scan the exact package versions listed in this file.
</p>
</li>
+ <li>
+ <a id="exported-dependency-information-notes-4"></a>
+ <p>
+ Because of the implementation of <code>go build</code>, the Go build process requires network access, a pre-loaded modcache via <code>go mod download</code>, or vendored dependencies. For more information,
+ refer to the Go documentation on <a href="https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies">compiling packages and dependencies</a>.
+ </p>
+ </li>
</ol>
<!-- markdownlint-enable MD044 -->
@@ -474,6 +499,12 @@ The following analyzers are executed, each of which have different behavior when
From GitLab 14.8 the `gemnasium` analyzer scans supported JavaScript projects for vendored libraries
(that is, those checked into the project but not managed by the package manager).
+#### Go
+
+When scanning a Go project, gemnasium invokes a builder and attempts to generate a [build list](https://go.dev/ref/mod#glos-build-list) using
+[Minimal Version Selection](https://go.dev/ref/mod#glos-minimal-version-selection). If a non-fatal error is encountered, the build process signals
+that the execution should proceed and falls back to parsing the available `go.sum` file.
+
#### PHP, Go, C, C++, .NET, C&#35;, Ruby, JavaScript
The analyzer for these languages supports multiple lockfiles.
@@ -621,6 +652,10 @@ The following variables are used for configuring specific analyzers (used for a
| `DS_PIP_VERSION` | `gemnasium-python` | | Force the install of a specific pip version (example: `"19.3"`), otherwise the pip installed in the Docker image is used. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12811) in GitLab 12.7) |
| `DS_PIP_DEPENDENCY_PATH` | `gemnasium-python` | | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12412) in GitLab 12.2) |
| `DS_INCLUDE_DEV_DEPENDENCIES` | `gemnasium` | `"true"` | When set to `"false"`, development dependencies and their vulnerabilities are not reported. Only NPM and Poetry projects are supported. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227861) in GitLab 15.1. |
+| `GOOS` | `gemnasium` | `"linux"` | The operating system for which to compile Go code. |
+| `GOARCH` | `gemnasium` | `"amd64"` | The architecture of the processor for which to compile Go code. |
+| `GOFLAGS` | `gemansium` | | The flags passed to the `go build` tool. |
+| `GOPRIVATE` | `gemnasium` | | A list of glob patterns and prefixes to be fetched from source. Read the Go private modules [documentation](https://go.dev/ref/mod#private-modules) for more information. |
#### Other variables
@@ -1279,3 +1314,40 @@ gemnasium-python-dependency_scanning:
### Error: Project has `<number>` unresolved dependencies
The error message `Project has <number> unresolved dependencies` indicates a dependency resolution problem caused by your `gradle.build` or `gradle.build.kts` file. In the current release, `gemnasium-maven` cannot continue processing when an unresolved dependency is encountered. However, There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/337083) to allow `gemnasium-maven` to recover from unresolved dependency errors and produce a dependency graph. Until this issue has been resolved, you'll need to consult the [Gradle dependency resolution docs](https://docs.gradle.org/current/userguide/dependency_resolution.html) for details on how to fix your `gradle.build` file.
+
+### Setting build constraints when scanning Go projects
+
+Dependency scanning runs within a `linux/amd64` container. As a result, the build list generated
+for a Go project will contain dependencies that are compatible with this environment. If your deployment environment is not
+`linux/amd64`, the final list of dependencies might contain additional incompatible
+modules. The dependency list might also omit modules that are only compatible with your deployment environment. To prevent
+this issue, you can configure the build process to target the operating system and architecture of the deployment
+environment by setting the `GOOS` and `GOARCH` [environment variables](https://go.dev/ref/mod#minimal-version-selection)
+of your `.gitlab-ci.yml` file.
+
+For example:
+
+```yaml
+variables:
+ GOOS: "darwin"
+ GOARCH: "arm64"
+```
+
+You can also supply build tag constraints by using the `GOFLAGS` variable:
+
+```yaml
+variables:
+ GOFLAGS: "-tags=test_feature"
+```
+
+### Dependency Scanning of Go projects returns false positives
+
+The `go.sum` file contains an entry of every module that was considered while generating the project's [build list](https://go.dev/ref/mod#glos-build-list).
+Multiple versions of a module are included in the `go.sum` file, but the [MVS](https://go.dev/ref/mod#minimal-version-selection)
+algorithm used by `go build` only selects one. As a result, when dependency scanning uses `go.sum`, it might report false positives.
+
+To prevent false positives, gemnasium only uses `go.sum` if it is unable to generate the build list for the Go project. If `go.sum` is selected, a warning occurs:
+
+```shell
+[WARN] [Gemnasium] [2022-09-14T20:59:38Z] â–¶ Selecting "go.sum" parser for "/test-projects/gitlab-shell/go.sum". False positives may occur. See https://gitlab.com/gitlab-org/gitlab/-/issues/321081.
+```
diff --git a/doc/user/application_security/policies/scan-execution-policies.md b/doc/user/application_security/policies/scan-execution-policies.md
index 1d698f8eb1e..41d25dfa8c8 100644
--- a/doc/user/application_security/policies/scan-execution-policies.md
+++ b/doc/user/application_security/policies/scan-execution-policies.md
@@ -88,6 +88,7 @@ This rule enforces the defined actions and schedules a scan on the provided date
| `type` | `string` | `schedule` | The rule's type. |
| `branches` | `array` of `string` | `*` or the branch's name | The branch the given policy applies to (supports wildcard). |
| `cadence` | `string` | CRON expression (for example, `0 0 * * *`) | A whitespace-separated string containing five fields that represents the scheduled time. |
+| `agents` | `object` | | The name of the [GitLab agents](../../clusters/agent/index.md) where [cluster image scanning](../../clusters/agent/vulnerabilities.md) will run. The object key is the name of the Kubernetes cluster configured for your project in GitLab. You can use the optional value of the object to select and scan specific Kubernetes resources. |
GitLab supports the following types of CRON syntax for the `cadence` field:
@@ -96,6 +97,31 @@ GitLab supports the following types of CRON syntax for the `cadence` field:
Other elements of the CRON syntax may work in the cadence field, however, GitLab does not officially test or support them. The CRON expression is evaluated in UTC by default. If you have a self-managed GitLab instance and have [changed the server timezone](../../../administration/timezone.md), the CRON expression is evaluated with the new timezone.
+### `agent` schema
+
+Use this schema to define `agents` objects in the [`schedule` rule type](#schedule-rule-type).
+
+| Field | Type | Possible values | Description |
+|--------------|---------------------|--------------------------|-------------|
+| `namespaces` | `array` of `string` | | The namespace that is scanned. If empty, all namespaces will be scanned. |
+
+#### Policy example
+
+```yaml
+- name: Enforce Container Scanning in cluster connected through gitlab-agent for production and staging namespaces
+ enabled: true
+ rules:
+ - type: schedule
+ cadence: '0 10 * * *'
+ agents:
+ gitlab-agent:
+ namespaces:
+ - 'production'
+ - 'staging'
+ actions:
+ - scan: container_scanning
+```
+
## `scan` action type
This action executes the selected `scan` with additional parameters when conditions for at least one
@@ -124,9 +150,8 @@ Note the following:
- A secret detection scan runs in `normal` mode when executed as part of a pipeline, and in
[`historic`](../secret_detection/index.md#full-history-secret-detection)
mode when executed as part of a scheduled scan.
-- A container scanning scan configured for the `pipeline` rule type ignores the cluster defined in the `clusters` object.
- They use predefined CI/CD variables defined for your project. Cluster selection with the `clusters` object is supported for the `schedule` rule type.
- A cluster with a name provided in the `clusters` object must be created and configured for the project.
+- A container scanning scan that is configured for the `pipeline` rule type ignores the agent defined in the `agents` object. The `agents` object is only considered for `schedule` rule types.
+ An agent with a name provided in the `agents` object must be created and configured for the project.
- The SAST scan uses the default template and runs in a [child pipeline](../../../ci/pipelines/downstream_pipelines.md#parent-child-pipelines).
## Example security policies project
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index 0fc49798a15..c4e058b5ecf 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -118,6 +118,21 @@ If you are not using the GitHub integration, you can still perform an authorizat
To use a newer personal access token in imports after previously performing these steps, sign out of
your GitLab account and sign in again, or revoke the older personal access token in GitHub.
+### Select additional items to import
+
+To make imports as fast as possible, the following items aren't imported from GitHub by default:
+
+- Issue and pull request events. For example, _opened_ or _closed_, _renamed_, and _labeled_ or _unlabeled_.
+- All comments. In regular import of large repositories some comments might get skipped due to limitation of GitHub API.
+- Markdown attachments from repository comments, release posts, issue descriptions, and pull request descriptions. These can include
+ images, text, or binary attachments. If not imported, links in Markdown to attachments break after you remove the attachments from GitHub.
+
+You can choose to import these items, but this could significantly increase import time. To import these items, select the appropriate fields in the UI:
+
+- **Import issue and pull request events**.
+- **Use alternative comments import method**.
+- **Import Markdown attachments**.
+
### Select which repositories to import
After you have authorized access to your GitHub repositories, you are redirected to the GitHub importer page and
@@ -182,9 +197,9 @@ The following items of a project are imported:
- Release notes. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15620) in GitLab 15.4.
- Comments and notes. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18052) in GitLab 15.5.
- NOTE: All attachment importers work under `github_importer_attachments_import` [feature flag](../../../administration/feature_flags.md) disabled by default.
-- Release note attachments. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15620) in GitLab 15.4 with `github_importer_attachments_import`
- [feature flag](../../../administration/feature_flags.md) disabled by default.
+ All attachment imports are disabled by default behind
+ `github_importer_attachments_import` [feature flag](../../../administration/feature_flags.md). From GitLab 15.5, can be imported
+ [as an additional item](#select-additional-items-to-import). The feature flag was removed.
- Pull request review comments.
- Regular issue and pull request comments.
- [Git Large File Storage (LFS) Objects](../../../topics/git/lfs/index.md).
@@ -194,6 +209,7 @@ The following items of a project are imported:
- Diff Notes suggestions ([GitLab.com and GitLab 14.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/340624)).
- Issue events and pull requests events. [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/7673) in GitLab 15.4 with `github_importer_issue_events_import`
[feature flag](../../../administration/feature_flags.md) disabled by default.
+ From GitLab 15.5, can be imported [as an additional item](#select-additional-items-to-import). The feature flag was removed.
References to pull requests and issues are preserved. Each imported repository maintains visibility level unless that
[visibility level is restricted](../../public_access.md#restrict-use-of-public-or-internal-projects), in which case it
@@ -214,25 +230,11 @@ You can still create [status checks](../merge_requests/status_checks.md) in GitL
When GitHub Importer runs on extremely large projects not all notes & diff notes can be imported due to GitHub API `issues_comments` & `pull_requests_comments` endpoints limitation.
Not all pages can be fetched due to the following error coming from GitHub API: `In order to keep the API fast for everyone, pagination is limited for this resource. Check the rel=last link relation in the Link response header to see how far back you can traverse.`.
-An alternative approach for importing notes and diff notes is available behind a feature flag.
+An [alternative approach](#select-additional-items-to-import) for importing comments is available.
Instead of using `issues_comments` and `pull_requests_comments`, use individual resources `issue_comments` and `pull_request_comments` instead to pull notes from one object at a time.
This allows us to carry over any missing comments, however it increases the number of network requests required to perform the import, which means its execution takes a longer time.
-To use the alternative way of importing notes, the `github_importer_single_endpoint_notes_import` feature flag must be enabled on the group project is being imported into.
-
-Start a [Rails console](../../../administration/operations/rails_console.md#starting-a-rails-console-session).
-
-```ruby
-group = Group.find_by_full_path('my/group/fullpath')
-
-# Enable
-Feature.enable(:github_importer_single_endpoint_notes_import, group)
-
-# Disable
-Feature.disable(:github_importer_single_endpoint_notes_import, group)
-```
-
## Reduce GitHub API request objects per page
Some GitHub API endpoints may return a 500 or 502 error for project imports from large repositories.
diff --git a/doc/user/project/integrations/webhook_events.md b/doc/user/project/integrations/webhook_events.md
index bd5fcde71b3..c0f0f5a0cd4 100644
--- a/doc/user/project/integrations/webhook_events.md
+++ b/doc/user/project/integrations/webhook_events.md
@@ -1100,6 +1100,7 @@ Payload example:
"object_kind": "pipeline",
"object_attributes":{
"id": 31,
+ "iid": 3,
"ref": "master",
"tag": false,
"sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
diff --git a/lib/api/import_github.rb b/lib/api/import_github.rb
index d91df47cd16..493cc038f46 100644
--- a/lib/api/import_github.rb
+++ b/lib/api/import_github.rb
@@ -43,6 +43,7 @@ module API
optional :new_name, type: String, desc: 'New repo name'
requires :target_namespace, type: String, desc: 'Namespace to import repo into'
optional :github_hostname, type: String, desc: 'Custom GitHub enterprise hostname'
+ optional :optional_stages, type: Hash, desc: 'Optional stages of import to be performed'
end
post 'import/github' do
result = Import::GithubService.new(client, current_user, params).execute(access_params, provider)
diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb
index 320ebe5e80f..a75c7c539ae 100644
--- a/lib/gitlab/data_builder/pipeline.rb
+++ b/lib/gitlab/data_builder/pipeline.rb
@@ -63,6 +63,7 @@ module Gitlab
def hook_attrs(pipeline)
{
id: pipeline.id,
+ iid: pipeline.iid,
ref: pipeline.source_ref,
tag: pipeline.tag,
sha: pipeline.sha,
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 0568e734c78..df40e3b3868 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -296,12 +296,11 @@ module Gitlab
with_lock_retries do
execute("LOCK TABLE #{target}, #{source} IN SHARE ROW EXCLUSIVE MODE") if reverse_lock_order
-
execute <<-EOF.strip_heredoc
ALTER TABLE #{source}
ADD CONSTRAINT #{options[:name]}
- FOREIGN KEY (#{options[:column]})
- REFERENCES #{target} (#{target_column})
+ FOREIGN KEY (#{multiple_columns(options[:column])})
+ REFERENCES #{target} (#{multiple_columns(target_column)})
#{on_delete_statement(options[:on_delete])}
NOT VALID;
EOF
@@ -355,7 +354,7 @@ module Gitlab
# - For standard rails foreign keys the prefix is `fk_rails_`
#
def concurrent_foreign_key_name(table, column, prefix: 'fk_')
- identifier = "#{table}_#{column}_fk"
+ identifier = "#{table}_#{multiple_columns(column, separator: '_')}_fk"
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
"#{prefix}#{hashed_identifier}"
@@ -1539,6 +1538,10 @@ into similar problems in the future (e.g. when new tables are created).
private
+ def multiple_columns(columns, separator: ', ')
+ Array.wrap(columns).join(separator)
+ end
+
def cascade_statement(cascade)
cascade ? 'CASCADE' : ''
end
diff --git a/lib/gitlab/github_import/issuable_finder.rb b/lib/gitlab/github_import/issuable_finder.rb
index e7a1b7b3368..b960df581e4 100644
--- a/lib/gitlab/github_import/issuable_finder.rb
+++ b/lib/gitlab/github_import/issuable_finder.rb
@@ -80,12 +80,16 @@ module Gitlab
end
def timeout
- if project.group.present? && ::Feature.enabled?(:github_importer_single_endpoint_notes_import, project.group, type: :ops)
+ if import_settings.enabled?(:single_endpoint_notes_import)
Gitlab::Cache::Import::Caching::LONGER_TIMEOUT
else
Gitlab::Cache::Import::Caching::TIMEOUT
end
end
+
+ def import_settings
+ ::Gitlab::GithubImport::Settings.new(project)
+ end
end
end
end
diff --git a/lib/gitlab/github_import/settings.rb b/lib/gitlab/github_import/settings.rb
new file mode 100644
index 00000000000..77288b9fb98
--- /dev/null
+++ b/lib/gitlab/github_import/settings.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ class Settings
+ OPTIONAL_STAGES = {
+ single_endpoint_issue_events_import: {
+ label: 'Import issue and pull request events',
+ details: <<-TEXT.split("\n").map(&:strip).join(' ')
+ For example, opened or closed, renamed, and labeled or unlabeled.
+ Time required to import these events depends on how many issues or pull requests your project has.
+ TEXT
+ },
+ single_endpoint_notes_import: {
+ label: 'Use alternative comments import method',
+ details: <<-TEXT.split("\n").map(&:strip).join(' ')
+ The default method can skip some comments in large projects because of limitations of the GitHub API.
+ TEXT
+ },
+ attachments_import: {
+ label: 'Import Markdown attachments',
+ details: <<-TEXT.split("\n").map(&:strip).join(' ')
+ Import Markdown attachments from repository comments, release posts, issue descriptions,
+ and pull request descriptions. These can include images, text, or binary attachments.
+ If not imported, links in Markdown to attachments break after you remove the attachments from GitHub.
+ TEXT
+ }
+ }.freeze
+
+ def self.stages_array
+ OPTIONAL_STAGES.map do |stage_name, data|
+ {
+ name: stage_name.to_s,
+ label: s_(format("GitHubImport|%{text}", text: data[:label])),
+ details: s_(format("GitHubImport|%{text}", text: data[:details]))
+ }
+ end
+ end
+
+ def initialize(project)
+ @project = project
+ end
+
+ def write(user_settings)
+ user_settings = user_settings.to_h.with_indifferent_access
+
+ optional_stages = fetch_stages_from_params(user_settings)
+ import_data = project.create_or_update_import_data(data: { optional_stages: optional_stages })
+ import_data.save!
+ end
+
+ def enabled?(stage_name)
+ project.import_data&.data&.dig('optional_stages', stage_name.to_s) || false
+ end
+
+ def disabled?(stage_name)
+ !enabled?(stage_name)
+ end
+
+ private
+
+ attr_reader :project
+
+ def fetch_stages_from_params(user_settings)
+ OPTIONAL_STAGES.keys.to_h do |stage_name|
+ enabled = Gitlab::Utils.to_boolean(user_settings[stage_name], default: false)
+ [stage_name, enabled]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/single_endpoint_notes_importing.rb b/lib/gitlab/github_import/single_endpoint_notes_importing.rb
index ecdda83972b..3584288da57 100644
--- a/lib/gitlab/github_import/single_endpoint_notes_importing.rb
+++ b/lib/gitlab/github_import/single_endpoint_notes_importing.rb
@@ -4,10 +4,10 @@
# - SingleEndpointDiffNotesImporter
# - SingleEndpointIssueNotesImporter
# - SingleEndpointMergeRequestNotesImporter
-# if `github_importer_single_endpoint_notes_import` feature flag is on.
+# if enabled by Gitlab::GithubImport::Settings
#
# - SingleEndpointIssueEventsImporter
-# if `github_importer_issue_events_import` feature flag is on.
+# if enabled by Gitlab::GithubImport::Settings
#
# Fetches associated objects page by page to each item of parent collection.
# Currently `associated` is note or event.
diff --git a/lib/gitlab/runtime.rb b/lib/gitlab/runtime.rb
index 5b1341207fd..6d95cb9a87b 100644
--- a/lib/gitlab/runtime.rb
+++ b/lib/gitlab/runtime.rb
@@ -25,9 +25,9 @@ module Gitlab
if matches.one?
matches.first
elsif matches.none?
- raise UnknownProcessError, "Failed to identify runtime for process #{Process.pid} (#{$0})"
+ raise UnknownProcessError, "Failed to identify runtime for process #{Process.pid} (#{$PROGRAM_NAME})"
else
- raise AmbiguousProcessError, "Ambiguous runtime #{matches} for process #{Process.pid} (#{$0})"
+ raise AmbiguousProcessError, "Ambiguous runtime #{matches} for process #{Process.pid} (#{$PROGRAM_NAME})"
end
end
diff --git a/lib/prometheus/pid_provider.rb b/lib/prometheus/pid_provider.rb
index d2563b4c806..05a2d3bd0c9 100644
--- a/lib/prometheus/pid_provider.rb
+++ b/lib/prometheus/pid_provider.rb
@@ -39,7 +39,7 @@ module Prometheus
end
def process_name
- $0
+ $PROGRAM_NAME
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9ab545b7ad6..a72b1c6c2b9 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -41388,6 +41388,9 @@ msgstr ""
msgid "This namespace has already been taken! Please choose another one."
msgstr ""
+msgid "This namespace has already been taken. Choose a different one."
+msgstr ""
+
msgid "This only applies to repository indexing operations."
msgstr ""
diff --git a/qa/Gemfile b/qa/Gemfile
index b01a5abf886..5817dcd2f90 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -39,7 +39,7 @@ gem 'chemlab', '~> 0.9'
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
# dependencies for jenkins client
-gem 'nokogiri', '~> 1.12'
+gem 'nokogiri', '~> 1.13', '>= 1.13.8'
gem 'deprecation_toolkit', '~> 1.5.1', require: false
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index f9492c8afa9..9917df25a65 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -329,7 +329,7 @@ DEPENDENCIES
gitlab-qa (~> 8)
influxdb-client (~> 1.17)
knapsack (~> 4.0)
- nokogiri (~> 1.12)
+ nokogiri (~> 1.13, >= 1.13.8)
octokit (~> 5.6.1)
parallel (~> 1.19)
parallel_tests (~> 2.29)
@@ -352,4 +352,4 @@ DEPENDENCIES
zeitwerk (~> 2.4)
BUNDLED WITH
- 2.3.15
+ 2.3.23
diff --git a/qa/chemlab-library-gitlab.gemspec b/qa/chemlab-library-gitlab.gemspec
index 9af4a650d98..309de157757 100644
--- a/qa/chemlab-library-gitlab.gemspec
+++ b/qa/chemlab-library-gitlab.gemspec
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-$:.unshift(File.expand_path('lib', __dir__))
+$LOAD_PATH.unshift(File.expand_path('lib', __dir__))
Gem::Specification.new do |spec|
spec.name = 'chemlab-library-gitlab'
diff --git a/scripts/api/cancel_pipeline.rb b/scripts/api/cancel_pipeline.rb
index 2de50dcee80..2667cfb9733 100755
--- a/scripts/api/cancel_pipeline.rb
+++ b/scripts/api/cancel_pipeline.rb
@@ -25,7 +25,7 @@ class CancelPipeline
attr_reader :project, :pipeline_id, :client
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
options = API::DEFAULT_OPTIONS.dup
OptionParser.new do |opts|
diff --git a/scripts/api/download_job_artifact.rb b/scripts/api/download_job_artifact.rb
index 23202ad3912..394ad8f3a3d 100755
--- a/scripts/api/download_job_artifact.rb
+++ b/scripts/api/download_job_artifact.rb
@@ -60,7 +60,7 @@ class ArtifactFinder
end
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
options = API::DEFAULT_OPTIONS.dup
OptionParser.new do |opts|
diff --git a/scripts/api/get_job_id.rb b/scripts/api/get_job_id.rb
index 2ee769d58f4..12535106a4c 100755
--- a/scripts/api/get_job_id.rb
+++ b/scripts/api/get_job_id.rb
@@ -95,7 +95,7 @@ class JobFinder
end
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
options = JobFinder::DEFAULT_OPTIONS.dup
OptionParser.new do |opts|
diff --git a/scripts/changed-feature-flags b/scripts/changed-feature-flags
index ded6156bfa8..8c1b219e5a6 100755
--- a/scripts/changed-feature-flags
+++ b/scripts/changed-feature-flags
@@ -90,7 +90,7 @@ class GetFeatureFlagsFromFiles
end
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
options = API::DEFAULT_OPTIONS.dup
OptionParser.new do |opts|
diff --git a/scripts/failed_tests.rb b/scripts/failed_tests.rb
index fb13df7bf62..319961d277c 100755
--- a/scripts/failed_tests.rb
+++ b/scripts/failed_tests.rb
@@ -87,7 +87,7 @@ class FailedTests
end
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
options = {
previous_tests_report_path: 'test_results/previous/test_reports.json',
output_directory: 'tmp/previous_failed_tests/',
diff --git a/scripts/generate-failed-pipeline-slack-message.rb b/scripts/generate-failed-pipeline-slack-message.rb
index 1628663190f..699e32872e6 100755
--- a/scripts/generate-failed-pipeline-slack-message.rb
+++ b/scripts/generate-failed-pipeline-slack-message.rb
@@ -8,8 +8,11 @@ finder_options = API::DEFAULT_OPTIONS.dup.merge(exclude_allowed_to_fail_jobs: tr
failed_jobs = PipelineFailedJobs.new(finder_options).execute
class SlackReporter
+ DEFAULT_FAILED_PIPELINE_REPORT_FILE = 'failed_pipeline_report.json'
+
def initialize(failed_jobs)
@failed_jobs = failed_jobs
+ @failed_pipeline_report_file = ENV.fetch('FAILED_PIPELINE_REPORT_FILE', DEFAULT_FAILED_PIPELINE_REPORT_FILE)
end
def report
@@ -44,7 +47,7 @@ class SlackReporter
fields: [
{
type: "mrkdwn",
- text: "*Source*\n#{source}"
+ text: "*Source*\n#{source} from #{project_link}"
},
{
type: "mrkdwn",
@@ -62,12 +65,12 @@ class SlackReporter
]
}
- File.write(ENV['FAILED_PIPELINE_REPORT_FILE'], JSON.pretty_generate(payload))
+ File.write(failed_pipeline_report_file, JSON.pretty_generate(payload))
end
private
- attr_reader :failed_jobs
+ attr_reader :failed_jobs, :failed_pipeline_report_file
def title
"Pipeline #{pipeline_link} for #{branch_link} failed"
@@ -93,6 +96,10 @@ class SlackReporter
"`#{ENV['CI_PIPELINE_SOURCE']}`"
end
+ def project_link
+ "<#{ENV['CI_PROJECT_URL']}|#{ENV['CI_PROJECT_NAME']}>"
+ end
+
def triggered_by_link
"<#{ENV['CI_SERVER_URL']}/#{ENV['GITLAB_USER_LOGIN']}|#{ENV['GITLAB_USER_NAME']}>"
end
diff --git a/scripts/perf/query_limiting_report.rb b/scripts/perf/query_limiting_report.rb
index 89abc1b301b..364cd6fc5d4 100755
--- a/scripts/perf/query_limiting_report.rb
+++ b/scripts/perf/query_limiting_report.rb
@@ -149,7 +149,7 @@ class QueryLimitingReport
end
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
options = QueryLimitingReport::DEFAULT_OPTIONS.dup
OptionParser.new do |opts|
diff --git a/scripts/pipeline_test_report_builder.rb b/scripts/pipeline_test_report_builder.rb
index 649b68427ea..90af0451864 100755
--- a/scripts/pipeline_test_report_builder.rb
+++ b/scripts/pipeline_test_report_builder.rb
@@ -128,7 +128,7 @@ class PipelineTestReportBuilder
end
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
options = Host::DEFAULT_OPTIONS.dup
OptionParser.new do |opts|
diff --git a/scripts/rubocop-parse b/scripts/rubocop-parse
index 4c82be5934b..50708bf55fa 100755
--- a/scripts/rubocop-parse
+++ b/scripts/rubocop-parse
@@ -38,7 +38,7 @@ end
options = Struct.new(:eval, :ruby_version, :print_help, keyword_init: true).new
parser = OptionParser.new do |opts|
- opts.banner = "Usage: #{$0} [-e code] [FILE...]"
+ opts.banner = "Usage: #{$PROGRAM_NAME} [-e code] [FILE...]"
opts.on('-e FRAGMENT', '--eval FRAGMENT', 'Process a fragment of Ruby code') do |code|
options.eval = code
diff --git a/scripts/setup/find-jh-branch.rb b/scripts/setup/find-jh-branch.rb
index a7c1cafd74c..5b36aa7a1f4 100755
--- a/scripts/setup/find-jh-branch.rb
+++ b/scripts/setup/find-jh-branch.rb
@@ -97,6 +97,6 @@ class FindJhBranch
end
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
puts FindJhBranch.new.run
end
diff --git a/scripts/static-analysis b/scripts/static-analysis
index 53f84c19ac6..c6cf09e056b 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -191,7 +191,7 @@ class StaticAnalysis
end
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
options = {}
if ARGV.include?('--dry-run')
diff --git a/scripts/trigger-build.rb b/scripts/trigger-build.rb
index b368bbdb1f1..897ca9f473e 100755
--- a/scripts/trigger-build.rb
+++ b/scripts/trigger-build.rb
@@ -427,7 +427,7 @@ module Trigger
Job = Class.new(Pipeline)
end
-if $0 == __FILE__
+if $PROGRAM_NAME == __FILE__
case ARGV[0]
when 'cng'
Trigger::CNG.new.invoke!.wait!
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
deleted file mode 100644
index 380c42af857..00000000000
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ /dev/null
@@ -1,613 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Boards::IssuesController do
- include ExternalAuthorizationServiceHelpers
-
- let(:project) { create(:project, :private) }
- let(:board) { create(:board, project: project) }
- let(:user) { create(:user) }
- let(:guest) { create(:user) }
-
- let(:planning) { create(:label, project: project, name: 'Planning') }
- let(:development) { create(:label, project: project, name: 'Development') }
-
- let!(:list1) { create(:list, board: board, label: planning, position: 0) }
- let!(:list2) { create(:list, board: board, label: development, position: 1) }
-
- before do
- project.add_maintainer(user)
- project.add_guest(guest)
- end
-
- describe 'GET index', :request_store do
- let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join('spec/fixtures/dk.png'))) }
-
- context 'with invalid board id' do
- it 'returns a not found 404 response' do
- list_issues user: user, board: non_existing_record_id, list: list2
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'when list id is present' do
- context 'with valid list id' do
- let(:group) { create(:group, :private, projects: [project]) }
- let(:group_board) { create(:board, group: group) }
- let!(:list3) { create(:list, board: group_board, label: development, position: 2) }
- let(:sub_group_1) { create(:group, :private, parent: group) }
-
- before do
- group.add_maintainer(user)
- end
-
- it 'returns issues that have the list label applied' do
- issue = create(:labeled_issue, project: project, labels: [planning])
- create(:labeled_issue, project: project, labels: [planning])
- create(:labeled_issue, project: project, labels: [development], due_date: Date.tomorrow)
- create(:labeled_issue, project: project, labels: [development], assignees: [johndoe])
- issue.subscribe(johndoe, project)
- expect(Issue).to receive(:move_nulls_to_end)
-
- list_issues user: user, board: board, list: list2
-
- expect(response).to match_response_schema('entities/issue_boards')
- expect(json_response['issues'].length).to eq 2
- expect(development.issues.map(&:relative_position)).not_to include(nil)
- end
-
- it 'returns issues by closed_at in descending order in closed list' do
- create(:closed_issue, project: project, title: 'New Issue 1', closed_at: 1.day.ago)
- create(:closed_issue, project: project, title: 'New Issue 2', closed_at: 1.week.ago)
-
- list_issues user: user, board: board, list: board.lists.last.id
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['issues'].length).to eq(2)
- expect(json_response['issues'][0]['title']).to eq('New Issue 1')
- expect(json_response['issues'][1]['title']).to eq('New Issue 2')
- end
-
- it 'avoids N+1 database queries' do
- create(:labeled_issue, project: project, labels: [development])
- control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: board, list: list2) }.count
-
- # 25 issues is bigger than the page size
- # the relative position will ignore the `#make_sure_position_set` queries
- create_list(:labeled_issue, 25, project: project, labels: [development], assignees: [johndoe], relative_position: 1)
-
- expect { list_issues(user: user, board: board, list: list2) }.not_to exceed_query_limit(control_count)
- end
-
- it 'avoids N+1 database queries when adding a project', :request_store do
- create(:labeled_issue, project: project, labels: [development])
- control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count
-
- 2.times do
- p = create(:project, group: group)
- create(:labeled_issue, project: p, labels: [development])
- end
-
- project_2 = create(:project, group: group)
- create(:labeled_issue, project: project_2, labels: [development], assignees: [johndoe])
-
- # because each issue without relative_position must be updated with
- # a different value, we have 8 extra queries per issue
- expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1))
- end
-
- it 'avoids N+1 database queries when adding a subgroup, project, and issue' do
- create(:project, group: sub_group_1)
- create(:labeled_issue, project: project, labels: [development])
- control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count
- project_2 = create(:project, group: group)
-
- 2.times do
- p = create(:project, group: sub_group_1)
- create(:labeled_issue, project: p, labels: [development])
- end
-
- create(:labeled_issue, project: project_2, labels: [development], assignees: [johndoe])
-
- expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1))
- end
-
- it 'does not query issues table more than once' do
- recorder = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: board, list: list1) }
- query_count = recorder.occurrences.select { |query,| query.match?(/FROM "?issues"?/) }.each_value.first
-
- expect(query_count).to eq(1)
- end
-
- context 'when block_issue_repositioning feature flag is enabled' do
- before do
- stub_feature_flags(block_issue_repositioning: true)
- end
-
- it 'does not reposition issues with null position' do
- expect(Issue).not_to receive(:move_nulls_to_end)
-
- list_issues(user: user, board: group_board, list: list3)
- end
- end
- end
-
- context 'with invalid list id' do
- it 'returns a not found 404 response' do
- list_issues user: user, board: board, list: non_existing_record_id
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- context 'when list id is missing' do
- it 'returns opened issues without board labels applied' do
- bug = create(:label, project: project, name: 'Bug')
- create(:issue, project: project)
- create(:labeled_issue, project: project, labels: [planning])
- create(:labeled_issue, project: project, labels: [development])
- create(:labeled_issue, project: project, labels: [bug])
-
- list_issues user: user, board: board
-
- expect(response).to match_response_schema('entities/issue_boards')
- expect(json_response['issues'].length).to eq 2
- end
- end
-
- context 'with unauthorized user' do
- let(:unauth_user) { create(:user) }
-
- it 'returns a forbidden 403 response' do
- list_issues user: unauth_user, board: board, list: list2
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- context 'with external authorization' do
- before do
- sign_in(user)
- enable_external_authorization_service_check
- end
-
- it 'returns a 403 for group boards' do
- group = create(:group)
- group_board = create(:board, group: group)
-
- list_issues(user: user, board: group_board)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
-
- it 'is successful for project boards' do
- project_board = create(:board, project: project)
-
- list_issues(user: user, board: project_board)
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- describe 'PUT bulk_move' do
- let(:todo) { create(:group_label, group: group, name: 'Todo') }
- let(:development) { create(:group_label, group: group, name: 'Development') }
- let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
- let(:guest) { create(:group_member, :guest, user: create(:user), group: group ).user }
- let(:project) { create(:project, group: group) }
- let(:group) { create(:group) }
- let(:board) { create(:board, project: project) }
- let(:list1) { create(:list, board: board, label: todo, position: 0) }
- let(:list2) { create(:list, board: board, label: development, position: 1) }
- let(:issue1) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 10) }
- let(:issue2) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 20) }
- let(:issue3) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 30) }
- let(:issue4) { create(:labeled_issue, project: project, labels: [development], author: user, relative_position: 100) }
-
- let(:move_params) do
- {
- board_id: board.id,
- ids: [issue1.id, issue2.id, issue3.id],
- from_list_id: list1.id,
- to_list_id: list2.id,
- move_before_id: issue4.id,
- move_after_id: nil
- }
- end
-
- before do
- project.add_maintainer(user)
- project.add_guest(guest)
- end
-
- shared_examples 'move issues endpoint provider' do
- before do
- sign_in(signed_in_user)
- end
-
- it 'responds as expected' do
- put :bulk_move, params: move_issues_params
- expect(response).to have_gitlab_http_status(expected_status)
-
- if expected_status == 200
- expect(json_response).to include(
- 'count' => move_issues_params[:ids].size,
- 'success' => true
- )
-
- expect(json_response['issues'].pluck('id')).to match_array(move_issues_params[:ids])
- end
- end
-
- it 'moves issues as expected' do
- put :bulk_move, params: move_issues_params
- expect(response).to have_gitlab_http_status(expected_status)
-
- list_issues user: requesting_user, board: board, list: list2
- expect(response).to have_gitlab_http_status(:ok)
-
- expect(response).to match_response_schema('entities/issue_boards')
-
- responded_issues = json_response['issues']
- expect(responded_issues.length).to eq expected_issue_count
-
- ids_in_order = responded_issues.pluck('id')
- expect(ids_in_order).to eq(expected_issue_ids_in_order)
- end
- end
-
- context 'when items are moved to another list' do
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { user }
- let(:move_issues_params) { move_params }
- let(:requesting_user) { user }
- let(:expected_status) { 200 }
- let(:expected_issue_count) { 4 }
- let(:expected_issue_ids_in_order) { [issue4.id, issue1.id, issue2.id, issue3.id] }
- end
- end
-
- context 'when moving just one issue' do
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { user }
- let(:move_issues_params) do
- move_params.dup.tap do |hash|
- hash[:ids] = [issue2.id]
- end
- end
-
- let(:requesting_user) { user }
- let(:expected_status) { 200 }
- let(:expected_issue_count) { 2 }
- let(:expected_issue_ids_in_order) { [issue4.id, issue2.id] }
- end
- end
-
- context 'when user is not allowed to move issue' do
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { guest }
- let(:move_issues_params) do
- move_params.dup.tap do |hash|
- hash[:ids] = [issue2.id]
- end
- end
-
- let(:requesting_user) { user }
- let(:expected_status) { 403 }
- let(:expected_issue_count) { 1 }
- let(:expected_issue_ids_in_order) { [issue4.id] }
- end
- end
-
- context 'when issues should be moved visually above existing issue in list' do
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { user }
- let(:move_issues_params) do
- move_params.dup.tap do |hash|
- hash[:move_after_id] = issue4.id
- hash[:move_before_id] = nil
- end
- end
-
- let(:requesting_user) { user }
- let(:expected_status) { 200 }
- let(:expected_issue_count) { 4 }
- let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id, issue4.id] }
- end
- end
-
- context 'when destination list is empty' do
- before do
- # Remove issue from list
- issue4.labels -= [development]
- issue4.save!
- end
-
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { user }
- let(:move_issues_params) do
- move_params.dup.tap do |hash|
- hash[:move_before_id] = nil
- end
- end
-
- let(:requesting_user) { user }
- let(:expected_status) { 200 }
- let(:expected_issue_count) { 3 }
- let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id] }
- end
- end
-
- context 'when no position arguments are given' do
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { user }
- let(:move_issues_params) do
- move_params.dup.tap do |hash|
- hash[:move_before_id] = nil
- end
- end
-
- let(:requesting_user) { user }
- let(:expected_status) { 200 }
- let(:expected_issue_count) { 4 }
- let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id, issue4.id] }
- end
- end
-
- context 'when move_before_id and move_after_id are given' do
- let(:issue5) { create(:labeled_issue, project: project, labels: [development], author: user, relative_position: 90) }
-
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { user }
- let(:move_issues_params) do
- move_params.dup.tap do |hash|
- hash[:move_before_id] = issue5.id
- hash[:move_after_id] = issue4.id
- end
- end
-
- let(:requesting_user) { user }
- let(:expected_status) { 200 }
- let(:expected_issue_count) { 5 }
- let(:expected_issue_ids_in_order) { [issue5.id, issue1.id, issue2.id, issue3.id, issue4.id] }
- end
- end
-
- context 'when request contains too many issues' do
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { user }
- let(:move_issues_params) do
- move_params.dup.tap do |hash|
- hash[:ids] = (0..51).to_a
- end
- end
-
- let(:requesting_user) { user }
- let(:expected_status) { 422 }
- let(:expected_issue_count) { 1 }
- let(:expected_issue_ids_in_order) { [issue4.id] }
- end
- end
-
- context 'when request is malformed' do
- it_behaves_like 'move issues endpoint provider' do
- let(:signed_in_user) { user }
- let(:move_issues_params) do
- move_params.dup.tap do |hash|
- hash[:ids] = 'foobar'
- end
- end
-
- let(:requesting_user) { user }
- let(:expected_status) { 400 }
- let(:expected_issue_count) { 1 }
- let(:expected_issue_ids_in_order) { [issue4.id] }
- end
- end
- end
-
- def list_issues(user:, board:, list: nil)
- sign_in(user)
-
- params = {
- board_id: board.to_param,
- list_id: list.try(:to_param)
- }
-
- unless board.try(:parent).is_a?(Group)
- params[:namespace_id] = project.namespace.to_param
- params[:project_id] = project
- end
-
- get :index, params: params.compact
- end
- end
-
- describe 'POST create' do
- context 'when trying to create issue on an unauthorized project' do
- let(:unauthorized_project) { create(:project, :private) }
- let(:issue_params) { { project_id: unauthorized_project.id } }
-
- it 'creates the issue on the board\'s project' do
- expect do
- create_issue user: user, board: board, list: list1, title: 'New issue', additional_issue_params: issue_params
- end.to change(Issue, :count).by(1)
-
- created_issue = Issue.last
-
- expect(created_issue.project).to eq(project)
- expect(unauthorized_project.reload.issues.count).to eq(0)
- end
- end
-
- context 'with valid params' do
- before do
- create_issue user: user, board: board, list: list1, title: 'New issue'
- end
-
- it 'returns a successful 200 response' do
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'returns the created issue' do
- expect(response).to match_response_schema('entities/issue_board')
- end
-
- it 'sets the default work_item_type' do
- expect(Issue.last.work_item_type.base_type).to eq('issue')
- end
- end
-
- context 'with invalid params' do
- context 'when title is nil' do
- it 'returns an unprocessable entity 422 response' do
- create_issue user: user, board: board, list: list1, title: nil
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- end
- end
-
- context 'when list does not belongs to project board' do
- it 'returns a not found 404 response' do
- list = create(:list)
-
- create_issue user: user, board: board, list: list, title: 'New issue'
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'with invalid board id' do
- it 'returns a not found 404 response' do
- create_issue user: user, board: non_existing_record_id, list: list1, title: 'New issue'
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'with invalid list id' do
- it 'returns a not found 404 response' do
- create_issue user: user, board: board, list: non_existing_record_id, title: 'New issue'
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- context 'when create service returns an unrecoverable error' do
- before do
- allow_next_instance_of(Issues::CreateService) do |create_service|
- allow(create_service).to receive(:execute).and_return(
- ServiceResponse.error(message: 'unrecoverable error', http_status: 404)
- )
- end
- end
-
- it 'returns an array with errors an service http_status' do
- create_issue user: user, board: board, list: list1, title: 'New issue'
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(json_response).to contain_exactly('unrecoverable error')
- end
- end
-
- context 'with guest user' do
- context 'in open list' do
- it 'returns a successful 200 response' do
- open_list = board.lists.create!(list_type: :backlog)
- create_issue user: guest, board: board, list: open_list, title: 'New issue'
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- context 'in label list' do
- it 'returns a forbidden 403 response' do
- create_issue user: guest, board: board, list: list1, title: 'New issue'
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
- end
-
- def create_issue(user:, board:, list:, title:, additional_issue_params: {})
- sign_in(user)
-
- post :create, params: {
- board_id: board.to_param,
- list_id: list.to_param,
- issue: { title: title, project_id: project.id }.merge(additional_issue_params)
- },
- format: :json
- end
- end
-
- describe 'PATCH update' do
- let!(:issue) { create(:labeled_issue, project: project, labels: [planning]) }
-
- context 'with valid params' do
- it 'returns a successful 200 response' do
- move user: user, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'moves issue to the desired list' do
- move user: user, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id
-
- expect(issue.reload.labels).to contain_exactly(development)
- end
- end
-
- context 'with invalid params' do
- it 'returns a unprocessable entity 422 response for invalid lists' do
- move user: user, board: board, issue: issue, from_list_id: nil, to_list_id: nil
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- end
-
- it 'returns a not found 404 response for invalid board id' do
- move user: user, board: non_existing_record_id, issue: issue, from_list_id: list1.id, to_list_id: list2.id
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
-
- it 'returns a not found 404 response for invalid issue id' do
- move user: user, board: board, issue: double(id: non_existing_record_id), from_list_id: list1.id, to_list_id: list2.id
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'with unauthorized user' do
- let(:guest) { create(:user) }
-
- before do
- project.add_guest(guest)
- end
-
- it 'returns a forbidden 403 response' do
- move user: guest, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- def move(user:, board:, issue:, from_list_id:, to_list_id:)
- sign_in(user)
-
- patch :update, params: {
- namespace_id: project.namespace.to_param,
- project_id: project.id,
- board_id: board.to_param,
- id: issue.id,
- from_list_id: from_list_id,
- to_list_id: to_list_id
- },
- format: :json
- end
- end
-end
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
deleted file mode 100644
index 95334974e66..00000000000
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ /dev/null
@@ -1,333 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Boards::ListsController do
- let(:project) { create(:project) }
- let(:board) { create(:board, project: project) }
- let(:user) { create(:user) }
- let(:guest) { create(:user) }
-
- before do
- project.add_maintainer(user)
- project.add_guest(guest)
- end
-
- describe 'GET index' do
- before do
- create(:list, board: board)
- end
-
- it 'returns a successful 200 response' do
- read_board_list user: user, board: board
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq 'application/json'
- end
-
- it 'returns a list of board lists' do
- read_board_list user: user, board: board
-
- expect(response).to match_response_schema('lists')
- expect(json_response.length).to eq 3
- end
-
- context 'when another user has list preferences' do
- before do
- board.lists.first.update_preferences_for(guest, collapsed: true)
- end
-
- it 'returns the complete list of board lists' do
- read_board_list user: user, board: board
-
- expect(json_response.length).to eq 3
- end
- end
-
- context 'with unauthorized user' do
- let(:unauth_user) { create(:user) }
-
- it 'returns a forbidden 403 response' do
- read_board_list user: unauth_user, board: board
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- def read_board_list(user:, board:)
- sign_in(user)
-
- get :index, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- board_id: board.to_param
- },
- format: :json
- end
- end
-
- describe 'POST create' do
- context 'with valid params' do
- let(:label) { create(:label, project: project, name: 'Development') }
-
- it 'returns a successful 200 response' do
- create_board_list user: user, board: board, label_id: label.id
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'returns the created list' do
- create_board_list user: user, board: board, label_id: label.id
-
- expect(response).to match_response_schema('list')
- end
- end
-
- context 'with invalid params' do
- context 'when label is nil' do
- it 'returns an unprocessable entity 422 response' do
- create_board_list user: user, board: board, label_id: nil
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response['errors']).to eq(['Label not found'])
- end
- end
-
- context 'when label that does not belongs to project' do
- it 'returns an unprocessable entity 422 response' do
- label = create(:label, name: 'Development')
-
- create_board_list user: user, board: board, label_id: label.id
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response['errors']).to eq(['Label not found'])
- end
- end
- end
-
- context 'with unauthorized user' do
- it 'returns a forbidden 403 response' do
- label = create(:label, project: project, name: 'Development')
-
- create_board_list user: guest, board: board, label_id: label.id
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- def create_board_list(user:, board:, label_id:)
- sign_in(user)
-
- post :create, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- board_id: board.to_param,
- list: { label_id: label_id }
- },
- format: :json
- end
- end
-
- describe 'PATCH update' do
- let!(:planning) { create(:list, board: board, position: 0) }
- let!(:development) { create(:list, board: board, position: 1) }
-
- context 'with valid position' do
- it 'returns a successful 200 response' do
- move user: user, board: board, list: planning, position: 1
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'moves the list to the desired position' do
- move user: user, board: board, list: planning, position: 1
-
- expect(planning.reload.position).to eq 1
- end
- end
-
- context 'with invalid position' do
- it 'returns an unprocessable entity 422 response' do
- move user: user, board: board, list: planning, position: 6
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- end
- end
-
- context 'with invalid list id' do
- it 'returns a not found 404 response' do
- move user: user, board: board, list: non_existing_record_id, position: 1
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'with unauthorized user' do
- it 'returns a 422 unprocessable entity response' do
- move user: guest, board: board, list: planning, position: 6
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- end
- end
-
- context 'with collapsed preference' do
- it 'saves collapsed preference for user' do
- save_setting user: user, board: board, list: planning, setting: { collapsed: true }
-
- expect(planning.preferences_for(user).collapsed).to eq(true)
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'saves not collapsed preference for user' do
- save_setting user: user, board: board, list: planning, setting: { collapsed: false }
-
- expect(planning.preferences_for(user).collapsed).to eq(false)
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- context 'with a list_type other than :label' do
- let!(:closed) { create(:closed_list, board: board, position: 2) }
-
- it 'saves collapsed preference for user' do
- save_setting user: user, board: board, list: closed, setting: { collapsed: true }
-
- expect(closed.preferences_for(user).collapsed).to eq(true)
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'saves not collapsed preference for user' do
- save_setting user: user, board: board, list: closed, setting: { collapsed: false }
-
- expect(closed.preferences_for(user).collapsed).to eq(false)
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- def move(user:, board:, list:, position:)
- sign_in(user)
-
- params = { namespace_id: project.namespace.to_param,
- project_id: project.id,
- board_id: board.to_param,
- id: list.to_param,
- list: { position: position },
- format: :json }
-
- patch :update, params: params, as: :json
- end
-
- def save_setting(user:, board:, list:, setting: {})
- sign_in(user)
-
- params = { namespace_id: project.namespace.to_param,
- project_id: project.id,
- board_id: board.to_param,
- id: list.to_param,
- list: setting,
- format: :json }
-
- patch :update, params: params, as: :json
- end
- end
-
- describe 'DELETE destroy' do
- let!(:planning) { create(:list, board: board, position: 0) }
-
- context 'with valid list id' do
- it 'returns a successful 200 response' do
- remove_board_list user: user, board: board, list: planning
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'removes list from board' do
- expect { remove_board_list user: user, board: board, list: planning }.to change(board.lists, :size).by(-1)
- end
- end
-
- context 'with invalid list id' do
- it 'returns a not found 404 response' do
- remove_board_list user: user, board: board, list: non_existing_record_id
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'with unauthorized user' do
- it 'returns a forbidden 403 response' do
- remove_board_list user: guest, board: board, list: planning
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- context 'with an error service response' do
- it 'returns an unprocessable entity response' do
- allow(Boards::Lists::DestroyService).to receive(:new)
- .and_return(double(execute: ServiceResponse.error(message: 'error')))
-
- remove_board_list user: user, board: board, list: planning
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- end
- end
-
- def remove_board_list(user:, board:, list:)
- sign_in(user)
-
- delete :destroy, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- board_id: board.to_param,
- id: list.to_param
- },
- format: :json
- end
- end
-
- describe 'POST generate' do
- context 'when board lists is empty' do
- it 'returns a successful 200 response' do
- generate_default_lists user: user, board: board
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'returns the defaults lists' do
- generate_default_lists user: user, board: board
-
- expect(response).to match_response_schema('lists')
- end
- end
-
- context 'when board lists is not empty' do
- it 'returns an unprocessable entity 422 response' do
- create(:list, board: board)
-
- generate_default_lists user: user, board: board
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- end
- end
-
- context 'with unauthorized user' do
- it 'returns a forbidden 403 response' do
- generate_default_lists user: guest, board: board
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- def generate_default_lists(user:, board:)
- sign_in(user)
-
- post :generate, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- board_id: board.to_param
- },
- format: :json
- end
- end
-end
diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb
index 7a2d9acadae..89d0669f47b 100644
--- a/spec/controllers/projects/boards_controller_spec.rb
+++ b/spec/controllers/projects/boards_controller_spec.rb
@@ -19,12 +19,6 @@ RSpec.describe Projects::BoardsController do
expect { list_boards }.to change(project.boards, :count).by(1)
end
- it 'sets boards_endpoint instance variable to a boards path' do
- list_boards
-
- expect(assigns(:boards_endpoint)).to eq project_boards_path(project)
- end
-
it 'renders template' do
list_boards
@@ -110,12 +104,6 @@ RSpec.describe Projects::BoardsController do
describe 'GET show' do
let_it_be(:board) { create(:board, project: project) }
- it 'sets boards_endpoint instance variable to a boards path' do
- read_board board: board
-
- expect(assigns(:boards_endpoint)).to eq project_boards_path(project)
- end
-
context 'when format is HTML' do
it 'renders template' do
expect { read_board board: board }.to change(BoardProjectRecentVisit, :count).by(1)
diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb
index db4d9125e6e..393cd6f6a21 100644
--- a/spec/fast_spec_helper.rb
+++ b/spec/fast_spec_helper.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-if $".include?(File.expand_path('spec_helper.rb', __dir__))
+if $LOADED_FEATURES.include?(File.expand_path('spec_helper.rb', __dir__))
# There's no need to load anything here if spec_helper is already loaded
# because spec_helper is more extensive than fast_spec_helper
return
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index baa691d244e..666bf3594de 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe 'Global search' do
let(:project) { create(:project, namespace: user.namespace) }
before do
+ stub_feature_flags(search_page_vertical_nav: false)
project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index e2c8708be78..50e6eb66466 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'User searches for code' do
context 'when signed in' do
before do
+ stub_feature_flags(search_page_vertical_nav: false)
project.add_maintainer(user)
sign_in(user)
end
@@ -214,6 +215,7 @@ RSpec.describe 'User searches for code' do
let(:project) { create(:project, :public, :repository) }
before do
+ stub_feature_flags(search_page_vertical_nav: false)
visit(project_path(project))
end
diff --git a/spec/features/search/user_searches_for_comments_spec.rb b/spec/features/search/user_searches_for_comments_spec.rb
index 5185a2460dc..a6793bc3aa7 100644
--- a/spec/features/search/user_searches_for_comments_spec.rb
+++ b/spec/features/search/user_searches_for_comments_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'User searches for comments' do
let(:user) { create(:user) }
before do
+ stub_feature_flags(search_page_vertical_nav: false)
project.add_reporter(user)
sign_in(user)
diff --git a/spec/features/search/user_searches_for_commits_spec.rb b/spec/features/search/user_searches_for_commits_spec.rb
index a157483ce70..4ec2a9e6cff 100644
--- a/spec/features/search/user_searches_for_commits_spec.rb
+++ b/spec/features/search/user_searches_for_commits_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'User searches for commits', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(search_page_vertical_nav: false)
project.add_reporter(user)
sign_in(user)
diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb
index c23a54594d4..51d2f355848 100644
--- a/spec/features/search/user_searches_for_issues_spec.rb
+++ b/spec/features/search/user_searches_for_issues_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe 'User searches for issues', :js do
before do
project.add_maintainer(user)
sign_in(user)
+ stub_feature_flags(search_page_vertical_nav: false)
visit(search_path)
end
@@ -110,6 +111,7 @@ RSpec.describe 'User searches for issues', :js do
before do
stub_feature_flags(block_anonymous_global_searches: false)
+ stub_feature_flags(search_page_vertical_nav: false)
visit(search_path)
end
@@ -127,6 +129,7 @@ RSpec.describe 'User searches for issues', :js do
context 'when block_anonymous_global_searches is enabled' do
before do
+ stub_feature_flags(search_page_vertical_nav: false)
visit(search_path)
end
diff --git a/spec/features/search/user_searches_for_merge_requests_spec.rb b/spec/features/search/user_searches_for_merge_requests_spec.rb
index 61c61d793db..a4fbe3a6e59 100644
--- a/spec/features/search/user_searches_for_merge_requests_spec.rb
+++ b/spec/features/search/user_searches_for_merge_requests_spec.rb
@@ -15,6 +15,7 @@ RSpec.describe 'User searches for merge requests', :js do
end
before do
+ stub_feature_flags(search_page_vertical_nav: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/search/user_searches_for_milestones_spec.rb b/spec/features/search/user_searches_for_milestones_spec.rb
index 61f2e8e0c8f..6773059830c 100644
--- a/spec/features/search/user_searches_for_milestones_spec.rb
+++ b/spec/features/search/user_searches_for_milestones_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe 'User searches for milestones', :js do
before do
project.add_maintainer(user)
sign_in(user)
+ stub_feature_flags(search_page_vertical_nav: false)
visit(search_path)
end
diff --git a/spec/features/search/user_searches_for_projects_spec.rb b/spec/features/search/user_searches_for_projects_spec.rb
index 562da56275c..5902859d1f5 100644
--- a/spec/features/search/user_searches_for_projects_spec.rb
+++ b/spec/features/search/user_searches_for_projects_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'User searches for projects', :js do
context 'when signed out' do
context 'when block_anonymous_global_searches is disabled' do
before do
+ stub_feature_flags(search_page_vertical_nav: false)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000)
stub_feature_flags(block_anonymous_global_searches: false)
diff --git a/spec/features/search/user_searches_for_users_spec.rb b/spec/features/search/user_searches_for_users_spec.rb
index a5cf12fa068..e21a66fed92 100644
--- a/spec/features/search/user_searches_for_users_spec.rb
+++ b/spec/features/search/user_searches_for_users_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'User searches for users' do
let(:user3) { create(:user, username: 'gob_2018', name: 'George Oscar Bluth') }
before do
+ stub_feature_flags(search_page_vertical_nav: false)
sign_in(user1)
end
diff --git a/spec/features/search/user_searches_for_wiki_pages_spec.rb b/spec/features/search/user_searches_for_wiki_pages_spec.rb
index 9808383adb7..2e390309022 100644
--- a/spec/features/search/user_searches_for_wiki_pages_spec.rb
+++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'User searches for wiki pages', :js do
let!(:wiki_page) { create(:wiki_page, wiki: project.wiki, title: 'directory/title', content: 'Some Wiki content') }
before do
+ stub_feature_flags(search_page_vertical_nav: false)
project.add_maintainer(user)
sign_in(user)
@@ -18,6 +19,10 @@ RSpec.describe 'User searches for wiki pages', :js do
include_examples 'search timeouts', 'wiki_blobs'
shared_examples 'search wiki blobs' do
+ before do
+ stub_feature_flags(search_page_vertical_nav: false)
+ end
+
it 'finds a page' do
find('[data-testid="project-filter"]').click
diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb
index 41288a34fb2..827e3984896 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe 'User uses header search field', :js do
end
before do
+ stub_feature_flags(search_page_vertical_nav: false)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).and_return(0)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000)
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index 46bc3b7caad..69b9a0aa64d 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe 'Search Snippets' do
+ before do
+ stub_feature_flags(search_page_vertical_nav: false)
+ end
+
it 'user searches for snippets by title' do
public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
diff --git a/spec/frontend/search/sidebar/components/app_spec.js b/spec/frontend/search/sidebar/components/app_spec.js
index 3bea0748c47..89959feec39 100644
--- a/spec/frontend/search/sidebar/components/app_spec.js
+++ b/spec/frontend/search/sidebar/components/app_spec.js
@@ -42,20 +42,39 @@ describe('GlobalSearchSidebar', () => {
const findResetLinkButton = () => wrapper.findComponent(GlLink);
describe('template', () => {
- beforeEach(() => {
- createComponent();
- });
+ describe('scope=projects', () => {
+ beforeEach(() => {
+ createComponent({ urlQuery: { ...MOCK_QUERY, scope: 'projects' } });
+ });
- it('renders StatusFilter always', () => {
- expect(findStatusFilter().exists()).toBe(true);
- });
+ it("doesn't render StatusFilter", () => {
+ expect(findStatusFilter().exists()).toBe(false);
+ });
+
+ it("doesn't render ConfidentialityFilter", () => {
+ expect(findConfidentialityFilter().exists()).toBe(false);
+ });
- it('renders ConfidentialityFilter always', () => {
- expect(findConfidentialityFilter().exists()).toBe(true);
+ it("doesn't render ApplyButton", () => {
+ expect(findApplyButton().exists()).toBe(false);
+ });
});
- it('renders ApplyButton always', () => {
- expect(findApplyButton().exists()).toBe(true);
+ describe('scope=issues', () => {
+ beforeEach(() => {
+ createComponent({ urlQuery: MOCK_QUERY });
+ });
+ it('renders StatusFilter', () => {
+ expect(findStatusFilter().exists()).toBe(true);
+ });
+
+ it('renders ConfidentialityFilter', () => {
+ expect(findConfidentialityFilter().exists()).toBe(true);
+ });
+
+ it('renders ApplyButton', () => {
+ expect(findApplyButton().exists()).toBe(true);
+ });
});
});
@@ -115,7 +134,7 @@ describe('GlobalSearchSidebar', () => {
describe('actions', () => {
beforeEach(() => {
- createComponent();
+ createComponent({});
});
it('clicking ApplyButton calls applyQuery', () => {
diff --git a/spec/helpers/boards_helper_spec.rb b/spec/helpers/boards_helper_spec.rb
index d72e111c7cd..27b7bac5a88 100644
--- a/spec/helpers/boards_helper_spec.rb
+++ b/spec/helpers/boards_helper_spec.rb
@@ -105,10 +105,6 @@ RSpec.describe BoardsHelper do
allow(helper).to receive(:can?).with(user, :admin_issue_board, project).and_return(false)
end
- it 'returns a board_lists_path as lists_endpoint' do
- expect(helper.board_data[:lists_endpoint]).to eq(board_lists_path(project_board))
- end
-
it 'returns board type as parent' do
expect(helper.board_data[:parent]).to eq('project')
end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 51aec7f7119..20718ad2f48 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -776,7 +776,7 @@ RSpec.describe SearchHelper do
end
context 'project data' do
- let(:project) { create(:project) }
+ let_it_be(:project) { create(:project) }
let(:project_metadata) { { project_path: project.path, issues_path: "/issues" } }
let(:scope) { 'issues' }
let(:code_search) { true }
@@ -852,7 +852,7 @@ RSpec.describe SearchHelper do
describe '.search_navigation' do
using RSpec::Parameterized::TableSyntax
let(:user) { build(:user) }
- let(:project) { build(:project) }
+ let_it_be(:project) { build(:project) }
before do
allow(self).to receive(:current_user).and_return(user)
@@ -1068,6 +1068,7 @@ RSpec.describe SearchHelper do
with_them do
it 'converts correctly' do
allow(self).to receive(:search_navigation).with(no_args).and_return(data)
+
expect(search_navigation_json).to instance_exec(&matcher)
end
end
diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb
index 86a1539a836..46a12d8c6f6 100644
--- a/spec/lib/gitlab/data_builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb
@@ -30,6 +30,7 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
expect(attributes[:sha]).to eq(pipeline.sha)
expect(attributes[:tag]).to eq(pipeline.tag)
expect(attributes[:id]).to eq(pipeline.id)
+ expect(attributes[:iid]).to eq(pipeline.iid)
expect(attributes[:source]).to eq(pipeline.source)
expect(attributes[:status]).to eq(pipeline.status)
expect(attributes[:detailed_status]).to eq('passed')
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index ded0ce7eb88..bcdd5646994 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -757,6 +757,58 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
model.add_concurrent_foreign_key(:projects, :users, column: :user_id, reverse_lock_order: true)
end
end
+
+ context 'when creating foreign key for a group of columns' do
+ it 'references the custom target columns when provided', :aggregate_failures do
+ expect(model).to receive(:with_lock_retries).and_yield
+ expect(model).to receive(:execute).with(
+ "ALTER TABLE projects\n" \
+ "ADD CONSTRAINT fk_multiple_columns\n" \
+ "FOREIGN KEY \(partition_number, user_id\)\n" \
+ "REFERENCES users \(partition_number, id\)\n" \
+ "ON DELETE CASCADE\n" \
+ "NOT VALID;\n"
+ )
+
+ model.add_concurrent_foreign_key(
+ :projects,
+ :users,
+ column: [:partition_number, :user_id],
+ target_column: [:partition_number, :id],
+ validate: false,
+ name: :fk_multiple_columns
+ )
+ end
+
+ context 'when foreign key is already defined' do
+ before do
+ expect(model).to receive(:foreign_key_exists?).with(
+ :projects,
+ :users,
+ {
+ column: [:partition_number, :user_id],
+ name: :fk_multiple_columns,
+ on_delete: :cascade,
+ primary_key: [:partition_number, :id]
+ }
+ ).and_return(true)
+ end
+
+ it 'does not create foreign key', :aggregate_failures do
+ expect(model).not_to receive(:with_lock_retries).and_yield
+ expect(model).not_to receive(:execute).with(/FOREIGN KEY/)
+
+ model.add_concurrent_foreign_key(
+ :projects,
+ :users,
+ column: [:partition_number, :user_id],
+ target_column: [:partition_number, :id],
+ validate: false,
+ name: :fk_multiple_columns
+ )
+ end
+ end
+ end
end
end
@@ -813,6 +865,15 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(name).to be_an_instance_of(String)
expect(name.length).to eq(13)
end
+
+ context 'when using multiple columns' do
+ it 'returns the name of the foreign key', :aggregate_failures do
+ result = model.concurrent_foreign_key_name(:table_name, [:partition_number, :id])
+
+ expect(result).to be_an_instance_of(String)
+ expect(result.length).to eq(13)
+ end
+ end
end
describe '#foreign_key_exists?' do
@@ -887,6 +948,62 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'compares by target table if no column given' do
expect(model.foreign_key_exists?(:projects, :other_table)).to be_falsey
end
+
+ context 'with foreign key using multiple columns' do
+ before do
+ key = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(
+ :projects, :users,
+ {
+ column: [:partition_number, :id],
+ name: :fk_projects_users_partition_number_id,
+ on_delete: :cascade,
+ primary_key: [:partition_number, :id]
+ }
+ )
+ allow(model).to receive(:foreign_keys).with(:projects).and_return([key])
+ end
+
+ it 'finds existing foreign keys by columns' do
+ expect(model.foreign_key_exists?(:projects, :users, column: [:partition_number, :id])).to be_truthy
+ end
+
+ it 'finds existing foreign keys by name' do
+ expect(model.foreign_key_exists?(:projects, :users, name: :fk_projects_users_partition_number_id)).to be_truthy
+ end
+
+ it 'finds existing foreign_keys by name and column' do
+ expect(model.foreign_key_exists?(:projects, :users, name: :fk_projects_users_partition_number_id, column: [:partition_number, :id])).to be_truthy
+ end
+
+ it 'finds existing foreign_keys by name, column and on_delete' do
+ expect(model.foreign_key_exists?(:projects, :users, name: :fk_projects_users_partition_number_id, column: [:partition_number, :id], on_delete: :cascade)).to be_truthy
+ end
+
+ it 'finds existing foreign keys by target table only' do
+ expect(model.foreign_key_exists?(:projects, :users)).to be_truthy
+ end
+
+ it 'compares by column name if given' do
+ expect(model.foreign_key_exists?(:projects, :users, column: :id)).to be_falsey
+ end
+
+ it 'compares by target column name if given' do
+ expect(model.foreign_key_exists?(:projects, :users, primary_key: :user_id)).to be_falsey
+ expect(model.foreign_key_exists?(:projects, :users, primary_key: [:partition_number, :id])).to be_truthy
+ end
+
+ it 'compares by foreign key name if given' do
+ expect(model.foreign_key_exists?(:projects, :users, name: :non_existent_foreign_key_name)).to be_falsey
+ end
+
+ it 'compares by foreign key name and column if given' do
+ expect(model.foreign_key_exists?(:projects, :users, name: :non_existent_foreign_key_name, column: [:partition_number, :id])).to be_falsey
+ end
+
+ it 'compares by foreign key name, column and on_delete if given' do
+ expect(model.foreign_key_exists?(:projects, :users, name: :fk_projects_users_partition_number_id, column: [:partition_number, :id], on_delete: :nullify)).to be_falsey
+ end
+ end
end
describe '#disable_statement_timeout' do
diff --git a/spec/lib/gitlab/github_import/issuable_finder_spec.rb b/spec/lib/gitlab/github_import/issuable_finder_spec.rb
index d550f15e8c5..d3236994cef 100644
--- a/spec/lib/gitlab/github_import/issuable_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/issuable_finder_spec.rb
@@ -3,11 +3,20 @@
require 'spec_helper'
RSpec.describe Gitlab::GithubImport::IssuableFinder, :clean_gitlab_redis_cache do
- let(:project) { double(:project, id: 4, group: nil) }
- let(:issue) do
- double(:issue, issuable_type: MergeRequest, issuable_id: 1)
+ let(:project) { double(:project, id: 4, import_data: import_data) }
+ let(:single_endpoint_optional_stage) { false }
+ let(:import_data) do
+ instance_double(
+ ProjectImportData,
+ data: {
+ optional_stages: {
+ single_endpoint_notes_import: single_endpoint_optional_stage
+ }
+ }.deep_stringify_keys
+ )
end
+ let(:issue) { double(:issue, issuable_type: MergeRequest, issuable_id: 1) }
let(:finder) { described_class.new(project, issue) }
describe '#database_id' do
@@ -28,13 +37,10 @@ RSpec.describe Gitlab::GithubImport::IssuableFinder, :clean_gitlab_redis_cache d
end
context 'when group is present' do
- context 'when github_importer_single_endpoint_notes_import feature flag is enabled' do
- it 'reads cache value with longer timeout' do
- project = create(:project, import_url: 'http://t0ken@github.com/user/repo.git')
- group = create(:group, projects: [project])
-
- stub_feature_flags(github_importer_single_endpoint_notes_import: group)
+ context 'when settings single_endpoint_notes_import is enabled' do
+ let(:single_endpoint_optional_stage) { true }
+ it 'reads cache value with longer timeout' do
expect(Gitlab::Cache::Import::Caching)
.to receive(:read)
.with(anything, timeout: Gitlab::Cache::Import::Caching::LONGER_TIMEOUT)
@@ -43,12 +49,8 @@ RSpec.describe Gitlab::GithubImport::IssuableFinder, :clean_gitlab_redis_cache d
end
end
- context 'when github_importer_single_endpoint_notes_import feature flag is disabled' do
+ context 'when settings single_endpoint_notes_import is disabled' do
it 'reads cache value with default timeout' do
- project = double(:project, id: 4, group: create(:group))
-
- stub_feature_flags(github_importer_single_endpoint_notes_import: false)
-
expect(Gitlab::Cache::Import::Caching)
.to receive(:read)
.with(anything, timeout: Gitlab::Cache::Import::Caching::TIMEOUT)
@@ -68,34 +70,25 @@ RSpec.describe Gitlab::GithubImport::IssuableFinder, :clean_gitlab_redis_cache d
finder.cache_database_id(10)
end
- context 'when group is present' do
- context 'when github_importer_single_endpoint_notes_import feature flag is enabled' do
- it 'caches value with longer timeout' do
- project = create(:project, import_url: 'http://t0ken@github.com/user/repo.git')
- group = create(:group, projects: [project])
-
- stub_feature_flags(github_importer_single_endpoint_notes_import: group)
+ context 'when settings single_endpoint_notes_import is enabled' do
+ let(:single_endpoint_optional_stage) { true }
- expect(Gitlab::Cache::Import::Caching)
- .to receive(:write)
- .with(anything, anything, timeout: Gitlab::Cache::Import::Caching::LONGER_TIMEOUT)
+ it 'caches value with longer timeout' do
+ expect(Gitlab::Cache::Import::Caching)
+ .to receive(:write)
+ .with(anything, anything, timeout: Gitlab::Cache::Import::Caching::LONGER_TIMEOUT)
- described_class.new(project, issue).cache_database_id(10)
- end
+ described_class.new(project, issue).cache_database_id(10)
end
+ end
- context 'when github_importer_single_endpoint_notes_import feature flag is disabled' do
- it 'caches value with default timeout' do
- project = double(:project, id: 4, group: create(:group))
-
- stub_feature_flags(github_importer_single_endpoint_notes_import: false)
-
- expect(Gitlab::Cache::Import::Caching)
- .to receive(:write)
- .with(anything, anything, timeout: Gitlab::Cache::Import::Caching::TIMEOUT)
+ context 'when settings single_endpoint_notes_import is disabled' do
+ it 'caches value with default timeout' do
+ expect(Gitlab::Cache::Import::Caching)
+ .to receive(:write)
+ .with(anything, anything, timeout: Gitlab::Cache::Import::Caching::TIMEOUT)
- described_class.new(project, issue).cache_database_id(10)
- end
+ described_class.new(project, issue).cache_database_id(10)
end
end
end
diff --git a/spec/lib/gitlab/github_import/settings_spec.rb b/spec/lib/gitlab/github_import/settings_spec.rb
new file mode 100644
index 00000000000..ad0c47e8e8a
--- /dev/null
+++ b/spec/lib/gitlab/github_import/settings_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Settings do
+ subject(:settings) { described_class.new(project) }
+
+ let_it_be(:project) { create(:project) }
+
+ let(:optional_stages) do
+ {
+ single_endpoint_issue_events_import: true,
+ single_endpoint_notes_import: false,
+ attachments_import: false
+ }
+ end
+
+ describe '.stages_array' do
+ let(:expected_list) do
+ stages = described_class::OPTIONAL_STAGES
+ [
+ {
+ name: 'single_endpoint_issue_events_import',
+ label: stages[:single_endpoint_issue_events_import][:label],
+ details: stages[:single_endpoint_issue_events_import][:details]
+ },
+ {
+ name: 'single_endpoint_notes_import',
+ label: stages[:single_endpoint_notes_import][:label],
+ details: stages[:single_endpoint_notes_import][:details]
+ },
+ {
+ name: 'attachments_import',
+ label: stages[:attachments_import][:label].strip,
+ details: stages[:attachments_import][:details]
+ }
+ ]
+ end
+
+ it 'returns stages list as array' do
+ expect(described_class.stages_array).to match_array(expected_list)
+ end
+ end
+
+ describe '#write' do
+ let(:data_input) do
+ {
+ single_endpoint_issue_events_import: true,
+ single_endpoint_notes_import: 'false',
+ attachments_import: nil,
+ foo: :bar
+ }.stringify_keys
+ end
+
+ it 'puts optional steps flags into projects import_data' do
+ settings.write(data_input)
+
+ expect(project.import_data.data['optional_stages'])
+ .to eq optional_stages.stringify_keys
+ end
+ end
+
+ describe '#enabled?' do
+ it 'returns is enabled or not specific optional stage' do
+ project.create_or_update_import_data(data: { optional_stages: optional_stages })
+
+ expect(settings.enabled?(:single_endpoint_issue_events_import)).to eq true
+ expect(settings.enabled?(:single_endpoint_notes_import)).to eq false
+ expect(settings.enabled?(:attachments_import)).to eq false
+ end
+ end
+
+ describe '#disabled?' do
+ it 'returns is disabled or not specific optional stage' do
+ project.create_or_update_import_data(data: { optional_stages: optional_stages })
+
+ expect(settings.disabled?(:single_endpoint_issue_events_import)).to eq false
+ expect(settings.disabled?(:single_endpoint_notes_import)).to eq true
+ expect(settings.disabled?(:attachments_import)).to eq true
+ end
+ end
+end
diff --git a/spec/models/ci/resource_group_spec.rb b/spec/models/ci/resource_group_spec.rb
index 76e74f3193c..e8eccc233db 100644
--- a/spec/models/ci/resource_group_spec.rb
+++ b/spec/models/ci/resource_group_spec.rb
@@ -3,8 +3,10 @@
require 'spec_helper'
RSpec.describe Ci::ResourceGroup do
+ let_it_be(:group) { create(:group) }
+
it_behaves_like 'cleanup by a loose foreign key' do
- let!(:parent) { create(:project) }
+ let!(:parent) { create(:project, group: group) }
let!(:model) { create(:ci_resource_group, project: parent) }
end
@@ -94,7 +96,7 @@ RSpec.describe Ci::ResourceGroup do
describe '#upcoming_processables' do
subject { resource_group.upcoming_processables }
- let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository, group: group) }
let_it_be(:pipeline_1) { create(:ci_pipeline, project: project) }
let_it_be(:pipeline_2) { create(:ci_pipeline, project: project) }
diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb
index 71d3a997ad7..015a09d41ab 100644
--- a/spec/requests/api/import_github_spec.rb
+++ b/spec/requests/api/import_github_spec.rb
@@ -70,7 +70,8 @@ RSpec.describe API::ImportGithub do
target_namespace: user.namespace_path,
personal_access_token: token,
repo_id: non_existing_record_id,
- github_hostname: "https://github.somecompany.com/"
+ github_hostname: "https://github.somecompany.com/",
+ optional_stages: { attachments_import: true }
}
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to be_a Hash
diff --git a/spec/requests/boards/lists_controller_spec.rb b/spec/requests/boards/lists_controller_spec.rb
deleted file mode 100644
index 47f4925d5b0..00000000000
--- a/spec/requests/boards/lists_controller_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Boards::ListsController do
- describe '#index' do
- let(:board) { create(:board) }
- let(:user) { board.project.first_owner }
-
- it 'does not have N+1 queries' do
- login_as(user)
-
- # First request has more queries because we create the default `backlog` list
- get board_lists_path(board)
-
- create(:list, board: board)
-
- control_count = ActiveRecord::QueryRecorder.new { get board_lists_path(board) }.count
-
- create_list(:list, 5, board: board)
-
- expect { get board_lists_path(board) }.not_to exceed_query_limit(control_count)
- end
- end
-end
diff --git a/spec/services/boards/lists/generate_service_spec.rb b/spec/services/boards/lists/generate_service_spec.rb
deleted file mode 100644
index 9597c8e0f54..00000000000
--- a/spec/services/boards/lists/generate_service_spec.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Boards::Lists::GenerateService do
- describe '#execute' do
- let(:project) { create(:project) }
- let(:board) { create(:board, project: project) }
- let(:user) { create(:user) }
-
- subject(:service) { described_class.new(project, user) }
-
- before do
- project.add_developer(user)
- end
-
- context 'when board lists is empty' do
- it 'creates the default lists' do
- expect { service.execute(board) }.to change(board.lists, :count).by(2)
- end
- end
-
- context 'when board lists is not empty' do
- it 'does not creates the default lists' do
- create(:list, board: board)
-
- expect { service.execute(board) }.not_to change(board.lists, :count)
- end
- end
-
- context 'when project labels does not contains any list label' do
- it 'creates labels' do
- expect { service.execute(board) }.to change(project.labels, :count).by(2)
- end
- end
-
- context 'when project labels contains some of list label' do
- it 'creates the missing labels' do
- create(:label, project: project, name: 'Doing')
-
- expect { service.execute(board) }.to change(project.labels, :count).by(1)
- end
- end
- end
-end
diff --git a/spec/services/import/github_service_spec.rb b/spec/services/import/github_service_spec.rb
index 67a2c237e43..38d84009f08 100644
--- a/spec/services/import/github_service_spec.rb
+++ b/spec/services/import/github_service_spec.rb
@@ -6,7 +6,16 @@ RSpec.describe Import::GithubService do
let_it_be(:user) { create(:user) }
let_it_be(:token) { 'complex-token' }
let_it_be(:access_params) { { github_access_token: 'github-complex-token' } }
- let_it_be(:params) { { repo_id: 123, new_name: 'new_repo', target_namespace: 'root' } }
+ let(:settings) { instance_double(Gitlab::GithubImport::Settings) }
+ let(:optional_stages) { nil }
+ let(:params) do
+ {
+ repo_id: 123,
+ new_name: 'new_repo',
+ target_namespace: 'root',
+ optional_stages: optional_stages
+ }
+ end
subject(:github_importer) { described_class.new(client, user, params) }
@@ -16,6 +25,12 @@ RSpec.describe Import::GithubService do
shared_examples 'handles errors' do |klass|
let(:client) { klass.new(token) }
+ let(:project_double) { instance_double(Project, persisted?: true) }
+
+ before do
+ allow(Gitlab::GithubImport::Settings).to receive(:new).with(project_double).and_return(settings)
+ allow(settings).to receive(:write).with(optional_stages)
+ end
context 'do not raise an exception on input error' do
let(:exception) { Octokit::ClientError.new(status: 404, body: 'Not Found') }
@@ -62,13 +77,14 @@ RSpec.describe Import::GithubService do
expect(client).to receive(:repository).and_return(repository_double)
allow_next_instance_of(Gitlab::LegacyGithubImport::ProjectCreator) do |creator|
- allow(creator).to receive(:execute).and_return(double(persisted?: true))
+ allow(creator).to receive(:execute).and_return(project_double)
end
end
context 'when there is no repository size limit defined' do
it 'skips the check and succeeds' do
expect(subject.execute(access_params, :github)).to include(status: :success)
+ expect(settings).to have_received(:write).with(nil)
end
end
@@ -81,6 +97,7 @@ RSpec.describe Import::GithubService do
it 'succeeds when the repository is smaller than the limit' do
expect(subject.execute(access_params, :github)).to include(status: :success)
+ expect(settings).to have_received(:write).with(nil)
end
it 'returns error when the repository is larger than the limit' do
@@ -100,6 +117,7 @@ RSpec.describe Import::GithubService do
context 'when application size limit is defined' do
it 'succeeds when the repository is smaller than the limit' do
expect(subject.execute(access_params, :github)).to include(status: :success)
+ expect(settings).to have_received(:write).with(nil)
end
it 'returns error when the repository is larger than the limit' do
@@ -109,6 +127,22 @@ RSpec.describe Import::GithubService do
end
end
end
+
+ context 'when optional stages params present' do
+ let(:optional_stages) do
+ {
+ single_endpoint_issue_events_import: true,
+ single_endpoint_notes_import: 'false',
+ attachments_import: false
+ }
+ end
+
+ it 'saves optional stages choice to import_data' do
+ subject.execute(access_params, :github)
+
+ expect(settings).to have_received(:write).with(optional_stages)
+ end
+ end
end
context 'when import source is disabled' do
@@ -170,7 +204,7 @@ RSpec.describe Import::GithubService do
include_examples 'handles errors', Gitlab::GithubImport::Client
end
- context 'when remove_legacy_github_client feature flag is enabled' do
+ context 'when remove_legacy_github_client feature flag is disabled' do
before do
stub_feature_flags(remove_legacy_github_client: false)
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 9f96162e629..74f1a712c30 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-if $".include?(File.expand_path('fast_spec_helper.rb', __dir__))
+if $LOADED_FEATURES.include?(File.expand_path('fast_spec_helper.rb', __dir__))
warn 'Detected fast_spec_helper is loaded first than spec_helper.'
warn 'If running test files using both spec_helper and fast_spec_helper,'
warn 'make sure spec_helper is loaded first, or run rspec with `-r spec_helper`.'
diff --git a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
index 095c48cade8..84dc2b20ddc 100644
--- a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
+++ b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
@@ -3,6 +3,7 @@
RSpec.shared_examples 'search timeouts' do |scope|
context 'when search times out' do
before do
+ stub_feature_flags(search_page_vertical_nav: false)
allow_next_instance_of(SearchService) do |service|
allow(service).to receive(:search_objects).and_raise(ActiveRecord::QueryCanceled)
end
diff --git a/spec/views/search/_results.html.haml_spec.rb b/spec/views/search/_results.html.haml_spec.rb
index 72e2d7131c0..2149c394320 100644
--- a/spec/views/search/_results.html.haml_spec.rb
+++ b/spec/views/search/_results.html.haml_spec.rb
@@ -12,6 +12,13 @@ RSpec.describe 'search/_results' do
controller.params[:action] = 'show'
controller.params[:search] = term
+ allow(self).to receive(:current_user).and_return(user)
+ allow(@search_results).to receive(:formatted_count).with(scope).and_return(10)
+ allow(self).to receive(:search_count_path).with(any_args).and_return("test count link")
+ allow(self).to receive(:search_path).with(any_args).and_return("link test")
+
+ stub_feature_flags(search_page_vertical_nav: false)
+
create_list(:issue, 3)
@search_objects = search_objects
@@ -147,7 +154,7 @@ RSpec.describe 'search/_results' do
it 'does not render the sidebar' do
render
- expect(rendered).not_to have_selector('#js-search-sidebar')
+ expect(rendered).not_to have_selector('form.search-sidebar')
end
end
end
diff --git a/spec/views/search/show.html.haml_spec.rb b/spec/views/search/show.html.haml_spec.rb
index a336ec91ff2..565dadd64fe 100644
--- a/spec/views/search/show.html.haml_spec.rb
+++ b/spec/views/search/show.html.haml_spec.rb
@@ -4,93 +4,118 @@ require 'spec_helper'
RSpec.describe 'search/show' do
let(:search_term) { nil }
+ let(:user) { build(:user) }
before do
stub_template "search/_category.html.haml" => 'Category Partial'
stub_template "search/_results.html.haml" => 'Results Partial'
-
- @search_term = search_term
-
- render
end
- context 'when the search page is opened' do
- it 'displays the title' do
- expect(rendered).to have_selector('h1.page-title', text: 'Search')
- expect(rendered).not_to have_selector('h1.page-title code')
+ context 'feature flag enabled' do
+ before do
+ allow(self).to receive(:current_user).and_return(user)
+ @search_term = search_term
+
+ render
end
- it 'does not render partials' do
- expect(rendered).not_to render_template('search/_category')
- expect(rendered).not_to render_template('search/_results')
+ context 'when search term is supplied' do
+ let(:search_term) { 'Search Foo' }
+
+ it 'will not render category partial' do
+ expect(rendered).not_to render_template('search/_category')
+ expect(rendered).to render_template('search/_results')
+ end
end
end
- context 'when search term is supplied' do
- let(:search_term) { 'Search Foo' }
+ context 'feature flag disabled' do
+ before do
+ stub_feature_flags(search_page_vertical_nav: false)
- it 'renders partials' do
- expect(rendered).to render_template('search/_category')
- expect(rendered).to render_template('search/_results')
+ @search_term = search_term
+
+ render
end
- context 'unfurling support' do
- let(:group) { build(:group) }
- let(:search_results) do
- instance_double(Gitlab::GroupSearchResults).tap do |double|
- allow(double).to receive(:formatted_count).and_return(0)
- end
+ context 'when the search page is opened' do
+ it 'displays the title' do
+ expect(rendered).to have_selector('h1.page-title', text: 'Search')
+ expect(rendered).not_to have_selector('h1.page-title code')
end
- before do
- assign(:search_results, search_results)
- assign(:scope, 'issues')
- assign(:group, group)
+ it 'does not render partials' do
+ expect(rendered).not_to render_template('search/_category')
+ expect(rendered).not_to render_template('search/_results')
end
+ end
+
+ context 'when search term is supplied' do
+ let(:search_term) { 'Search Foo' }
+
+ it 'renders partials' do
+ expect(rendered).to render_template('search/_category')
+ expect(rendered).to render_template('search/_results')
+ end
+
+ context 'unfurling support' do
+ let(:group) { build(:group) }
+ let(:search_results) do
+ instance_double(Gitlab::GroupSearchResults).tap do |double|
+ allow(double).to receive(:formatted_count).and_return(0)
+ end
+ end
- context 'search with full count' do
before do
- assign(:without_count, false)
+ assign(:search_results, search_results)
+ assign(:scope, 'issues')
+ assign(:group, group)
end
- it 'renders meta tags for a group' do
- render
+ context 'search with full count' do
+ before do
+ assign(:without_count, false)
+ end
- expect(view.page_description).to match(/\d+ issues for term '#{search_term}'/)
- expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
- end
+ it 'renders meta tags for a group' do
+ render
- it 'renders meta tags for both group and project' do
- project = build(:project, group: group)
- assign(:project, project)
+ expect(view.page_description).to match(/\d+ issues for term '#{search_term}'/)
+ expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
+ end
- render
+ it 'renders meta tags for both group and project' do
+ project = build(:project, group: group)
+ assign(:project, project)
- expect(view.page_description).to match(/\d+ issues for term '#{search_term}'/)
- expect(view.page_card_attributes).to eq("Namespace" => group.full_path, "Project" => project.full_path)
- end
- end
+ render
- context 'search without full count' do
- before do
- assign(:without_count, true)
+ expect(view.page_description).to match(/\d+ issues for term '#{search_term}'/)
+ expect(view.page_card_attributes).to eq("Namespace" => group.full_path, "Project" => project.full_path)
+ end
end
- it 'renders meta tags for a group' do
- render
+ context 'search without full count' do
+ before do
+ assign(:without_count, true)
+ end
- expect(view.page_description).to match(/issues results for term '#{search_term}'/)
- expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
- end
+ it 'renders meta tags for a group' do
+ render
+
+ expect(view.page_description).to match(/issues results for term '#{search_term}'/)
+ expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
+ end
- it 'renders meta tags for both group and project' do
- project = build(:project, group: group)
- assign(:project, project)
+ it 'renders meta tags for both group and project' do
+ project = build(:project, group: group)
+ assign(:project, project)
- render
+ render
- expect(view.page_description).to match(/issues results for term '#{search_term}'/)
- expect(view.page_card_attributes).to eq("Namespace" => group.full_path, "Project" => project.full_path)
+ expect(view.page_description).to match(/issues results for term '#{search_term}'/)
+ expect(view.page_card_attributes).to eq("Namespace" => group.full_path, "Project" => project.full_path)
+ end
end
end
end
diff --git a/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb
index 015a2beed7e..33e2443d106 100644
--- a/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb
@@ -7,7 +7,12 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
let(:project) { create(:project) }
let!(:group) { create(:group, projects: [project]) }
- let(:feature_flag_state) { [group] }
+ let(:settings) { ::Gitlab::GithubImport::Settings.new(project) }
+ let(:stage_enabled) { true }
+
+ before do
+ settings.write({ attachments_import: stage_enabled })
+ end
describe '#import' do
let(:releases_importer) { instance_double('Gitlab::GithubImport::Importer::Attachments::ReleasesImporter') }
@@ -16,10 +21,6 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
let(:releases_waiter) { Gitlab::JobWaiter.new(2, '123') }
let(:notes_waiter) { Gitlab::JobWaiter.new(3, '234') }
- before do
- stub_feature_flags(github_importer_attachments_import: feature_flag_state)
- end
-
it 'imports release attachments' do
expect(Gitlab::GithubImport::Importer::Attachments::ReleasesImporter)
.to receive(:new)
@@ -40,8 +41,8 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
worker.import(client, project)
end
- context 'when feature flag is disabled' do
- let(:feature_flag_state) { false }
+ context 'when stage is disabled' do
+ let(:stage_enabled) { false }
it 'skips release attachments import and calls next stage' do
expect(Gitlab::GithubImport::Importer::Attachments::ReleasesImporter).not_to receive(:new)
diff --git a/spec/workers/gitlab/github_import/stage/import_issue_events_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_issue_events_worker_spec.rb
index 932152c0764..199d1b9a3ca 100644
--- a/spec/workers/gitlab/github_import/stage/import_issue_events_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/import_issue_events_worker_spec.rb
@@ -7,23 +7,21 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportIssueEventsWorker do
let(:project) { create(:project) }
let!(:group) { create(:group, projects: [project]) }
- let(:feature_flag_state) { [group] }
- let(:single_endpoint_feature_flag_state) { [group] }
+ let(:settings) { ::Gitlab::GithubImport::Settings.new(project) }
+ let(:stage_enabled) { true }
+
+ before do
+ settings.write({ single_endpoint_issue_events_import: stage_enabled })
+ end
describe '#import' do
let(:importer) { instance_double('Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter') }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- before do
- stub_feature_flags(github_importer_single_endpoint_issue_events_import: single_endpoint_feature_flag_state)
- stub_feature_flags(github_importer_issue_events_import: feature_flag_state)
- end
-
- context 'when single endpoint feature flag enabled' do
- it 'imports all the issue events' do
+ context 'when stage is enabled' do
+ it 'imports issue events' do
waiter = Gitlab::JobWaiter.new(2, '123')
- expect(Gitlab::GithubImport::Importer::IssueEventsImporter).not_to receive(:new)
expect(Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter)
.to receive(:new)
.with(project, client)
@@ -39,35 +37,11 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportIssueEventsWorker do
end
end
- context 'when import issue events feature flag enabled' do
- let(:single_endpoint_feature_flag_state) { false }
-
- it 'imports the issue events partly' do
- waiter = Gitlab::JobWaiter.new(2, '123')
-
- expect(Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter).not_to receive(:new)
- expect(Gitlab::GithubImport::Importer::IssueEventsImporter)
- .to receive(:new)
- .with(project, client)
- .and_return(importer)
-
- expect(importer).to receive(:execute).and_return(waiter)
-
- expect(Gitlab::GithubImport::AdvanceStageWorker)
- .to receive(:perform_async)
- .with(project.id, { '123' => 2 }, :notes)
-
- worker.import(client, project)
- end
- end
-
- context 'when feature flags are disabled' do
- let(:feature_flag_state) { false }
- let(:single_endpoint_feature_flag_state) { false }
+ context 'when stage is disabled' do
+ let(:stage_enabled) { false }
it 'skips issue events import and calls next stage' do
expect(Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter).not_to receive(:new)
- expect(Gitlab::GithubImport::Importer::IssueEventsImporter).not_to receive(:new)
expect(Gitlab::GithubImport::AdvanceStageWorker).to receive(:perform_async).with(project.id, {}, :notes)
worker.import(client, project)
diff --git a/spec/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker_spec.rb
index a88256b3cae..beef0864715 100644
--- a/spec/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker_spec.rb
@@ -6,6 +6,13 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportIssuesAndDiffNotesWorker do
let(:project) { create(:project) }
let(:worker) { described_class.new }
+ let(:settings) { ::Gitlab::GithubImport::Settings.new(project) }
+ let(:single_endpoint_optional_stage) { true }
+
+ before do
+ settings.write({ single_endpoint_notes_import: single_endpoint_optional_stage })
+ end
+
describe '#import' do
it 'imports the issues and diff notes' do
client = double(:client)
@@ -33,37 +40,18 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportIssuesAndDiffNotesWorker do
end
describe '#importers' do
- context 'when project group is present' do
- let_it_be(:project) { create(:project) }
- let_it_be(:group) { create(:group, projects: [project]) }
-
- context 'when feature flag github_importer_single_endpoint_notes_import is enabled' do
- it 'includes single endpoint diff notes importer' do
- project = create(:project)
- group = create(:group, projects: [project])
-
- stub_feature_flags(github_importer_single_endpoint_notes_import: group)
-
- expect(worker.importers(project)).to contain_exactly(
- Gitlab::GithubImport::Importer::IssuesImporter,
- Gitlab::GithubImport::Importer::SingleEndpointDiffNotesImporter
- )
- end
- end
-
- context 'when feature flag github_importer_single_endpoint_notes_import is disabled' do
- it 'includes default diff notes importer' do
- stub_feature_flags(github_importer_single_endpoint_notes_import: false)
-
- expect(worker.importers(project)).to contain_exactly(
- Gitlab::GithubImport::Importer::IssuesImporter,
- Gitlab::GithubImport::Importer::DiffNotesImporter
- )
- end
+ context 'when optional stage single_endpoint_notes_import is enabled' do
+ it 'includes single endpoint diff notes importer' do
+ expect(worker.importers(project)).to contain_exactly(
+ Gitlab::GithubImport::Importer::IssuesImporter,
+ Gitlab::GithubImport::Importer::SingleEndpointDiffNotesImporter
+ )
end
end
- context 'when project group is missing' do
+ context 'when optional stage single_endpoint_notes_import is disabled' do
+ let(:single_endpoint_optional_stage) { false }
+
it 'includes default diff notes importer' do
expect(worker.importers(project)).to contain_exactly(
Gitlab::GithubImport::Importer::IssuesImporter,
diff --git a/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb
index adf20d24a7e..dbcf2083ec1 100644
--- a/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb
@@ -6,6 +6,13 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportNotesWorker do
let(:project) { create(:project) }
let(:worker) { described_class.new }
+ let(:settings) { ::Gitlab::GithubImport::Settings.new(project) }
+ let(:single_endpoint_optional_stage) { true }
+
+ before do
+ settings.write({ single_endpoint_notes_import: single_endpoint_optional_stage })
+ end
+
describe '#import' do
it 'imports all the notes' do
client = double(:client)
@@ -33,37 +40,19 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportNotesWorker do
end
describe '#importers' do
- context 'when project group is present' do
- let_it_be(:project) { create(:project) }
- let_it_be(:group) { create(:group, projects: [project]) }
-
- context 'when feature flag github_importer_single_endpoint_notes_import is enabled' do
- it 'includes single endpoint mr and issue notes importers' do
- project = create(:project)
- group = create(:group, projects: [project])
-
- stub_feature_flags(github_importer_single_endpoint_notes_import: group)
-
- expect(worker.importers(project)).to contain_exactly(
- Gitlab::GithubImport::Importer::SingleEndpointMergeRequestNotesImporter,
- Gitlab::GithubImport::Importer::SingleEndpointIssueNotesImporter
- )
- end
- end
-
- context 'when feature flag github_importer_single_endpoint_notes_import is disabled' do
- it 'includes default notes importer' do
- stub_feature_flags(github_importer_single_endpoint_notes_import: false)
-
- expect(worker.importers(project)).to contain_exactly(
- Gitlab::GithubImport::Importer::NotesImporter
- )
- end
+ context 'when settings single_endpoint_notes_import is enabled' do
+ it 'includes single endpoint mr and issue notes importers' do
+ expect(worker.importers(project)).to contain_exactly(
+ Gitlab::GithubImport::Importer::SingleEndpointMergeRequestNotesImporter,
+ Gitlab::GithubImport::Importer::SingleEndpointIssueNotesImporter
+ )
end
end
- context 'when project group is missing' do
- it 'includes default diff notes importer' do
+ context 'when settings single_endpoint_notes_import is disabled' do
+ let(:single_endpoint_optional_stage) { false }
+
+ it 'includes default notes importer' do
expect(worker.importers(project)).to contain_exactly(
Gitlab::GithubImport::Importer::NotesImporter
)