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"
>
-
-
-
@@ -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) 1 |
| 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) |
+
+
+
+
+
+ Dependency Scanning will only parse go.sum if it's unable to generate the build list
+ used by the Go project.
+
+
+
+
+
#### 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)3, [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)4 |
@@ -416,6 +434,13 @@ To support the following package managers, the GitLab analyzers proceed in two s
This test confirms that if a Pipfile.lock file is found, it will be used by Gemnasium to scan the exact package versions listed in this file.
+
+
+
+ Because of the implementation of go build, the Go build process requires network access, a pre-loaded modcache via go mod download, or vendored dependencies. For more information,
+ refer to the Go documentation on compiling packages and dependencies.
+
+
@@ -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#, 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 `` unresolved dependencies
The error message `Project has 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("doesn't render StatusFilter", () => {
+ expect(findStatusFilter().exists()).toBe(false);
+ });
+
+ it("doesn't render ConfidentialityFilter", () => {
+ expect(findConfidentialityFilter().exists()).toBe(false);
+ });
+
+ it("doesn't render ApplyButton", () => {
+ expect(findApplyButton().exists()).toBe(false);
+ });
});
- it('renders StatusFilter always', () => {
- expect(findStatusFilter().exists()).toBe(true);
- });
+ describe('scope=issues', () => {
+ beforeEach(() => {
+ createComponent({ urlQuery: MOCK_QUERY });
+ });
+ it('renders StatusFilter', () => {
+ expect(findStatusFilter().exists()).toBe(true);
+ });
- it('renders ConfidentialityFilter always', () => {
- expect(findConfidentialityFilter().exists()).toBe(true);
- });
+ it('renders ConfidentialityFilter', () => {
+ expect(findConfidentialityFilter().exists()).toBe(true);
+ });
- it('renders ApplyButton always', () => {
- expect(findApplyButton().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
+ context 'when settings single_endpoint_notes_import is enabled' do
+ let(:single_endpoint_optional_stage) { true }
+
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)
-
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])
+ context 'when settings single_endpoint_notes_import is enabled' do
+ let(:single_endpoint_optional_stage) { true }
- stub_feature_flags(github_importer_single_endpoint_notes_import: group)
+ 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)
- 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))
+ 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)
- 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)
-
- 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)
+ 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
+
+ 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
- end
- before do
- assign(:search_results, search_results)
- assign(:scope, 'issues')
- assign(:group, group)
- 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)
+ it 'renders meta tags for a group' do
+ render
+
+ 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 both group and project' do
+ project = build(:project, group: group)
+ assign(:project, project)
+
+ render
+
+ 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 both group and project' do
- project = build(:project, group: group)
- assign(:project, project)
+ context 'search without full count' do
+ before do
+ assign(:without_count, true)
+ end
- render
+ it 'renders meta tags for a group' do
+ render
- 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
+ expect(view.page_description).to match(/issues results for term '#{search_term}'/)
+ expect(view.page_card_attributes).to eq("Namespace" => group.full_path)
+ end
- context 'search without full count' do
- before do
- assign(:without_count, true)
- end
+ it 'renders meta tags for both group and project' do
+ project = build(:project, group: group)
+ assign(:project, project)
- it 'renders meta tags for a group' do
- 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)
- end
-
- it 'renders meta tags for both group and project' do
- project = build(:project, group: group)
- assign(:project, project)
-
- 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
)