gitlab-qa 14.5.0 → 14.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +4 -2
- data/Gemfile.lock +2 -2
- data/README.md +20 -0
- data/docs/running_against_remote_grid.md +1 -1
- data/docs/what_tests_can_be_run.md +6 -4
- data/lib/gitlab/qa/component/ai_gateway.rb +11 -1
- data/lib/gitlab/qa/component/specs.rb +95 -24
- data/lib/gitlab/qa/runtime/env.rb +19 -4
- data/lib/gitlab/qa/scenario/test/omnibus/update_from_previous.rb +2 -1
- data/lib/gitlab/qa/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aca8f96e30555fcac3d968c580f1393b87d2114a3616e8ab6dbb98f913f852d4
|
4
|
+
data.tar.gz: 566a628d9587a7eeb6e6bcb530bed7170aa689aaadc50cecb28eaba17096e26e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4304f800c1430834f7b335ddae6627e51e6126d883359feefb2863662abe274c8b3de2fadca9328c6f6e9815aeb5df2798a1e839a24cf372ee66cdd5db9bea84
|
7
|
+
data.tar.gz: 1a8325bf53f232d5db6292a63107b15f0acf19a04acb7dc0186474656dbd12744ab429030eb53a4e3f83acc5f9655a7159fc14d89cf81b373650b4045904e95c
|
data/.gitlab-ci.yml
CHANGED
@@ -133,9 +133,11 @@ package-and-test:
|
|
133
133
|
RUN_WITH_BUNDLE: "true"
|
134
134
|
SKIP_OMNIBUS_TRIGGER: "true"
|
135
135
|
SKIP_REPORT_IN_ISSUES: "true"
|
136
|
-
ALLURE_JOB_NAME: gitlab-qa
|
137
136
|
UPDATE_QA_CACHE: $UPDATE_QA_CACHE
|
138
137
|
GITLAB_QA_CACHE_KEY: $GITLAB_QA_CACHE_KEY
|
138
|
+
QA_RETRY_FAILED_SPECS: "true"
|
139
|
+
QA_RUN_TYPE: gitlab-qa
|
140
|
+
QA_EXPORT_TEST_METRICS: "false" # skip metrics export as this pipeline is only used to validate gitlab-qa changes
|
139
141
|
inherit:
|
140
142
|
variables:
|
141
143
|
- RUBY_VERSION
|
@@ -153,4 +155,4 @@ package-and-test:
|
|
153
155
|
- if: '$CI_MERGE_REQUEST_IID'
|
154
156
|
when: manual
|
155
157
|
allow_failure: true
|
156
|
-
- when: always
|
158
|
+
- when: always
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gitlab-qa (14.
|
4
|
+
gitlab-qa (14.7.0)
|
5
5
|
activesupport (>= 6.1, < 7.2)
|
6
6
|
gitlab (~> 4.19)
|
7
7
|
http (~> 5.0)
|
@@ -127,7 +127,7 @@ GEM
|
|
127
127
|
multi_xml (>= 0.5.2)
|
128
128
|
i18n (1.14.1)
|
129
129
|
concurrent-ruby (~> 1.0)
|
130
|
-
jaro_winkler (1.5.
|
130
|
+
jaro_winkler (1.5.6)
|
131
131
|
json (2.6.3)
|
132
132
|
kramdown (2.4.0)
|
133
133
|
rexml
|
data/README.md
CHANGED
@@ -121,6 +121,7 @@ All the scenarios you can run are described in the
|
|
121
121
|
|
122
122
|
Note: The GitLab QA tool requires that [Docker](https://docs.docker.com/install/) is installed.
|
123
123
|
|
124
|
+
|
124
125
|
### Command-line options
|
125
126
|
|
126
127
|
In addition to the [arguments you can use to specify the scenario and
|
@@ -166,6 +167,25 @@ useful if you want to run and debug a specific test, for example:
|
|
166
167
|
$ bundle exec bin/qa Test::Instance::All http://localhost:32768 -- qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
|
167
168
|
```
|
168
169
|
|
170
|
+
### Lefthook
|
171
|
+
|
172
|
+
[Lefthook](https://github.com/evilmartians/lefthook) is a Git hooks manager that allows
|
173
|
+
custom logic to be executed prior to Git committing or pushing. This project comes with
|
174
|
+
Lefthook configuration checked in (`lefthook.yml`), but it must be installed.
|
175
|
+
|
176
|
+
#### Install Lefthook
|
177
|
+
|
178
|
+
```shell
|
179
|
+
# Install the `lefthook` Ruby gem:
|
180
|
+
bundle install
|
181
|
+
# Initialize the lefthook config and adds to .git/hooks dir
|
182
|
+
bundle exec lefthook install
|
183
|
+
# Verify hook execution works as expected
|
184
|
+
bundle exec lefthook run pre-push
|
185
|
+
```
|
186
|
+
|
187
|
+
For a detailed guide on `lefthook` configuration see https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
|
188
|
+
|
169
189
|
### How to add new tests
|
170
190
|
|
171
191
|
Please see the [Beginner's guide to writing end-to-end tests](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/beginners_guide.html).
|
@@ -18,7 +18,7 @@ I.e, if you have a Selenium server set up at http://localhost:4444 or if you hav
|
|
18
18
|
| QA_SAVE_ALL_VIDEOS | Used with Selenoid. Saves video for both passed and failed tests | false | false, true |
|
19
19
|
| QA_SELENOID_BROWSER_IMAGE | Used with Selenoid. Sets the browser image to use for video recording. | "selenoid/chrome" | "selenoid/chrome", "registry.gitlab.com/gitlab-org/gitlab-qa/selenoid-chrome-gitlab" |
|
20
20
|
| QA_SELENOID_BROWSER_VERSION | Used in conjunction with QA_SELENOID_BROWSER_IMAGE. Version of browser to run against. | "111.0" | "latest" "111.0" "mobile-111.0"|
|
21
|
-
| QA_VIDEO_RECORDER_IMAGE | Used with Selenoid. Sets the video recorder image to use for video recording. | "
|
21
|
+
| QA_VIDEO_RECORDER_IMAGE | Used with Selenoid. Sets the video recorder image to use for video recording. | "registry.gitlab.com/gitlab-org/gitlab-qa/selenoid-manual-video-recorder" | "registry.gitlab.com/gitlab-org/gitlab-qa/selenoid-manual-video-recorder", "presidenten/selenoid-manual-video-recorder" |
|
22
22
|
| QA_VIDEO_RECORDER_VERSION | Used with Selenoid. Sets the video recorder image version to use for video recording. | "latest" | "latest" |
|
23
23
|
| QA_REMOTE_GRID_USERNAME | Used with Sauce Labs. Username to specify in the remote grid. "USERNAME@provider:80" | | "gitlab-sl" |
|
24
24
|
| QA_REMOTE_GRID_ACCESS_KEY | Used with Sauce Labs. Key/Token paired with `QA_REMOTE_GRID_USERNAME` | | |
|
@@ -349,11 +349,13 @@ $ gitlab-qa Test::Omnibus::UpdateFromPrevious gitlab-ee:dev-tag 15.6.0-pre major
|
|
349
349
|
|
350
350
|
### `Test::Integration::Geo EE|<full image address>`
|
351
351
|
|
352
|
-
This tests that
|
352
|
+
This tests that Geo UI proxying is working as expected.
|
353
353
|
|
354
|
-
The scenario spins up
|
355
|
-
|
356
|
-
|
354
|
+
The scenario spins up primary and secondary GitLab Geo nodes and
|
355
|
+
can be used to verify that web requests to secondary Geo site return
|
356
|
+
data that is present on the primary.
|
357
|
+
|
358
|
+
See https://docs.gitlab.com/ee/administration/geo/secondary_proxy
|
357
359
|
|
358
360
|
To run tests against the GitLab containers, a GitLab QA (`gitlab/gitlab-qa`)
|
359
361
|
container is spun up and tests are run from it by running the
|
@@ -6,17 +6,27 @@ module Gitlab
|
|
6
6
|
class AiGateway < Base
|
7
7
|
DOCKER_IMAGE = 'registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway'
|
8
8
|
DOCKER_IMAGE_TAG = 'latest'
|
9
|
+
LOG_DIR = '/var/log'
|
9
10
|
|
10
11
|
def name
|
11
12
|
@name ||= 'ai-gateway'
|
12
13
|
end
|
13
14
|
|
15
|
+
def log_volume
|
16
|
+
@log_volume ||= {
|
17
|
+
src: File.join(Runtime::Env.host_artifacts_dir, @name, 'logs'),
|
18
|
+
dest: LOG_DIR
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
14
22
|
def configure_environment(gitlab_hostname:)
|
15
23
|
@environment = {
|
16
24
|
'AIGW_GITLAB_URL' => "http://#{gitlab_hostname}",
|
17
25
|
'AIGW_GITLAB_API_URL' => "http://#{gitlab_hostname}/api/v4",
|
18
26
|
'AIGW_CUSTOMER_PORTAL_URL' => Runtime::Env.customer_portal_url,
|
19
|
-
'
|
27
|
+
'AIGW_MOCK_MODEL_RESPONSES' => true,
|
28
|
+
'AIGW_LOGGING__LEVEL' => 'debug',
|
29
|
+
'AIGW_LOGGING__TO_FILE' => "..#{LOG_DIR}/modelgateway_debug.log"
|
20
30
|
}
|
21
31
|
end
|
22
32
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
4
3
|
# rubocop:disable Metrics/AbcSize
|
5
4
|
|
6
5
|
require 'securerandom'
|
@@ -13,7 +12,18 @@ module Gitlab
|
|
13
12
|
# the `qa/` directory located in GitLab CE / EE repositories.
|
14
13
|
#
|
15
14
|
class Specs < Scenario::Template
|
16
|
-
|
15
|
+
LAST_RUN_FILE = "examples.txt"
|
16
|
+
|
17
|
+
attr_accessor :suite,
|
18
|
+
:release,
|
19
|
+
:network,
|
20
|
+
:args,
|
21
|
+
:volumes,
|
22
|
+
:env,
|
23
|
+
:runner_network,
|
24
|
+
:hostname,
|
25
|
+
:additional_hosts,
|
26
|
+
:retry_failed_specs
|
17
27
|
|
18
28
|
def initialize
|
19
29
|
@docker = Docker::Engine.new(stream_output: true) # stream test output directly instead of through logger
|
@@ -21,6 +31,7 @@ module Gitlab
|
|
21
31
|
@volumes = {}
|
22
32
|
@additional_hosts = []
|
23
33
|
@volumes = { '/var/run/docker.sock' => '/var/run/docker.sock' }
|
34
|
+
@retry_failed_specs = Runtime::Env.retry_failed_specs?
|
24
35
|
|
25
36
|
include_optional_volumes(
|
26
37
|
Runtime::Env.qa_rspec_report_path => 'rspec',
|
@@ -51,9 +62,30 @@ module Gitlab
|
|
51
62
|
Runtime::Logger.info("Running test suite `#{suite}` for #{release.project_name}")
|
52
63
|
|
53
64
|
name = "#{release.project_name}-qa-#{SecureRandom.hex(4)}"
|
65
|
+
run_specs(name)
|
66
|
+
rescue Support::ShellCommand::StatusError => e
|
67
|
+
raise e unless retry_failed_specs
|
54
68
|
|
55
|
-
|
69
|
+
results_file = File.join(host_artifacts_dir(name), LAST_RUN_FILE)
|
70
|
+
raise "Failed to find initial run results file #{results_file}" unless File.exist?(results_file)
|
71
|
+
|
72
|
+
Runtime::Logger.warn("Initial test run failed, retrying failed specs in new process!")
|
73
|
+
run_specs(name, retry_process: true, initial_run_results_host_path: results_file)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Ful path to tmp dir inside container
|
79
|
+
#
|
80
|
+
# @return [String]
|
81
|
+
def tmp_dir
|
82
|
+
@tmp_dir ||= File.join(Docker::Volumes::QA_CONTAINER_WORKDIR, 'tmp')
|
83
|
+
end
|
84
|
+
|
85
|
+
def feature_flag_sets
|
86
|
+
return @feature_flag_sets if defined?(@feature_flag_sets)
|
56
87
|
|
88
|
+
feature_flag_sets = []
|
57
89
|
# When `args` includes:
|
58
90
|
# `[..., "--disable-feature", "a", "--enable-feature", "b", "--set-feature-flags", "c=enable", ...]`
|
59
91
|
# `feature_flag_sets` will be set to:
|
@@ -62,14 +94,36 @@ module Gitlab
|
|
62
94
|
while (index = args&.index { |x| x =~ /--.*-feature/ })
|
63
95
|
feature_flag_sets << args.slice!(index, 2)
|
64
96
|
end
|
65
|
-
|
66
97
|
# When `args` do not have any feature flag options, we add [] so that test is run exactly once.
|
67
98
|
feature_flag_sets << [] unless feature_flag_sets.any?
|
68
99
|
|
100
|
+
@feature_flag_sets = feature_flag_sets
|
101
|
+
end
|
102
|
+
|
103
|
+
def run_specs(name, retry_process: false, initial_run_results_host_path: nil)
|
104
|
+
container_name = retry_process ? "#{name}-retry" : name
|
105
|
+
|
106
|
+
env_vars = if retry_process
|
107
|
+
Runtime::Env.variables.merge({
|
108
|
+
**env,
|
109
|
+
"QA_RSPEC_RETRIED" => "true",
|
110
|
+
"NO_KNAPSACK" => "true"
|
111
|
+
})
|
112
|
+
else
|
113
|
+
Runtime::Env.variables.merge(env)
|
114
|
+
end
|
115
|
+
|
116
|
+
env_vars["RSPEC_LAST_RUN_RESULTS_FILE"] = last_run_results_file
|
117
|
+
# TODO: remove once rspec-retry gem is removed
|
118
|
+
env_vars["QA_DISABLE_RSPEC_RETRY"] = "true" if retry_failed_specs
|
119
|
+
|
120
|
+
run_volumes = volumes.to_h.merge({ host_artifacts_dir(container_name) => tmp_dir })
|
121
|
+
run_volumes[initial_run_results_host_path] = last_run_results_file if retry_process
|
122
|
+
|
69
123
|
feature_flag_sets.each do |feature_flag_set|
|
70
124
|
@docker.run(
|
71
125
|
image: qa_image,
|
72
|
-
args: [suite, *args_with_flags(
|
126
|
+
args: [suite, *args_with_flags(feature_flag_set, retry_process: retry_process)],
|
73
127
|
mask_secrets: Runtime::Env.variables_to_mask
|
74
128
|
) do |command|
|
75
129
|
command << "-t --rm --net=#{network || 'bridge'}"
|
@@ -84,37 +138,40 @@ module Gitlab
|
|
84
138
|
command << hosts # override /etc/hosts in docker container when test runs
|
85
139
|
end
|
86
140
|
|
87
|
-
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
command.volume(
|
92
|
-
File.join(Runtime::Env.host_artifacts_dir, name),
|
93
|
-
File.join(Docker::Volumes::QA_CONTAINER_WORKDIR, 'tmp')
|
94
|
-
)
|
95
|
-
|
96
|
-
volumes.to_h.each { |to, from| command.volume(to, from) }
|
141
|
+
env_vars.each { |key, value| command.env(key, value) }
|
142
|
+
run_volumes.each { |to, from| command.volume(to, from) }
|
97
143
|
|
98
|
-
command.name(
|
144
|
+
command.name(container_name)
|
99
145
|
end
|
100
146
|
end
|
101
147
|
end
|
102
148
|
|
103
|
-
private
|
104
|
-
|
105
149
|
def docker_pull_qa_image_if_needed
|
106
150
|
@docker.login(**release.login_params) if release.login_params
|
107
151
|
|
108
152
|
@docker.pull(image: qa_image) unless Runtime::Env.skip_pull?
|
109
153
|
end
|
110
154
|
|
111
|
-
def args_with_flags(
|
112
|
-
return args if feature_flag_set.empty?
|
155
|
+
def args_with_flags(feature_flag_set, retry_process: false)
|
156
|
+
return args if feature_flag_set.empty? && !retry_process
|
113
157
|
|
114
|
-
|
158
|
+
run_args = args.dup
|
159
|
+
|
160
|
+
if retry_process
|
161
|
+
if run_args.include?("--")
|
162
|
+
run_args.push("--only-failures")
|
163
|
+
first_rspec_arg_index = run_args.index("--") + 1
|
164
|
+
# if first arg after `--` is a not a flag, it's a specific spec/folder which needs to be removed
|
165
|
+
run_args.delete_at(first_rspec_arg_index) unless run_args[first_rspec_arg_index].start_with?("--")
|
166
|
+
else
|
167
|
+
run_args.push("--", "--only-failures")
|
168
|
+
end
|
169
|
+
end
|
115
170
|
|
116
|
-
|
117
|
-
|
171
|
+
return run_args if feature_flag_set.empty?
|
172
|
+
|
173
|
+
Runtime::Logger.info("Running with feature flag: #{feature_flag_set.join(' ')}")
|
174
|
+
run_args.insert(1, *feature_flag_set)
|
118
175
|
end
|
119
176
|
|
120
177
|
def skip_tests?
|
@@ -138,9 +195,23 @@ module Gitlab
|
|
138
195
|
host_path.present? && volumes[host_path] = File.join(Docker::Volumes::QA_CONTAINER_WORKDIR, container_path)
|
139
196
|
end
|
140
197
|
end
|
198
|
+
|
199
|
+
# Full path to host artifacts dir
|
200
|
+
#
|
201
|
+
# @param [String] name
|
202
|
+
# @return [String]
|
203
|
+
def host_artifacts_dir(name)
|
204
|
+
File.join(Runtime::Env.host_artifacts_dir, name)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Path to save or read run results file within container
|
208
|
+
#
|
209
|
+
# @return [String]
|
210
|
+
def last_run_results_file
|
211
|
+
File.join(tmp_dir, LAST_RUN_FILE)
|
212
|
+
end
|
141
213
|
end
|
142
214
|
end
|
143
215
|
end
|
144
216
|
end
|
145
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
146
217
|
# rubocop:enable Metrics/AbcSize
|
@@ -96,6 +96,7 @@ module Gitlab
|
|
96
96
|
'CI' => :ci,
|
97
97
|
'CI_JOB_NAME' => :ci_job_name,
|
98
98
|
'CI_JOB_NAME_SLUG' => :ci_job_name_slug,
|
99
|
+
'CI_JOB_ID' => :ci_job_id,
|
99
100
|
'CI_JOB_URL' => :ci_job_url,
|
100
101
|
'CI_JOB_TOKEN' => :ci_job_token,
|
101
102
|
'CI_RUNNER_ID' => :ci_runner_id,
|
@@ -106,6 +107,7 @@ module Gitlab
|
|
106
107
|
'CI_PROJECT_NAME' => :ci_project_name,
|
107
108
|
'CI_PROJECT_PATH' => :ci_project_path,
|
108
109
|
'CI_PIPELINE_SOURCE' => :ci_pipeline_source,
|
110
|
+
'CI_PIPELINE_ID' => :ci_pipeline_id,
|
109
111
|
'CI_PIPELINE_URL' => :ci_pipeline_url,
|
110
112
|
'CI_PIPELINE_CREATED_AT' => :ci_pipeline_created_at,
|
111
113
|
'CI_MERGE_REQUEST_IID' => :ci_merge_request_iid,
|
@@ -277,20 +279,29 @@ module Gitlab
|
|
277
279
|
|
278
280
|
def require_aws_s3_environment!
|
279
281
|
%w[AWS_S3_REGION AWS_S3_KEY_ID AWS_S3_ACCESS_KEY AWS_S3_BUCKET_NAME].each do |env_key|
|
280
|
-
|
282
|
+
unless ENV.key?(env_key)
|
283
|
+
raise ArgumentError,
|
284
|
+
"Environment variable #{env_key} must be set to run AWS S3 object storage specs"
|
285
|
+
end
|
281
286
|
end
|
282
287
|
end
|
283
288
|
|
284
289
|
def require_gcs_environment!
|
285
290
|
%w[GOOGLE_PROJECT GOOGLE_CLIENT_EMAIL GOOGLE_JSON_KEY GCS_BUCKET_NAME].each do |env_key|
|
286
|
-
|
291
|
+
unless ENV.key?(env_key)
|
292
|
+
raise ArgumentError,
|
293
|
+
"Environment variable #{env_key} must be set to run GCS object storage specs"
|
294
|
+
end
|
287
295
|
end
|
288
296
|
end
|
289
297
|
|
290
298
|
def require_gcs_with_cdn_environment!
|
291
299
|
%w[GOOGLE_CDN_JSON_KEY GCS_CDN_BUCKET_NAME GOOGLE_CDN_LB GOOGLE_CDN_SIGNURL_KEY
|
292
300
|
GOOGLE_CDN_SIGNURL_KEY_NAME].each do |env_key|
|
293
|
-
|
301
|
+
unless ENV.key?(env_key)
|
302
|
+
raise ArgumentError,
|
303
|
+
"Environment variable #{env_key} must be set to run GCS with CDN enabled scenario"
|
304
|
+
end
|
294
305
|
end
|
295
306
|
end
|
296
307
|
|
@@ -359,7 +370,7 @@ module Gitlab
|
|
359
370
|
end
|
360
371
|
|
361
372
|
def video_recorder_image
|
362
|
-
env_var_value_if_defined('QA_VIDEO_RECORDER_IMAGE') || '
|
373
|
+
env_var_value_if_defined('QA_VIDEO_RECORDER_IMAGE') || 'registry.gitlab.com/gitlab-org/gitlab-qa/selenoid-manual-video-recorder'
|
363
374
|
end
|
364
375
|
|
365
376
|
def video_recorder_version
|
@@ -455,6 +466,10 @@ module Gitlab
|
|
455
466
|
enabled?(env_var_value_if_defined('QA_MOCK_GITHUB'), default: true)
|
456
467
|
end
|
457
468
|
|
469
|
+
def retry_failed_specs?
|
470
|
+
enabled?(env_var_value_if_defined('QA_RETRY_FAILED_SPECS'), default: false)
|
471
|
+
end
|
472
|
+
|
458
473
|
private
|
459
474
|
|
460
475
|
def enabled?(value, default: true)
|
@@ -111,8 +111,9 @@ module Gitlab
|
|
111
111
|
specs.args = [gitlab.address, *rspec_args]
|
112
112
|
next if release == current_release
|
113
113
|
|
114
|
-
# do not generate reports and metrics artifacts for non release runs
|
114
|
+
# do not generate reports and metrics artifacts for non release runs or retry failures
|
115
115
|
specs.env = { 'QA_GENERATE_ALLURE_REPORT' => false, 'QA_SAVE_TEST_METRICS' => false }
|
116
|
+
specs.retry_failed_specs = false
|
116
117
|
end
|
117
118
|
rescue Support::ShellCommand::StatusError => e
|
118
119
|
if release == current_release # only fail on current release
|
data/lib/gitlab/qa/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-qa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 14.
|
4
|
+
version: 14.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab Quality
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|