summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml2
-rw-r--r--.rubocop_todo/rspec/verified_doubles.yml1
-rw-r--r--app/assets/javascripts/blob/3d_viewer/index.js7
-rw-r--r--app/assets/javascripts/blob/3d_viewer/mesh_object.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue5
-rw-r--r--app/controllers/projects/pipelines/tests_controller.rb3
-rw-r--r--app/graphql/resolvers/ci/test_suite_resolver.rb3
-rw-r--r--app/graphql/resolvers/concerns/issue_resolver_arguments.rb3
-rw-r--r--app/graphql/types/base_field.rb31
-rw-r--r--app/graphql/types/ci/config_variable_type.rb22
-rw-r--r--app/graphql/types/ci/group_variable_type.rb16
-rw-r--r--app/graphql/types/ci/instance_variable_type.rb28
-rw-r--r--app/graphql/types/ci/manual_variable_type.rb12
-rw-r--r--app/graphql/types/ci/project_variable_type.rb12
-rw-r--r--app/graphql/types/ci/variable_interface.rb24
-rw-r--r--app/graphql/types/project_type.rb21
-rw-r--r--app/models/ci/build.rb28
-rw-r--r--app/services/ci/build_report_result_service.rb3
-rw-r--r--app/services/ci/test_failure_history_service.rb4
-rw-r--r--doc/api/graphql/reference/index.md28
-rw-r--r--doc/development/api_graphql_styleguide.md6
-rw-r--r--lib/gitlab/ci/parsers/test/junit.rb4
-rw-r--r--lib/gitlab/import_export/group/import_export.yml9
-rw-r--r--lib/gitlab/import_export/group/relation_factory.rb9
-rw-r--r--package.json4
-rw-r--r--rubocop/code_reuse_helpers.rb6
-rw-r--r--rubocop/cop/gitlab/mark_used_feature_flags.rb20
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js61
-rw-r--r--spec/graphql/features/feature_flag_spec.rb52
-rw-r--r--spec/graphql/types/base_field_spec.rb99
-rw-r--r--spec/graphql/types/ci/config_variable_type_spec.rb7
-rw-r--r--spec/graphql/types/ci/instance_variable_type_spec.rb2
-rw-r--r--spec/graphql/types/ci/manual_variable_type_spec.rb2
-rw-r--r--spec/graphql/types/ci/variable_interface_spec.rb2
-rw-r--r--spec/graphql/types/project_type_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/parsers/test/junit_spec.rb8
-rw-r--r--spec/models/ci/build_spec.rb78
-rw-r--r--spec/models/ci/pipeline_spec.rb32
-rw-r--r--spec/requests/api/graphql/ci/config_variables_spec.rb92
-rw-r--r--spec/rubocop/code_reuse_helpers_spec.rb25
-rw-r--r--spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb13
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml3
-rw-r--r--yarn.lock18
43 files changed, 404 insertions, 404 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index db607ec36ec..43b4e46171b 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -569,6 +569,8 @@ RSpec/ImplicitSubject:
RSpec/ReceiveNever:
Enabled: false
+# Already covered by `RSpec::Configuration#on_potential_false_positives = :raise`.
+# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86429
RSpec/UnspecifiedException:
Enabled: false
diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml
index 819685b38ba..ee24ed73a27 100644
--- a/.rubocop_todo/rspec/verified_doubles.yml
+++ b/.rubocop_todo/rspec/verified_doubles.yml
@@ -90,7 +90,6 @@ RSpec/VerifiedDoubles:
- ee/spec/lib/sidebars/groups/menus/analytics_menu_spec.rb
- ee/spec/lib/system_check/geo/geo_database_configured_check_spec.rb
- ee/spec/models/app_sec/fuzzing/api/ci_configuration_spec.rb
- - ee/spec/models/ee/approvable_spec.rb
- ee/spec/models/concerns/geo/verification_state_spec.rb
- ee/spec/models/ee/ci/job_artifact_spec.rb
- ee/spec/models/ee/user_spec.rb
diff --git a/app/assets/javascripts/blob/3d_viewer/index.js b/app/assets/javascripts/blob/3d_viewer/index.js
index d4efe409fef..2831c37838b 100644
--- a/app/assets/javascripts/blob/3d_viewer/index.js
+++ b/app/assets/javascripts/blob/3d_viewer/index.js
@@ -1,11 +1,8 @@
-import OrbitControlsClass from 'three-orbit-controls';
-import STLLoaderClass from 'three-stl-loader';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
+import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';
import * as THREE from 'three/build/three.module';
import MeshObject from './mesh_object';
-const STLLoader = STLLoaderClass(THREE);
-const OrbitControls = OrbitControlsClass(THREE);
-
export default class Renderer {
constructor(container) {
this.renderWrapper = this.render.bind(this);
diff --git a/app/assets/javascripts/blob/3d_viewer/mesh_object.js b/app/assets/javascripts/blob/3d_viewer/mesh_object.js
index c55a9ca8926..5322dc00e86 100644
--- a/app/assets/javascripts/blob/3d_viewer/mesh_object.js
+++ b/app/assets/javascripts/blob/3d_viewer/mesh_object.js
@@ -22,7 +22,7 @@ export default class MeshObject extends Mesh {
if (this.geometry.boundingSphere.radius > 4) {
const scale = 4 / this.geometry.boundingSphere.radius;
- this.geometry.applyMatrix(new Matrix4().makeScale(scale, scale, scale));
+ this.geometry.applyMatrix4(new Matrix4().makeScale(scale, scale, scale));
this.geometry.computeBoundingSphere();
this.position.x = -this.geometry.boundingSphere.center.x;
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
index 4e580c88ef3..5581863591c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
@@ -130,11 +130,16 @@ export default {
},
async fetchExpandedContent() {
this.isLoadingExpandedContent = true;
+ this.error = null;
try {
await this.fetch(this.fetchExpandedData, FETCH_TYPE_EXPANDED);
} catch {
this.error = this.errorText;
+
+ // Reset these values so that we allow refetching
+ this.isExpandedForTheFirstTime = true;
+ this.isCollapsed = true;
}
this.isLoadingExpandedContent = false;
diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb
index 8ac370b1bd4..d77cf095a4f 100644
--- a/app/controllers/projects/pipelines/tests_controller.rb
+++ b/app/controllers/projects/pipelines/tests_controller.rb
@@ -51,7 +51,8 @@ module Projects
def test_suite
suite = builds.sum do |build|
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report.get_suite(build.test_suite_name)
end
Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, project).load!
diff --git a/app/graphql/resolvers/ci/test_suite_resolver.rb b/app/graphql/resolvers/ci/test_suite_resolver.rb
index f758e217b47..a2d3af9c664 100644
--- a/app/graphql/resolvers/ci/test_suite_resolver.rb
+++ b/app/graphql/resolvers/ci/test_suite_resolver.rb
@@ -28,7 +28,8 @@ module Resolvers
def load_test_suite_data(builds)
suite = builds.sum do |build|
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report.get_suite(build.test_suite_name)
end
Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, pipeline.project).load!
diff --git a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
index 15ab4435204..8295bd58388 100644
--- a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
+++ b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
@@ -84,6 +84,8 @@ module IssueResolverArguments
end
def ready?(**args)
+ args[:not] = args[:not].to_h if args[:not].present?
+
params_not_mutually_exclusive(args, mutually_exclusive_assignee_username_args)
params_not_mutually_exclusive(args, mutually_exclusive_milestone_args)
params_not_mutually_exclusive(args.fetch(:not, {}), mutually_exclusive_milestone_args)
@@ -114,7 +116,6 @@ module IssueResolverArguments
def prepare_finder_params(args)
params = super(args)
- params[:not] = params[:not].to_h if params[:not].present?
params[:iids] ||= [params.delete(:iid)].compact if params[:iid]
params[:attempt_project_search_optimizations] = true if params[:search].present?
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 1c43432594a..6f64e5b5053 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -17,8 +17,6 @@ module Types
@requires_argument = !!kwargs.delete(:requires_argument)
@authorize = Array.wrap(kwargs.delete(:authorize))
kwargs[:complexity] = field_complexity(kwargs[:resolver_class], kwargs[:complexity])
- @feature_flag = kwargs[:_deprecated_feature_flag]
- kwargs = check_feature_flag(kwargs)
@deprecation = gitlab_deprecation(kwargs)
after_connection_extensions = kwargs.delete(:late_extensions) || []
@@ -91,16 +89,8 @@ module Types
@constant_complexity
end
- def visible?(context)
- return false if feature_flag.present? && !Feature.enabled?(feature_flag)
-
- super
- end
-
private
- attr_reader :feature_flag
-
def field_authorized?(object, ctx)
object = object.node if object.is_a?(GraphQL::Pagination::Connection::Edge)
@@ -123,27 +113,6 @@ module Types
@authorization ||= ::Gitlab::Graphql::Authorize::ObjectAuthorization.new(@authorize)
end
- def feature_documentation_message(key, description)
- message_parts = ["#{description} Available only when feature flag `#{key}` is enabled."]
-
- message_parts << if Feature::Definition.has_definition?(key) && Feature::Definition.default_enabled?(key)
- "This flag is enabled by default."
- else
- "This flag is disabled by default, because the feature is experimental and is subject to change without notice."
- end
-
- message_parts.join(' ')
- end
-
- def check_feature_flag(args)
- ff = args.delete(:_deprecated_feature_flag)
- return args unless ff.present?
-
- args[:description] = feature_documentation_message(ff, args[:description])
-
- args
- end
-
def field_complexity(resolver_class, current)
return current if current.present? && current > 0
diff --git a/app/graphql/types/ci/config_variable_type.rb b/app/graphql/types/ci/config_variable_type.rb
new file mode 100644
index 00000000000..87ae026c2c1
--- /dev/null
+++ b/app/graphql/types/ci/config_variable_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class ConfigVariableType < BaseObject # rubocop:disable Graphql/AuthorizeTypes
+ graphql_name 'CiConfigVariable'
+ description 'CI/CD config variables.'
+
+ field :key, GraphQL::Types::String,
+ null: true,
+ description: 'Name of the variable.'
+
+ field :description, GraphQL::Types::String,
+ null: true,
+ description: 'Description for the CI/CD config variable.'
+
+ field :value, GraphQL::Types::String,
+ null: true,
+ description: 'Value of the variable.'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/group_variable_type.rb b/app/graphql/types/ci/group_variable_type.rb
index 3322f741342..8821fdd8eb4 100644
--- a/app/graphql/types/ci/group_variable_type.rb
+++ b/app/graphql/types/ci/group_variable_type.rb
@@ -10,16 +10,16 @@ module Types
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
- null: true,
- description: 'Scope defining the environments that can use the variable.'
-
- field :protected, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is protected.'
+ null: true,
+ description: 'Scope defining the environments that can use the variable.'
field :masked, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is masked.'
+ null: true,
+ description: 'Indicates whether the variable is masked.'
+
+ field :protected, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates whether the variable is protected.'
end
end
end
diff --git a/app/graphql/types/ci/instance_variable_type.rb b/app/graphql/types/ci/instance_variable_type.rb
index f564a2f59a0..7ffc52deb73 100644
--- a/app/graphql/types/ci/instance_variable_type.rb
+++ b/app/graphql/types/ci/instance_variable_type.rb
@@ -9,21 +9,29 @@ module Types
implements(VariableInterface)
+ field :id, GraphQL::Types::ID,
+ null: false,
+ description: 'ID of the variable.'
+
field :environment_scope, GraphQL::Types::String,
- null: true,
- deprecated: {
- reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
- milestone: '15.3'
- },
- description: 'Scope defining the environments that can use the variable.'
+ null: true,
+ deprecated: {
+ reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
+ milestone: '15.3'
+ },
+ description: 'Scope defining the environments that can use the variable.'
field :protected, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is protected.'
+ null: true,
+ description: 'Indicates whether the variable is protected.'
field :masked, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is masked.'
+ null: true,
+ description: 'Indicates whether the variable is masked.'
+
+ field :raw, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates whether the variable is raw.'
def environment_scope
nil
diff --git a/app/graphql/types/ci/manual_variable_type.rb b/app/graphql/types/ci/manual_variable_type.rb
index d6f59c1d249..ed92a6645b4 100644
--- a/app/graphql/types/ci/manual_variable_type.rb
+++ b/app/graphql/types/ci/manual_variable_type.rb
@@ -10,12 +10,12 @@ module Types
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
- null: true,
- deprecated: {
- reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
- milestone: '15.3'
- },
- description: 'Scope defining the environments that can use the variable.'
+ null: true,
+ deprecated: {
+ reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
+ milestone: '15.3'
+ },
+ description: 'Scope defining the environments that can use the variable.'
def environment_scope
nil
diff --git a/app/graphql/types/ci/project_variable_type.rb b/app/graphql/types/ci/project_variable_type.rb
index 625bb7fd4b1..02e3cdc1a11 100644
--- a/app/graphql/types/ci/project_variable_type.rb
+++ b/app/graphql/types/ci/project_variable_type.rb
@@ -10,16 +10,16 @@ module Types
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
- null: true,
- description: 'Scope defining the environments that can use the variable.'
+ null: true,
+ description: 'Scope defining the environments that can use the variable.'
field :protected, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is protected.'
+ null: true,
+ description: 'Indicates whether the variable is protected.'
field :masked, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is masked.'
+ null: true,
+ description: 'Indicates whether the variable is masked.'
end
end
end
diff --git a/app/graphql/types/ci/variable_interface.rb b/app/graphql/types/ci/variable_interface.rb
index 82c9ba7121c..ec68d3c987c 100644
--- a/app/graphql/types/ci/variable_interface.rb
+++ b/app/graphql/types/ci/variable_interface.rb
@@ -8,24 +8,24 @@ module Types
graphql_name 'CiVariable'
field :id, GraphQL::Types::ID,
- null: false,
- description: 'ID of the variable.'
+ null: false,
+ description: 'ID of the variable.'
field :key, GraphQL::Types::String,
- null: true,
- description: 'Name of the variable.'
+ null: true,
+ description: 'Name of the variable.'
+
+ field :raw, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates whether the variable is raw.'
field :value, GraphQL::Types::String,
- null: true,
- description: 'Value of the variable.'
+ null: true,
+ description: 'Value of the variable.'
field :variable_type, ::Types::Ci::VariableTypeEnum,
- null: true,
- description: 'Type of the variable.'
-
- field :raw, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is raw.'
+ null: true,
+ description: 'Type of the variable.'
end
end
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 2ad4e8848d1..53336a2affa 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -18,6 +18,17 @@ module Types
null: false,
description: 'Path of the CI configuration file.'
+ field :ci_config_variables, [Types::Ci::ConfigVariableType],
+ null: true,
+ calls_gitaly: true,
+ authorize: :create_pipeline,
+ alpha: { milestone: '15.3' },
+ description: 'CI/CD config variable.' do
+ argument :sha, GraphQL::Types::String,
+ required: true,
+ description: 'Sha.'
+ end
+
field :full_path, GraphQL::Types::ID,
null: false,
description: 'Full path of the project.'
@@ -549,6 +560,16 @@ module Types
project.container_repositories.size
end
+ def ci_config_variables(sha)
+ result = ::Ci::ListConfigVariablesService.new(object, context[:current_user]).execute(sha)
+
+ return if result.nil?
+
+ result.map do |var_key, var_config|
+ { key: var_key, **var_config }
+ end
+ end
+
def sast_ci_configuration
return unless Ability.allowed?(current_user, :download_code, object)
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index d9e8873ffc1..1228dfd87d6 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -996,15 +996,11 @@ module Ci
end
def collect_test_reports!(test_reports)
- test_reports.get_suite(test_suite_name).tap do |test_suite|
- each_report(Ci::JobArtifact.file_types_for_report(:test)) do |file_type, blob|
- Gitlab::Ci::Parsers.fabricate!(file_type).parse!(
- blob,
- test_suite,
- job: self
- )
- end
+ each_report(Ci::JobArtifact.file_types_for_report(:test)) do |file_type, blob|
+ Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, test_reports, job: self)
end
+
+ test_reports
end
def collect_accessibility_reports!(accessibility_report)
@@ -1181,6 +1177,14 @@ module Ci
job_artifacts.map(&:file_type)
end
+ def test_suite_name
+ if matrix_build?
+ name
+ else
+ group_name
+ end
+ end
+
protected
def run_status_commit_hooks!
@@ -1191,14 +1195,6 @@ module Ci
private
- def test_suite_name
- if matrix_build?
- name
- else
- group_name
- end
- end
-
def matrix_build?
options.dig(:parallel, :matrix).present?
end
diff --git a/app/services/ci/build_report_result_service.rb b/app/services/ci/build_report_result_service.rb
index f9146b3677a..20a31322919 100644
--- a/app/services/ci/build_report_result_service.rb
+++ b/app/services/ci/build_report_result_service.rb
@@ -22,7 +22,8 @@ module Ci
private
def generate_test_suite_report(build)
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report.get_suite(build.test_suite_name)
end
def tests_params(test_suite)
diff --git a/app/services/ci/test_failure_history_service.rb b/app/services/ci/test_failure_history_service.rb
index 2214a6a2729..5a8072b2a0d 100644
--- a/app/services/ci/test_failure_history_service.rb
+++ b/app/services/ci/test_failure_history_service.rb
@@ -80,8 +80,8 @@ module Ci
end
def generate_test_suite!(build)
- # Returns an instance of Gitlab::Ci::Reports::TestSuite
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report.get_suite(build.test_suite_name)
end
def ci_unit_test_attrs(batch)
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 5a33b2ebc44..68afb17f485 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -10098,6 +10098,18 @@ Represents the total number of issues and their weights for a particular day.
| <a id="ciconfigstagegroups"></a>`groups` | [`CiConfigGroupConnection`](#ciconfiggroupconnection) | Groups of jobs for the stage. (see [Connections](#connections)) |
| <a id="ciconfigstagename"></a>`name` | [`String`](#string) | Name of the stage. |
+### `CiConfigVariable`
+
+CI/CD config variables.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="ciconfigvariabledescription"></a>`description` | [`String`](#string) | Description for the CI/CD config variable. |
+| <a id="ciconfigvariablekey"></a>`key` | [`String`](#string) | Name of the variable. |
+| <a id="ciconfigvariablevalue"></a>`value` | [`String`](#string) | Value of the variable. |
+
### `CiGroup`
#### Fields
@@ -15801,6 +15813,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="projectboardsid"></a>`id` | [`BoardID`](#boardid) | Find a board by its ID. |
+##### `Project.ciConfigVariables`
+
+CI/CD config variable.
+
+WARNING:
+**Introduced** in 15.3.
+This feature is in Alpha. It can be changed or removed at any time.
+
+Returns [`[CiConfigVariable!]`](#ciconfigvariable).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectciconfigvariablessha"></a>`sha` | [`String!`](#string) | Sha. |
+
##### `Project.ciTemplate`
Find a single CI/CD template by name.
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 190e853a376..78ff8fbba36 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -546,12 +546,6 @@ def resolve(id: )
end
```
-### `feature_flag` property (deprecated)
-
-NOTE:
-This property is deprecated and should no longer be used. The property
-has been temporarily renamed to `_deprecated_feature_flag` and support for it will be removed in [#369202](https://gitlab.com/gitlab-org/gitlab/-/issues/369202).
-
## Deprecating schema items
The GitLab GraphQL API is versionless, which means we maintain backwards
diff --git a/lib/gitlab/ci/parsers/test/junit.rb b/lib/gitlab/ci/parsers/test/junit.rb
index 999ffff85d2..d95ecff85cd 100644
--- a/lib/gitlab/ci/parsers/test/junit.rb
+++ b/lib/gitlab/ci/parsers/test/junit.rb
@@ -8,7 +8,9 @@ module Gitlab
JunitParserError = Class.new(Gitlab::Ci::Parsers::ParserError)
ATTACHMENT_TAG_REGEX = /\[\[ATTACHMENT\|(?<path>.+?)\]\]/.freeze
- def parse!(xml_data, test_suite, job:)
+ def parse!(xml_data, test_report, job:)
+ test_suite = test_report.get_suite(job.test_suite_name)
+
root = Hash.from_xml(xml_data)
total_parsed = 0
max_test_cases = job.max_test_cases_per_report
diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml
index 7e81112796f..a58469cea57 100644
--- a/lib/gitlab/import_export/group/import_export.yml
+++ b/lib/gitlab/import_export/group/import_export.yml
@@ -32,14 +32,11 @@ included_attributes:
- :created_at
- :updated_at
- :start_date
- - :last_run_date
- - :duration_in_weeks
- - :iterations_in_advance
- :active
- - :automatic
- :roll_over
- :title
- :description
+ - :sequence
iterations_cadences: *iterations_cadence_definition
iteration: &iteration_definition
- :iid
@@ -69,6 +66,10 @@ excluded_attributes:
- :state_id
iterations_cadence: &iterations_cadence_definition
- :id
+ - :last_run_date
+ - :duration_in_weeks
+ - :iterations_in_advance
+ - :automatic
iterations_cadences: *iterations_cadence_definition
iteration: &iteration_excluded_definition
- :id
diff --git a/lib/gitlab/import_export/group/relation_factory.rb b/lib/gitlab/import_export/group/relation_factory.rb
index 08db9471e27..1b8436c4ed9 100644
--- a/lib/gitlab/import_export/group/relation_factory.rb
+++ b/lib/gitlab/import_export/group/relation_factory.rb
@@ -26,7 +26,10 @@ module Gitlab
private
def setup_models
- setup_note if @relation_name == :notes
+ case @relation_name
+ when :notes then setup_note
+ when :'Iterations::Cadence' then setup_iterations_cadence
+ end
update_group_references
end
@@ -45,6 +48,10 @@ module Gitlab
def use_attributes_permitter?
false
end
+
+ def setup_iterations_cadence
+ @relation_hash['automatic'] = false
+ end
end
end
end
diff --git a/package.json b/package.json
index 710b9595927..1bc6317f00b 100644
--- a/package.json
+++ b/package.json
@@ -168,9 +168,7 @@
"string-hash": "1.1.3",
"style-loader": "^2.0.0",
"swagger-ui-dist": "4.12.0",
- "three": "^0.84.0",
- "three-orbit-controls": "^82.1.0",
- "three-stl-loader": "^1.0.4",
+ "three": "^0.143.0",
"timeago.js": "^4.0.2",
"unified": "^10.1.2",
"unist-util-visit-parents": "^5.1.0",
diff --git a/rubocop/code_reuse_helpers.rb b/rubocop/code_reuse_helpers.rb
index 2769da2389c..87b907f8778 100644
--- a/rubocop/code_reuse_helpers.rb
+++ b/rubocop/code_reuse_helpers.rb
@@ -81,12 +81,6 @@ module RuboCop
in_app_directory?(node, 'graphql')
end
- # Returns true if the given node resides in app/graphql/types,
- # ee/app/graphql/types, or ee/app/graphql/ee/types.
- def in_graphql_types?(node)
- in_graphql_directory?(node, 'types')
- end
-
# Returns true if the given node resides in lib/api or ee/lib/api.
def in_api?(node)
in_lib_directory?(node, 'api')
diff --git a/rubocop/cop/gitlab/mark_used_feature_flags.rb b/rubocop/cop/gitlab/mark_used_feature_flags.rb
index 63bccec31c0..4844dab0a30 100644
--- a/rubocop/cop/gitlab/mark_used_feature_flags.rb
+++ b/rubocop/cop/gitlab/mark_used_feature_flags.rb
@@ -26,9 +26,6 @@ module RuboCop
data_consistency
deduplicate
].freeze
- GRAPHQL_METHODS = %i[
- field
- ].freeze
SELF_METHODS = %i[
push_frontend_feature_flag
push_force_frontend_feature_flag
@@ -36,7 +33,7 @@ module RuboCop
limit_feature_flag_for_override=
].freeze + EXPERIMENT_METHODS + RUGGED_METHODS + WORKER_METHODS
- RESTRICT_ON_SEND = FEATURE_METHODS + EXPERIMENTATION_METHODS + GRAPHQL_METHODS + SELF_METHODS
+ RESTRICT_ON_SEND = FEATURE_METHODS + EXPERIMENTATION_METHODS + SELF_METHODS
USAGE_DATA_COUNTERS_EVENTS_YAML_GLOBS = [
File.expand_path("../../../config/metrics/aggregates/*.yml", __dir__),
@@ -138,15 +135,6 @@ module RuboCop
node.children[3].each_pair.find do |pair|
pair.key.value == :feature_flag
end&.value
- elsif graphql_method?(node)
- return unless node.children.size > 3
-
- opts_index = node.children[3].hash_type? ? 3 : 4
- return unless node.children[opts_index]
-
- node.children[opts_index].each_pair.find do |pair|
- pair.key.value == :_deprecated_feature_flag
- end&.value
else
arg_index = rugged_method?(node) ? 3 : 2
@@ -209,16 +197,12 @@ module RuboCop
WORKER_METHODS.include?(method_name(node))
end
- def graphql_method?(node)
- GRAPHQL_METHODS.include?(method_name(node)) && in_graphql_types?(node)
- end
-
def self_method?(node)
SELF_METHODS.include?(method_name(node)) && class_caller(node).empty?
end
def trackable_flag?(node)
- feature_method?(node) || experimentation_method?(node) || graphql_method?(node) || self_method?(node)
+ feature_method?(node) || experimentation_method?(node) || self_method?(node)
end
# Marking all event's feature flags as used as Gitlab::UsageDataCounters::HLLRedisCounter.track_event{,context}
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
index 049e5bcce92..b1ed61faf66 100644
--- a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
@@ -236,16 +236,17 @@ describe('MR Widget', () => {
data: { vulnerabilities: [{ vuln: 2 }] },
};
+ const fetchExpandedData = jest.fn().mockResolvedValue(mockDataExpanded);
+
createComponent({
propsData: {
isCollapsible: true,
fetchCollapsedData: () => Promise.resolve(mockDataCollapsed),
- fetchExpandedData: () => Promise.resolve(mockDataExpanded),
+ fetchExpandedData,
},
});
findToggleButton().vm.$emit('click');
-
await waitForPromises();
// First fetches the collapsed data
@@ -259,6 +260,62 @@ describe('MR Widget', () => {
collapsed: null,
expanded: mockDataExpanded.data,
});
+
+ // Triggering a click does not call the expanded data again
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+ expect(fetchExpandedData).toHaveBeenCalledTimes(1);
+ });
+
+ it('allows refetching when fetch expanded data returns an error', async () => {
+ const fetchExpandedData = jest.fn().mockRejectedValue({ error: true });
+
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ fetchCollapsedData: () => Promise.resolve([]),
+ fetchExpandedData,
+ },
+ });
+
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+
+ // First fetches the collapsed data
+ expect(wrapper.emitted('input')[0][0]).toEqual({
+ collapsed: undefined,
+ expanded: null,
+ });
+
+ expect(fetchExpandedData).toHaveBeenCalledTimes(1);
+ expect(wrapper.emitted('input')).toHaveLength(1); // Should not an emit an input call because request failed
+
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+ expect(fetchExpandedData).toHaveBeenCalledTimes(2);
+ });
+
+ it('resets the error message when another request is fetched', async () => {
+ const fetchExpandedData = jest.fn().mockRejectedValue({ error: true });
+
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ fetchCollapsedData: () => Promise.resolve([]),
+ fetchExpandedData,
+ },
+ });
+
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(wrapper.findByText('Failed to load').exists()).toBe(true);
+ fetchExpandedData.mockImplementation(() => new Promise(() => {}));
+
+ findToggleButton().vm.$emit('click');
+ await nextTick();
+
+ expect(wrapper.findByText('Failed to load').exists()).toBe(false);
});
});
});
diff --git a/spec/graphql/features/feature_flag_spec.rb b/spec/graphql/features/feature_flag_spec.rb
deleted file mode 100644
index b06718eb16a..00000000000
--- a/spec/graphql/features/feature_flag_spec.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Graphql Field feature flags' do
- include GraphqlHelpers
- include Graphql::ResolverFactories
-
- let_it_be(:user) { create(:user) }
-
- let(:feature_flag) { 'test_feature' }
- let(:test_object) { double(name: 'My name') }
- let(:query_string) { '{ item { name } }' }
- let(:result) { execute_query(query_type)['data'] }
-
- before do
- skip_feature_flags_yaml_validation
- end
-
- subject { result }
-
- describe 'Feature flagged field' do
- let(:type) { type_factory }
-
- let(:query_type) do
- query_factory do |query|
- query.field :item, type, null: true, _deprecated_feature_flag: feature_flag, resolver: new_resolver(test_object)
- end
- end
-
- it 'checks YAML definition for default_enabled' do
- # Exception is indicative of a check for YAML definition
- expect { subject }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{feature_flag}' does not exist/)
- end
-
- context 'skipping YAML check' do
- before do
- skip_default_enabled_yaml_check
- end
-
- it 'returns the value when feature is enabled' do
- expect(subject['item']).to eq('name' => test_object.name)
- end
-
- it 'returns nil when the feature is disabled' do
- stub_feature_flags(feature_flag => false)
-
- expect(subject).to be_nil
- end
- end
- end
-end
diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb
index b85716e4d21..9f8a8717efb 100644
--- a/spec/graphql/types/base_field_spec.rb
+++ b/spec/graphql/types/base_field_spec.rb
@@ -205,39 +205,6 @@ RSpec.describe Types::BaseField do
end
end
end
-
- describe '#visible?' do
- context 'and has a feature_flag' do
- let(:flag) { :test_feature }
- let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String, _deprecated_feature_flag: flag, null: false) }
- let(:context) { {} }
-
- before do
- skip_feature_flags_yaml_validation
- end
-
- it 'checks YAML definition for default_enabled' do
- # Exception is indicative of a check for YAML definition
- expect { field.visible?(context) }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{flag}' does not exist/)
- end
-
- context 'skipping YAML check' do
- before do
- skip_default_enabled_yaml_check
- end
-
- it 'returns false if the feature is not enabled' do
- stub_feature_flags(flag => false)
-
- expect(field.visible?(context)).to eq(false)
- end
-
- it 'returns true if the feature is enabled' do
- expect(field.visible?(context)).to eq(true)
- end
- end
- end
- end
end
describe '#resolve' do
@@ -251,77 +218,11 @@ RSpec.describe Types::BaseField do
end
end
- describe '#description' do
- context 'feature flag given' do
- let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String, _deprecated_feature_flag: flag, null: false, description: 'Test description.') }
- let(:flag) { :test_flag }
-
- it 'prepends the description' do
- expect(field.description).to start_with 'Test description. Available only when feature flag `test_flag` is enabled.'
- end
-
- context 'falsey feature_flag values' do
- using RSpec::Parameterized::TableSyntax
-
- where(:flag, :feature_value, :default_enabled) do
- '' | false | false
- '' | true | false
- nil | false | true
- nil | true | false
- end
-
- with_them do
- it 'returns the correct description' do
- expect(field.description).to eq('Test description.')
- end
- end
- end
-
- context 'with different default_enabled values' do
- using RSpec::Parameterized::TableSyntax
-
- where(:feature_value, :default_enabled, :expected_description) do
- disabled_ff_description = "Test description. Available only when feature flag `test_flag` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice."
- enabled_ff_description = "Test description. Available only when feature flag `test_flag` is enabled. This flag is enabled by default."
-
- false | false | disabled_ff_description
- true | false | disabled_ff_description
- false | true | enabled_ff_description
- true | true | enabled_ff_description
- end
-
- with_them do
- before do
- stub_feature_flags("#{flag}": feature_value)
-
- allow(Feature::Definition).to receive(:has_definition?).with(flag).and_return(true)
- allow(Feature::Definition).to receive(:default_enabled?).and_return(default_enabled)
- end
-
- it 'returns the correct availability in the description' do
- expect(field.description).to eq expected_description
- end
- end
- end
- end
- end
-
include_examples 'Gitlab-style deprecations' do
def subject(args = {})
base_args = { name: 'test', type: GraphQL::Types::String, null: true }
described_class.new(**base_args.merge(args))
end
-
- it 'interacts well with the `_deprecated_feature_flag` property' do
- field = subject(
- deprecated: { milestone: '1.10', reason: 'Deprecation reason' },
- description: 'Field description.',
- _deprecated_feature_flag: 'foo_flag'
- )
-
- expect(field.description).to start_with('Field description. Available only when feature flag `foo_flag` is enabled.')
- expect(field.description).to end_with('Deprecated in 1.10: Deprecation reason.')
- end
end
end
diff --git a/spec/graphql/types/ci/config_variable_type_spec.rb b/spec/graphql/types/ci/config_variable_type_spec.rb
new file mode 100644
index 00000000000..2b0937a7858
--- /dev/null
+++ b/spec/graphql/types/ci/config_variable_type_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CiConfigVariable'] do
+ specify { expect(described_class).to have_graphql_fields(:key, :description, :value).at_least }
+end
diff --git a/spec/graphql/types/ci/instance_variable_type_spec.rb b/spec/graphql/types/ci/instance_variable_type_spec.rb
index cf4aaed31f1..c77a4ac1dc4 100644
--- a/spec/graphql/types/ci/instance_variable_type_spec.rb
+++ b/spec/graphql/types/ci/instance_variable_type_spec.rb
@@ -5,5 +5,5 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiInstanceVariable'] do
specify { expect(described_class.interfaces).to contain_exactly(Types::Ci::VariableInterface) }
- specify { expect(described_class).to have_graphql_fields(:masked, :protected).at_least }
+ specify { expect(described_class).to have_graphql_fields(:environment_scope, :masked, :protected).at_least }
end
diff --git a/spec/graphql/types/ci/manual_variable_type_spec.rb b/spec/graphql/types/ci/manual_variable_type_spec.rb
index 2884c818a52..21d36b7dfc0 100644
--- a/spec/graphql/types/ci/manual_variable_type_spec.rb
+++ b/spec/graphql/types/ci/manual_variable_type_spec.rb
@@ -4,4 +4,6 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiManualVariable'] do
specify { expect(described_class.interfaces).to contain_exactly(Types::Ci::VariableInterface) }
+
+ specify { expect(described_class).to have_graphql_fields(:environment_scope).at_least }
end
diff --git a/spec/graphql/types/ci/variable_interface_spec.rb b/spec/graphql/types/ci/variable_interface_spec.rb
index 8cef0ac2a14..328c5305a44 100644
--- a/spec/graphql/types/ci/variable_interface_spec.rb
+++ b/spec/graphql/types/ci/variable_interface_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiVariable'] do
specify do
expect(described_class).to have_graphql_fields(
- :id, :key, :value, :variable_type, :raw
+ :id, :key, :raw, :value, :variable_type
).at_least
end
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 538ccfb2d01..c340275bd9c 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -37,6 +37,7 @@ RSpec.describe GitlabSchema.types['Project'] do
ci_template timelogs merge_commit_template squash_commit_template work_item_types
recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables
timelog_categories fork_targets
+ ci_config_variables
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
index 82fa11d5f98..821a5057d2e 100644
--- a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
@@ -4,11 +4,13 @@ require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
describe '#parse!' do
- subject { described_class.new.parse!(junit, test_suite, job: job) }
+ subject { described_class.new.parse!(junit, test_report, job: job) }
- let(:test_suite) { Gitlab::Ci::Reports::TestSuite.new('rspec') }
+ let(:job) { double(test_suite_name: 'rspec', max_test_cases_per_report: max_test_cases) }
+
+ let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
+ let(:test_suite) { test_report.get_suite(job.test_suite_name) }
let(:test_cases) { flattened_test_cases(test_suite) }
- let(:job) { double(max_test_cases_per_report: max_test_cases) }
let(:max_test_cases) { 0 }
context 'when data is JUnit style XML' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 960bde3ec93..64cb547a048 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -4366,9 +4366,7 @@ RSpec.describe Ci::Build do
end
describe '#collect_test_reports!' do
- subject { build.collect_test_reports!(test_reports) }
-
- let(:test_reports) { Gitlab::Ci::Reports::TestReport.new }
+ subject(:test_reports) { build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) }
it { expect(test_reports.get_suite(build.name).total_count).to eq(0) }
@@ -4416,56 +4414,6 @@ RSpec.describe Ci::Build do
end
end
end
-
- context 'when build is part of parallel build' do
- let(:build_1) { create(:ci_build, name: 'build 1/2') }
- let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
-
- before do
- build_1.collect_test_reports!(test_report)
- end
-
- it 'uses the group name for test suite name' do
- expect(test_report.test_suites.keys).to contain_exactly('build')
- end
-
- context 'when there are more than one parallel builds' do
- let(:build_2) { create(:ci_build, name: 'build 2/2') }
-
- before do
- build_2.collect_test_reports!(test_report)
- end
-
- it 'merges the test suite from parallel builds' do
- expect(test_report.test_suites.keys).to contain_exactly('build')
- end
- end
- end
-
- context 'when build is part of matrix build' do
- let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
- let(:matrix_build_1) { create(:ci_build, :matrix) }
-
- before do
- matrix_build_1.collect_test_reports!(test_report)
- end
-
- it 'uses the job name for the test suite' do
- expect(test_report.test_suites.keys).to contain_exactly(matrix_build_1.name)
- end
-
- context 'when there are more than one matrix builds' do
- let(:matrix_build_2) { create(:ci_build, :matrix) }
-
- before do
- matrix_build_2.collect_test_reports!(test_report)
- end
-
- it 'keeps separate test suites' do
- expect(test_report.test_suites.keys).to match_array([matrix_build_1.name, matrix_build_2.name])
- end
- end
- end
end
describe '#collect_accessibility_reports!' do
@@ -5662,4 +5610,28 @@ RSpec.describe Ci::Build do
end
end
end
+
+ describe '#test_suite_name' do
+ let(:build) { create(:ci_build, name: 'test') }
+
+ it 'uses the group name for test suite name' do
+ expect(build.test_suite_name).to eq('test')
+ end
+
+ context 'when build is part of parallel build' do
+ let(:build) { create(:ci_build, name: 'build 1/2') }
+
+ it 'uses the group name for test suite name' do
+ expect(build.test_suite_name).to eq('build')
+ end
+ end
+
+ context 'when build is part of matrix build' do
+ let!(:matrix_build) { create(:ci_build, :matrix) }
+
+ it 'uses the job name for the test suite' do
+ expect(matrix_build.test_suite_name).to eq(matrix_build.name)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index e713d0dc068..e4c0e81cc1b 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -4370,6 +4370,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
create(:ci_job_artifact, :junit_with_ant, job: build_java)
end
+ it 'has a test suite for each job' do
+ expect(subject.test_suites.keys).to contain_exactly('rspec', 'java')
+ end
+
it 'returns test reports with collected data' do
expect(subject.total_count).to be(7)
expect(subject.success_count).to be(5)
@@ -4388,6 +4392,34 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ context 'when the pipeline has parallel builds with test reports' do
+ let!(:parallel_build_1) { create(:ci_build, name: 'build 1/2', pipeline: pipeline) }
+ let!(:parallel_build_2) { create(:ci_build, name: 'build 2/2', pipeline: pipeline) }
+
+ before do
+ create(:ci_job_artifact, :junit, job: parallel_build_1)
+ create(:ci_job_artifact, :junit, job: parallel_build_2)
+ end
+
+ it 'merges the test suite from parallel builds' do
+ expect(subject.test_suites.keys).to contain_exactly('build')
+ end
+ end
+
+ context 'the pipeline has matrix builds with test reports' do
+ let!(:matrix_build_1) { create(:ci_build, :matrix, pipeline: pipeline) }
+ let!(:matrix_build_2) { create(:ci_build, :matrix, pipeline: pipeline) }
+
+ before do
+ create(:ci_job_artifact, :junit, job: matrix_build_1)
+ create(:ci_job_artifact, :junit, job: matrix_build_2)
+ end
+
+ it 'keeps separate test suites for each matrix build' do
+ expect(subject.test_suites.keys).to contain_exactly(matrix_build_1.name, matrix_build_2.name)
+ end
+ end
+
context 'when pipeline does not have any builds with test reports' do
it 'returns empty test reports' do
expect(subject.total_count).to be(0)
diff --git a/spec/requests/api/graphql/ci/config_variables_spec.rb b/spec/requests/api/graphql/ci/config_variables_spec.rb
new file mode 100644
index 00000000000..c010ea3b9d4
--- /dev/null
+++ b/spec/requests/api/graphql/ci/config_variables_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)' do
+ include GraphqlHelpers
+ include ReactiveCachingHelpers
+
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ end
+
+ let(:sha) { project.commit.sha }
+
+ let(:service) { Ci::ListConfigVariablesService.new(project, user) }
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ ciConfigVariables(sha: "#{sha}") {
+ key
+ value
+ description
+ }
+ }
+ }
+ )
+ end
+
+ context 'when the user has the correct permissions' do
+ before do
+ project.add_maintainer(user)
+ stub_ci_pipeline_yaml_file(content)
+ allow(Ci::ListConfigVariablesService)
+ .to receive(:new)
+ .and_return(service)
+ end
+
+ context 'when the cache is not empty' do
+ before do
+ synchronous_reactive_cache(service)
+ end
+
+ it 'returns the CI variables for the config' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'ciConfigVariables')).to contain_exactly(
+ {
+ 'key' => 'DB_NAME',
+ 'value' => 'postgres',
+ 'description' => nil
+ },
+ {
+ 'key' => 'ENVIRONMENT_VAR',
+ 'value' => 'env var value',
+ 'description' => 'env var description'
+ }
+ )
+ end
+ end
+
+ context 'when the cache is empty' do
+ let(:sha) { 'main' }
+
+ it 'returns nothing' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'ciConfigVariables')).to be_nil
+ end
+ end
+ end
+
+ context 'when the user is not authorized' do
+ before do
+ project.add_guest(user)
+ stub_ci_pipeline_yaml_file(content)
+ allow(Ci::ListConfigVariablesService)
+ .to receive(:new)
+ .and_return(service)
+ synchronous_reactive_cache(service)
+ end
+
+ it 'returns nothing' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'ciConfigVariables')).to be_nil
+ end
+ end
+end
diff --git a/spec/rubocop/code_reuse_helpers_spec.rb b/spec/rubocop/code_reuse_helpers_spec.rb
index 5cc396a4e3e..a8f935426e6 100644
--- a/spec/rubocop/code_reuse_helpers_spec.rb
+++ b/spec/rubocop/code_reuse_helpers_spec.rb
@@ -172,31 +172,6 @@ RSpec.describe RuboCop::CodeReuseHelpers do
end
end
- describe '#in_graphql_types?' do
- %w[
- app/graphql/types
- ee/app/graphql/ee/types
- ee/app/graphql/types
- ].each do |path|
- it "returns true for a node in #{path}" do
- node = build_and_parse_source('10', rails_root_join(path, 'foo.rb'))
-
- expect(cop.in_graphql_types?(node)).to eq(true)
- end
- end
-
- %w[
- app/graphql/resolvers
- app/foo
- ].each do |path|
- it "returns false for a node in #{path}" do
- node = build_and_parse_source('10', rails_root_join(path, 'foo.rb'))
-
- expect(cop.in_graphql_types?(node)).to eq(false)
- end
- end
- end
-
describe '#in_api?' do
it 'returns true for a node in the API directory' do
node = build_and_parse_source('10', rails_root_join('lib', 'api', 'foo.rb'))
diff --git a/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb b/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb
index 5f72996a06a..c61376d7760 100644
--- a/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb
+++ b/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb
@@ -212,19 +212,6 @@ RSpec.describe RuboCop::Cop::Gitlab::MarkUsedFeatureFlags do
include_examples 'does not set any flags as used', 'deduplicate :delayed'
end
- describe 'GraphQL `field` method' do
- before do
- allow(cop).to receive(:in_graphql_types?).and_return(true)
- end
-
- include_examples 'sets flag as used', 'field :runners, Types::Ci::RunnerType.connection_type, null: true, _deprecated_feature_flag: :foo', 'foo'
- include_examples 'sets flag as used', 'field :runners, null: true, _deprecated_feature_flag: :foo', 'foo'
- include_examples 'does not set any flags as used', 'field :solution'
- include_examples 'does not set any flags as used', 'field :runners, Types::Ci::RunnerType.connection_type'
- include_examples 'does not set any flags as used', 'field :runners, Types::Ci::RunnerType.connection_type, null: true, description: "hello world"'
- include_examples 'does not set any flags as used', 'field :solution, type: GraphQL::Types::String, null: true, description: "URL to the vulnerabilitys details page."'
- end
-
describe "tracking of usage data metrics known events happens at the beginning of inspection" do
let(:usage_data_counters_known_event_feature_flags) { ['an_event_feature_flag'] }
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index b1533879e32..b6a66cfa2c6 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -8,6 +8,9 @@ before_script:
variables:
DB_NAME: postgres
+ ENVIRONMENT_VAR:
+ value: 'env var value'
+ description: 'env var description'
stages:
- test
diff --git a/yarn.lock b/yarn.lock
index c294459f91e..0af3b375914 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11286,20 +11286,10 @@ textextensions@2:
resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286"
integrity sha512-j5EMxnryTvKxwH2Cq+Pb43tsf6sdEgw6Pdwxk83mPaq0ToeFJt6WE4J3s5BqY7vmjlLgkgXvhtXUxo80FyBhCA==
-three-orbit-controls@^82.1.0:
- version "82.1.0"
- resolved "https://registry.yarnpkg.com/three-orbit-controls/-/three-orbit-controls-82.1.0.tgz#11a7f33d0a20ecec98f098b37780f6537374fab4"
- integrity sha1-EafzPQog7OyY8Jizd4D2U3N0+rQ=
-
-three-stl-loader@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/three-stl-loader/-/three-stl-loader-1.0.4.tgz#6b3319a31e3b910aab1883d19b00c81a663c3e03"
- integrity sha1-azMZox47kQqrGIPRmwDIGmY8PgM=
-
-three@^0.84.0:
- version "0.84.0"
- resolved "https://registry.yarnpkg.com/three/-/three-0.84.0.tgz#95be85a55a0fa002aa625ed559130957dcffd918"
- integrity sha1-lb6FpVoPoAKqYl7VWRMJV9z/2Rg=
+three@^0.143.0:
+ version "0.143.0"
+ resolved "https://registry.yarnpkg.com/three/-/three-0.143.0.tgz#1455bca132cc2b20beb7f41d313e10c29e5ed9df"
+ integrity sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==
throat@^6.0.1:
version "6.0.1"