gitlab-qa 4.20.0 → 5.0.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 +85 -97
- data/docs/what_tests_can_be_run.md +18 -0
- data/exe/gitlab-qa-report +8 -0
- data/gitlab-qa.gemspec +2 -0
- data/lib/gitlab/qa.rb +3 -0
- data/lib/gitlab/qa/report/results_in_issues.rb +191 -0
- data/lib/gitlab/qa/reporter.rb +68 -0
- data/lib/gitlab/qa/runner.rb +1 -15
- data/lib/gitlab/qa/runtime/env.rb +17 -1
- data/lib/gitlab/qa/runtime/token_finder.rb +44 -0
- data/lib/gitlab/qa/version.rb +1 -1
- metadata +35 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbeeb65a1b5a859f9908822611d61985da8ba9dd3118a56fe9e52ce0e645e09b
|
|
4
|
+
data.tar.gz: 78f96fa6fe4c8d7e4139e689ff08fd8c71df18d3d111917c9c672b36b6376936
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b43e0971ca42b9a1fe77475ee94c92c164e3f0a3d8f6a45129f899f67a4708d469b36509df84614d33763d6f469d9702d86ab498aa1151fadfc4297c0b96b5f
|
|
7
|
+
data.tar.gz: cbbd9b29c55a243614d127b9e6840f6b8a1241e8984d87b05c0c728ac445f0c6e4b8d3cdb85f0a8dc45e1cb6dd82350eb52237354792f292a63a3e6cbd3806ff
|
data/.gitlab-ci.yml
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
.default-rules:
|
|
2
|
+
rules:
|
|
3
|
+
- if: '$CI_COMMIT_TAG || $RELEASE'
|
|
4
|
+
when: never
|
|
5
|
+
- if: '$RELEASE == null && $CI_JOB_NAME =~ /staging/'
|
|
6
|
+
when: manual
|
|
7
|
+
- if: '$CI_MERGE_REQUEST_ID || $CI_COMMIT_REF_NAME == "master"'
|
|
2
8
|
|
|
3
|
-
|
|
4
|
-
- docker:19.03.0-dind
|
|
9
|
+
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-ruby-2.6
|
|
5
10
|
|
|
6
11
|
stages:
|
|
7
12
|
- check
|
|
@@ -10,11 +15,10 @@ stages:
|
|
|
10
15
|
- notify
|
|
11
16
|
|
|
12
17
|
variables:
|
|
13
|
-
|
|
14
|
-
DOCKER_DRIVER: overlay
|
|
18
|
+
DOCKER_DRIVER: overlay2
|
|
15
19
|
DOCKER_HOST: tcp://docker:2375
|
|
16
20
|
QA_ARTIFACTS_DIR: $CI_PROJECT_DIR
|
|
17
|
-
QA_CAN_TEST_GIT_PROTOCOL_V2: '
|
|
21
|
+
QA_CAN_TEST_GIT_PROTOCOL_V2: 'true'
|
|
18
22
|
|
|
19
23
|
cache:
|
|
20
24
|
key: "ruby:2.6"
|
|
@@ -29,27 +33,37 @@ before_script:
|
|
|
29
33
|
fi
|
|
30
34
|
- export LANG=C.UTF-8
|
|
31
35
|
|
|
32
|
-
check:
|
|
36
|
+
.check-base:
|
|
37
|
+
extends: .default-rules
|
|
33
38
|
stage: check
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
except:
|
|
37
|
-
- triggers
|
|
38
|
-
tags:
|
|
39
|
-
- docker
|
|
39
|
+
script:
|
|
40
|
+
- bundle exec $CI_JOB_NAME
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
rubocop:
|
|
43
|
+
extends: .check-base
|
|
44
|
+
|
|
45
|
+
rspec:
|
|
46
|
+
extends: .check-base
|
|
47
|
+
|
|
48
|
+
release:
|
|
49
|
+
stage: release
|
|
50
|
+
rules:
|
|
51
|
+
- if: '$CI_COMMIT_TAG'
|
|
52
|
+
script:
|
|
53
|
+
- gem update --system
|
|
54
|
+
- ruby --version
|
|
55
|
+
- gem env version
|
|
56
|
+
- gem build gitlab-qa.gemspec
|
|
57
|
+
- gem push gitlab-qa*.gem
|
|
58
|
+
artifacts:
|
|
59
|
+
paths:
|
|
60
|
+
- gitlab-qa*.gem
|
|
61
|
+
expire_in: 30 days
|
|
49
62
|
|
|
50
63
|
.test:
|
|
51
64
|
stage: test
|
|
52
|
-
|
|
65
|
+
services:
|
|
66
|
+
- docker:19.03.0-dind
|
|
53
67
|
tags:
|
|
54
68
|
- docker
|
|
55
69
|
artifacts:
|
|
@@ -63,23 +77,33 @@ check:rspec:
|
|
|
63
77
|
.ce-qa:
|
|
64
78
|
variables:
|
|
65
79
|
DEFAULT_RELEASE: "CE"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
rules:
|
|
81
|
+
- if: '$CI_COMMIT_TAG || $RELEASE =~ /gitlab-ee/'
|
|
82
|
+
when: never
|
|
83
|
+
- if: '$RELEASE == null && $CI_JOB_NAME =~ /quarantine|praefect|custom/'
|
|
84
|
+
when: manual
|
|
85
|
+
- if: '$RELEASE =~ /gitlab-ce/ && $CI_JOB_NAME =~ /quarantine|praefect|custom/'
|
|
86
|
+
when: manual
|
|
87
|
+
- if: '$CI_MERGE_REQUEST_ID && $CI_JOB_NAME =~ /quarantine|praefect|custom/'
|
|
88
|
+
when: manual
|
|
89
|
+
- if: '$RELEASE == null || $RELEASE =~ /gitlab-ce/ || $CI_MERGE_REQUEST_ID || $CI_COMMIT_REF_NAME == "master"'
|
|
70
90
|
|
|
71
91
|
.ee-qa:
|
|
72
92
|
variables:
|
|
73
93
|
DEFAULT_RELEASE: "EE"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
rules:
|
|
95
|
+
- if: '$CI_COMMIT_TAG || $RELEASE =~ /gitlab-ce/'
|
|
96
|
+
when: never
|
|
97
|
+
- if: '$RELEASE == null && $CI_JOB_NAME =~ /quarantine|praefect|custom/'
|
|
98
|
+
when: manual
|
|
99
|
+
- if: '$RELEASE =~ /gitlab-ee/ && $CI_JOB_NAME =~ /quarantine|praefect|custom/'
|
|
100
|
+
when: manual
|
|
101
|
+
- if: '$CI_MERGE_REQUEST_ID && $CI_JOB_NAME =~ /quarantine|praefect|custom/'
|
|
102
|
+
when: manual
|
|
103
|
+
- if: '$RELEASE == null || $RELEASE =~ /gitlab-ee/ || $CI_MERGE_REQUEST_ID || $CI_COMMIT_REF_NAME == "master"'
|
|
78
104
|
|
|
79
105
|
.only-qa:
|
|
80
|
-
|
|
81
|
-
variables:
|
|
82
|
-
- $RELEASE
|
|
106
|
+
extends: .default-rules
|
|
83
107
|
|
|
84
108
|
.high-capacity:
|
|
85
109
|
tags:
|
|
@@ -100,7 +124,6 @@ check:rspec:
|
|
|
100
124
|
|
|
101
125
|
.quarantine:
|
|
102
126
|
allow_failure: true
|
|
103
|
-
when: manual
|
|
104
127
|
|
|
105
128
|
.echo-custom-variables-before-calling-gitlab-qa:
|
|
106
129
|
script:
|
|
@@ -133,7 +156,7 @@ ce:custom-parallel:
|
|
|
133
156
|
- .ce-qa
|
|
134
157
|
- .rspec-report-opts
|
|
135
158
|
- .echo-custom-variables-before-calling-gitlab-qa
|
|
136
|
-
|
|
159
|
+
allow_failure: true
|
|
137
160
|
parallel: 10
|
|
138
161
|
|
|
139
162
|
ee:custom-parallel:
|
|
@@ -143,7 +166,7 @@ ee:custom-parallel:
|
|
|
143
166
|
- .ee-qa
|
|
144
167
|
- .rspec-report-opts
|
|
145
168
|
- .echo-custom-variables-before-calling-gitlab-qa
|
|
146
|
-
|
|
169
|
+
allow_failure: true
|
|
147
170
|
parallel: 10
|
|
148
171
|
|
|
149
172
|
ce:instance:
|
|
@@ -188,24 +211,6 @@ ee:instance-quarantine:
|
|
|
188
211
|
- .quarantine
|
|
189
212
|
- .rspec-report-opts
|
|
190
213
|
|
|
191
|
-
ce:docker:
|
|
192
|
-
script:
|
|
193
|
-
- exe/gitlab-qa Test::Instance::Image ${RELEASE:=CE} -- --tag docker $RSPEC_REPORT_OPTS
|
|
194
|
-
extends:
|
|
195
|
-
- .test
|
|
196
|
-
- .high-capacity
|
|
197
|
-
- .ce-qa
|
|
198
|
-
- .rspec-report-opts
|
|
199
|
-
|
|
200
|
-
ee:docker:
|
|
201
|
-
script:
|
|
202
|
-
- exe/gitlab-qa Test::Instance::Image ${RELEASE:=EE} -- --tag docker $RSPEC_REPORT_OPTS
|
|
203
|
-
extends:
|
|
204
|
-
- .test
|
|
205
|
-
- .high-capacity
|
|
206
|
-
- .ee-qa
|
|
207
|
-
- .rspec-report-opts
|
|
208
|
-
|
|
209
214
|
ce:relative_url:
|
|
210
215
|
script:
|
|
211
216
|
- exe/gitlab-qa Test::Instance::RelativeUrl ${RELEASE:=CE} -- $RSPEC_REPORT_OPTS
|
|
@@ -753,7 +758,7 @@ ce:praefect:
|
|
|
753
758
|
- .knapsack-variables
|
|
754
759
|
- .rspec-report-opts
|
|
755
760
|
parallel: 5
|
|
756
|
-
|
|
761
|
+
allow_failure: true
|
|
757
762
|
|
|
758
763
|
ee:praefect:
|
|
759
764
|
script:
|
|
@@ -765,7 +770,7 @@ ee:praefect:
|
|
|
765
770
|
- .knapsack-variables
|
|
766
771
|
- .rspec-report-opts
|
|
767
772
|
parallel: 5
|
|
768
|
-
|
|
773
|
+
allow_failure: true
|
|
769
774
|
|
|
770
775
|
ce:smtp:
|
|
771
776
|
script:
|
|
@@ -785,29 +790,6 @@ ee:smtp:
|
|
|
785
790
|
- .ee-qa
|
|
786
791
|
- .rspec-report-opts
|
|
787
792
|
|
|
788
|
-
.notify_upstream_commit:
|
|
789
|
-
stage: notify
|
|
790
|
-
except:
|
|
791
|
-
variables:
|
|
792
|
-
- $TOP_UPSTREAM_SOURCE_PROJECT == null
|
|
793
|
-
- $TOP_UPSTREAM_SOURCE_SHA == null
|
|
794
|
-
before_script:
|
|
795
|
-
- gem install gitlab --no-document
|
|
796
|
-
|
|
797
|
-
notify_upstream_commit:success:
|
|
798
|
-
extends:
|
|
799
|
-
- .notify_upstream_commit
|
|
800
|
-
script:
|
|
801
|
-
- bin/notify_upstream_commit success
|
|
802
|
-
when: on_success
|
|
803
|
-
|
|
804
|
-
notify_upstream_commit:failure:
|
|
805
|
-
extends:
|
|
806
|
-
- .notify_upstream_commit
|
|
807
|
-
script:
|
|
808
|
-
- bin/notify_upstream_commit failure
|
|
809
|
-
when: on_failure
|
|
810
|
-
|
|
811
793
|
# This job requires the `GITLAB_QA_ACCESS_TOKEN` and `GITLAB_QA_DEV_ACCESS_TOKEN`
|
|
812
794
|
# variable to be passed when triggered.
|
|
813
795
|
staging:
|
|
@@ -818,23 +800,29 @@ staging:
|
|
|
818
800
|
- .test
|
|
819
801
|
- .high-capacity
|
|
820
802
|
- .only-qa
|
|
821
|
-
|
|
803
|
+
allow_failure: true
|
|
822
804
|
|
|
823
|
-
|
|
824
|
-
stage:
|
|
805
|
+
.notify_upstream_commit:
|
|
806
|
+
stage: notify
|
|
807
|
+
image: ruby:2.6
|
|
808
|
+
before_script:
|
|
809
|
+
- gem install gitlab --no-document
|
|
810
|
+
|
|
811
|
+
notify_upstream_commit:success:
|
|
812
|
+
extends: .notify_upstream_commit
|
|
813
|
+
script:
|
|
814
|
+
- bin/notify_upstream_commit success
|
|
825
815
|
rules:
|
|
826
|
-
- if: '$
|
|
816
|
+
- if: '$TOP_UPSTREAM_SOURCE_PROJECT && $TOP_UPSTREAM_SOURCE_SHA'
|
|
827
817
|
when: on_success
|
|
818
|
+
|
|
819
|
+
notify_upstream_commit:failure:
|
|
820
|
+
extends: .notify_upstream_commit
|
|
828
821
|
script:
|
|
829
|
-
-
|
|
830
|
-
|
|
831
|
-
-
|
|
832
|
-
|
|
833
|
-
- gem push gitlab-qa*.gem
|
|
834
|
-
artifacts:
|
|
835
|
-
paths:
|
|
836
|
-
- gitlab-qa*.gem
|
|
837
|
-
expire_in: 30 days
|
|
822
|
+
- bin/notify_upstream_commit failure
|
|
823
|
+
rules:
|
|
824
|
+
- if: '$TOP_UPSTREAM_SOURCE_PROJECT && $TOP_UPSTREAM_SOURCE_SHA'
|
|
825
|
+
when: on_failure
|
|
838
826
|
|
|
839
827
|
.notify_slack:
|
|
840
828
|
image: alpine
|
|
@@ -847,10 +835,11 @@ release:
|
|
|
847
835
|
notify_slack:
|
|
848
836
|
extends:
|
|
849
837
|
- .notify_slack
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
838
|
+
rules:
|
|
839
|
+
- if: '$TOP_UPSTREAM_SOURCE_JOB && $NOTIFY_CHANNEL'
|
|
840
|
+
when: on_failure
|
|
841
|
+
- if: '$TOP_UPSTREAM_SOURCE_JOB && $TOP_UPSTREAM_SOURCE_REF == "master"'
|
|
842
|
+
when: on_failure
|
|
854
843
|
script:
|
|
855
844
|
- export RELEASE=${TOP_UPSTREAM_SOURCE_REF:-$RELEASE}
|
|
856
845
|
- echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL:=qa-$TOP_UPSTREAM_SOURCE_REF}"
|
|
@@ -858,4 +847,3 @@ notify_slack:
|
|
|
858
847
|
- echo "CI_PIPELINE_URL is $CI_PIPELINE_URL"
|
|
859
848
|
- echo "TOP_UPSTREAM_SOURCE_JOB is $TOP_UPSTREAM_SOURCE_JOB"
|
|
860
849
|
- bin/slack $NOTIFY_CHANNEL "☠️ QA against $RELEASE failed! ☠️ See $CI_PIPELINE_URL (triggered from $TOP_UPSTREAM_SOURCE_JOB)" ci_failing
|
|
861
|
-
when: on_failure
|
|
@@ -613,6 +613,24 @@ Example:
|
|
|
613
613
|
$ gitlab-qa Test::Instance::Smoke ee:<tag> https://staging.gitlab.com
|
|
614
614
|
```
|
|
615
615
|
|
|
616
|
+
### `Test::Instance::RepositoryStorage`
|
|
617
|
+
|
|
618
|
+
This scenario will run a limited number of tests that are tagged with `:repository_storage`.
|
|
619
|
+
|
|
620
|
+
These tests verify features related to multiple repository storages.
|
|
621
|
+
|
|
622
|
+
**Required environment variables:**
|
|
623
|
+
|
|
624
|
+
- `QA_ADDITIONAL_REPOSITORY_STORAGE`: The name of the non-default repository storage.
|
|
625
|
+
|
|
626
|
+
Example:
|
|
627
|
+
|
|
628
|
+
```
|
|
629
|
+
$ export QA_ADDITIONAL_REPOSITORY_STORAGE=secondary
|
|
630
|
+
|
|
631
|
+
$ gitlab-qa Test::Instance::RepositoryStorage
|
|
632
|
+
```
|
|
633
|
+
|
|
616
634
|
----
|
|
617
635
|
|
|
618
636
|
[Back to README.md](../README.md)
|
data/gitlab-qa.gemspec
CHANGED
|
@@ -27,5 +27,7 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_development_dependency 'rubocop', '~> 0.54.0'
|
|
28
28
|
spec.add_development_dependency 'rubocop-rspec', '1.20.1'
|
|
29
29
|
spec.add_development_dependency 'webmock', '3.7.0'
|
|
30
|
+
spec.add_runtime_dependency 'activesupport', '~> 6.0.2'
|
|
31
|
+
spec.add_runtime_dependency 'gitlab', '~> 4.11.0'
|
|
30
32
|
spec.add_runtime_dependency 'nokogiri', '~> 1.10'
|
|
31
33
|
end
|
data/lib/gitlab/qa.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
module Gitlab
|
|
2
2
|
module QA
|
|
3
3
|
autoload :Release, 'gitlab/qa/release'
|
|
4
|
+
autoload :Reporter, 'gitlab/qa/reporter'
|
|
4
5
|
autoload :Runner, 'gitlab/qa/runner'
|
|
5
6
|
|
|
6
7
|
module Runtime
|
|
7
8
|
autoload :Env, 'gitlab/qa/runtime/env'
|
|
9
|
+
autoload :TokenFinder, 'gitlab/qa/runtime/token_finder'
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
module Scenario
|
|
@@ -86,6 +88,7 @@ module Gitlab
|
|
|
86
88
|
|
|
87
89
|
module Report
|
|
88
90
|
autoload :PrepareStageReports, 'gitlab/qa/report/prepare_stage_reports'
|
|
91
|
+
autoload :ResultsInIssues, 'gitlab/qa/report/results_in_issues'
|
|
89
92
|
end
|
|
90
93
|
end
|
|
91
94
|
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'nokogiri'
|
|
4
|
+
require 'gitlab'
|
|
5
|
+
require 'active_support/core_ext/enumerable'
|
|
6
|
+
|
|
7
|
+
module Gitlab
|
|
8
|
+
# Monkey patch the Gitlab client to use the correct API path
|
|
9
|
+
class Client
|
|
10
|
+
def team_member(project, id)
|
|
11
|
+
get("/projects/#{url_encode project}/members/all/#{id}")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module QA
|
|
16
|
+
module Report
|
|
17
|
+
# Uses the API to create or update GitLab issues with the results of tests from RSpec report files.
|
|
18
|
+
# The GitLab client is used for API access: https://github.com/NARKOZ/gitlab
|
|
19
|
+
class ResultsInIssues
|
|
20
|
+
MAINTAINER_ACCESS_LEVEL = 40
|
|
21
|
+
MAX_TITLE_LENGTH = 255
|
|
22
|
+
|
|
23
|
+
def initialize(token:, input_files:, project: nil)
|
|
24
|
+
@token = token
|
|
25
|
+
@files = Array(input_files)
|
|
26
|
+
@project = project
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def invoke!
|
|
30
|
+
configure_gitlab_client
|
|
31
|
+
|
|
32
|
+
validate_input!
|
|
33
|
+
|
|
34
|
+
puts "Reporting test results in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
|
35
|
+
|
|
36
|
+
Dir.glob(files).each do |file|
|
|
37
|
+
puts "Reporting tests in #{file}"
|
|
38
|
+
Nokogiri::XML(File.open(file)).xpath('//testcase').each do |test|
|
|
39
|
+
report_test(test)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
attr_reader :files, :token, :project
|
|
47
|
+
|
|
48
|
+
def validate_input!
|
|
49
|
+
assert_project!
|
|
50
|
+
assert_input_files!(files)
|
|
51
|
+
assert_user_permission!
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def assert_project!
|
|
55
|
+
return if project
|
|
56
|
+
|
|
57
|
+
abort "Please provide a valid project ID or path with the `-p/--project` option!"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def assert_input_files!(files)
|
|
61
|
+
return if Dir.glob(files).any?
|
|
62
|
+
|
|
63
|
+
abort "Please provide valid JUnit report files. No files were found matching `#{files.join(',')}`"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def assert_user_permission!
|
|
67
|
+
user = Gitlab.user
|
|
68
|
+
member = Gitlab.team_member(project, user.id)
|
|
69
|
+
|
|
70
|
+
abort_not_permitted if member.access_level < MAINTAINER_ACCESS_LEVEL
|
|
71
|
+
rescue Gitlab::Error::NotFound
|
|
72
|
+
abort_not_permitted
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def abort_not_permitted
|
|
76
|
+
abort "You must have at least Maintainer access to the project to use this feature."
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def configure_gitlab_client
|
|
80
|
+
Gitlab.configure do |config|
|
|
81
|
+
config.endpoint = Runtime::Env.gitlab_api_base
|
|
82
|
+
config.private_token = token
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def report_test(test)
|
|
87
|
+
return if test.search('skipped').any?
|
|
88
|
+
|
|
89
|
+
puts "Reporting test name: #{test['name']} | #{test['file']}"
|
|
90
|
+
|
|
91
|
+
issue = find_issue(test)
|
|
92
|
+
if issue
|
|
93
|
+
puts "Found existing issue: #{issue.web_url}"
|
|
94
|
+
else
|
|
95
|
+
issue = create_issue(test)
|
|
96
|
+
puts "Created new issue: #{issue.web_url}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
update_labels(issue, test)
|
|
100
|
+
note_status(issue, test)
|
|
101
|
+
|
|
102
|
+
puts "Issue updated"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def create_issue(test)
|
|
106
|
+
puts "Creating issue for file: #{test['file']} | name: #{test['name']}"
|
|
107
|
+
|
|
108
|
+
Gitlab.create_issue(
|
|
109
|
+
project,
|
|
110
|
+
title_from_test(test),
|
|
111
|
+
{ description: "### Full description\n\n#{search_safe(test['name'])}\n\n### File path\n\n#{test['file']}", labels: 'status::automated' }
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def find_issue(test)
|
|
116
|
+
issues = Gitlab.search_in_project(project, 'issues', search_term(test))
|
|
117
|
+
.auto_paginate
|
|
118
|
+
.select { |issue| issue.state == 'opened' && issue.title.strip == title_from_test(test) }
|
|
119
|
+
|
|
120
|
+
warn(%(Too many issues found with the file path "#{test['file']}" and name "#{test['name']}")) if issues.many?
|
|
121
|
+
|
|
122
|
+
issues.first
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def search_term(test)
|
|
126
|
+
%("#{test['file']}" "#{search_safe(test['name'])}")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def title_from_test(test)
|
|
130
|
+
title = "Results for #{test['file']} | #{search_safe(test['name'])}".strip
|
|
131
|
+
|
|
132
|
+
return title unless title.length > MAX_TITLE_LENGTH
|
|
133
|
+
|
|
134
|
+
"#{title[0...MAX_TITLE_LENGTH - 3]}..."
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def search_safe(value)
|
|
138
|
+
value.delete('"')
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def note_status(issue, test)
|
|
142
|
+
return if failures(test).empty?
|
|
143
|
+
|
|
144
|
+
errors = failures(test).each_with_object([]) do |failure, text|
|
|
145
|
+
text << <<~TEXT
|
|
146
|
+
Error:
|
|
147
|
+
```
|
|
148
|
+
#{failure['message']}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Stacktrace:
|
|
152
|
+
```
|
|
153
|
+
#{failure.content}
|
|
154
|
+
```
|
|
155
|
+
TEXT
|
|
156
|
+
end.join("\n\n")
|
|
157
|
+
|
|
158
|
+
Gitlab.create_issue_note(project, issue.iid, ":x: ~\"#{pipeline}::failed\" in job `#{Runtime::Env.ci_job_name}` in #{Runtime::Env.ci_job_url}\n\n#{errors}")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def update_labels(issue, test)
|
|
162
|
+
labels = issue.labels
|
|
163
|
+
labels.delete_if { |label| label.start_with?("#{pipeline}::") }
|
|
164
|
+
labels << (failures(test).empty? ? "#{pipeline}::passed" : "#{pipeline}::failed")
|
|
165
|
+
|
|
166
|
+
Gitlab.edit_issue(project, issue.iid, labels: labels)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def failures(test)
|
|
170
|
+
test.search('failure')
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def pipeline
|
|
174
|
+
# Gets the name of the pipeline the test was run in, to be used as the key of a scoped label
|
|
175
|
+
#
|
|
176
|
+
# Tests can be run in several pipelines:
|
|
177
|
+
# gitlab-qa, nightly, master, staging, canary, production, preprod, and MRs
|
|
178
|
+
#
|
|
179
|
+
# Some of those run in their own project, so CI_PROJECT_NAME is the name we need. Those are:
|
|
180
|
+
# nightly, staging, canary, production, and preprod
|
|
181
|
+
#
|
|
182
|
+
# MR, master, and gitlab-qa tests run in gitlab-qa, but we only want to report tests run on master
|
|
183
|
+
# because the other pipelines will be monitored by the author of the MR that triggered them.
|
|
184
|
+
# So we assume that we're reporting a master pipeline if the project name is 'gitlab-qa'.
|
|
185
|
+
|
|
186
|
+
Runtime::Env.ci_project_name == 'gitlab-qa' ? 'master' : Runtime::Env.ci_project_name
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
|
|
3
|
+
module Gitlab
|
|
4
|
+
module QA
|
|
5
|
+
class Reporter
|
|
6
|
+
# rubocop:disable Metrics/AbcSize
|
|
7
|
+
def self.invoke(args)
|
|
8
|
+
report_options = {}
|
|
9
|
+
|
|
10
|
+
options = OptionParser.new do |opts|
|
|
11
|
+
opts.banner = 'Usage: gitlab-qa-reporter [options]'
|
|
12
|
+
|
|
13
|
+
opts.on('--prepare-stage-reports FILES', 'Prepare separate reports for each Stage from the provided JUnit XML files') do |files|
|
|
14
|
+
report_options[:prepare_stage_reports] = true
|
|
15
|
+
report_options[:input_files] = files if files
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
opts.on('--report-in-issues FILES', String, 'Report test results from JUnit XML files in GitLab issues') do |files|
|
|
19
|
+
report_options[:report_in_issues] = true
|
|
20
|
+
report_options[:input_files] = files if files
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
opts.on('-p', '--project PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-in-issues') do |value|
|
|
24
|
+
report_options[:project] = value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token. Used by --report-in-issues') do |value|
|
|
28
|
+
report_options[:token] = value
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
opts.on_tail('-v', '--version', 'Show the version') do
|
|
32
|
+
require 'gitlab/qa/version'
|
|
33
|
+
puts "#{$PROGRAM_NAME} : #{VERSION}"
|
|
34
|
+
exit
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
opts.on_tail('-h', '--help', 'Show the usage') do
|
|
38
|
+
puts opts
|
|
39
|
+
exit
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
opts.parse(args)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if args.any?
|
|
46
|
+
if report_options[:prepare_stage_reports]
|
|
47
|
+
report_options.delete(:prepare_stage_reports)
|
|
48
|
+
Gitlab::QA::Report::PrepareStageReports.new(**report_options).invoke!
|
|
49
|
+
|
|
50
|
+
exit
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if report_options[:report_in_issues]
|
|
54
|
+
report_options.delete(:report_in_issues)
|
|
55
|
+
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
|
|
56
|
+
Gitlab::QA::Report::ResultsInIssues.new(**report_options).invoke!
|
|
57
|
+
|
|
58
|
+
exit
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
puts options
|
|
62
|
+
exit 1
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
# rubocop:enable Metrics/AbcSize
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/gitlab/qa/runner.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'optparse'
|
|
|
2
2
|
|
|
3
3
|
module Gitlab
|
|
4
4
|
module QA
|
|
5
|
+
# rubocop:disable Metrics/AbcSize
|
|
5
6
|
class Runner
|
|
6
7
|
# These options are implemented in the QA framework (i.e., in the CE/EE codebase)
|
|
7
8
|
# They're included here so that gitlab-qa treats them as valid options
|
|
@@ -13,10 +14,7 @@ module Gitlab
|
|
|
13
14
|
['--loop', 'Execute tests in a loop']
|
|
14
15
|
].freeze
|
|
15
16
|
|
|
16
|
-
# rubocop:disable Metrics/AbcSize
|
|
17
17
|
def self.run(args)
|
|
18
|
-
report_options = {}
|
|
19
|
-
|
|
20
18
|
options = OptionParser.new do |opts|
|
|
21
19
|
opts.banner = 'Usage: gitlab-qa [options] Scenario URL [[--] path] [rspec_options]'
|
|
22
20
|
|
|
@@ -24,11 +22,6 @@ module Gitlab
|
|
|
24
22
|
opts.on(*opt)
|
|
25
23
|
end
|
|
26
24
|
|
|
27
|
-
opts.on('--prepare-stage-reports FILES', 'Prepare separate reports for each Stage from the provided JUnit XML files') do |files|
|
|
28
|
-
report_options[:prepare_stage_reports] = true
|
|
29
|
-
report_options[:input_files] = files if files
|
|
30
|
-
end
|
|
31
|
-
|
|
32
25
|
opts.on_tail('-v', '--version', 'Show the version') do
|
|
33
26
|
require 'gitlab/qa/version'
|
|
34
27
|
puts "#{$PROGRAM_NAME} : #{VERSION}"
|
|
@@ -44,13 +37,6 @@ module Gitlab
|
|
|
44
37
|
end
|
|
45
38
|
|
|
46
39
|
if args.size >= 1
|
|
47
|
-
if report_options[:prepare_stage_reports]
|
|
48
|
-
report_options.delete(:prepare_stage_reports)
|
|
49
|
-
Gitlab::QA::Report::PrepareStageReports.new(**report_options).invoke!
|
|
50
|
-
|
|
51
|
-
exit
|
|
52
|
-
end
|
|
53
|
-
|
|
54
40
|
Scenario
|
|
55
41
|
.const_get(args.shift)
|
|
56
42
|
.perform(*args)
|
|
@@ -12,6 +12,7 @@ module Gitlab
|
|
|
12
12
|
'QA_REMOTE_GRID_ACCESS_KEY' => :remote_grid_access_key,
|
|
13
13
|
'QA_REMOTE_GRID_PROTOCOL' => :remote_grid_protocol,
|
|
14
14
|
'QA_BROWSER' => :browser,
|
|
15
|
+
'GITLAB_API_BASE' => :api_base,
|
|
15
16
|
'GITLAB_ADMIN_USERNAME' => :admin_username,
|
|
16
17
|
'GITLAB_ADMIN_PASSWORD' => :admin_password,
|
|
17
18
|
'GITLAB_USERNAME' => :user_username,
|
|
@@ -22,7 +23,6 @@ module Gitlab
|
|
|
22
23
|
'GITLAB_FORKER_PASSWORD' => :forker_password,
|
|
23
24
|
'GITLAB_USER_TYPE' => :user_type,
|
|
24
25
|
'GITLAB_SANDBOX_NAME' => :gitlab_sandbox_name,
|
|
25
|
-
'GITLAB_QA_ACCESS_TOKEN' => :qa_access_token,
|
|
26
26
|
'GITLAB_QA_ADMIN_ACCESS_TOKEN' => :qa_admin_access_token,
|
|
27
27
|
'GITHUB_ACCESS_TOKEN' => :github_access_token,
|
|
28
28
|
'GITLAB_URL' => :gitlab_url,
|
|
@@ -78,6 +78,22 @@ module Gitlab
|
|
|
78
78
|
send(:attr_accessor, accessor) # rubocop:disable GitlabSecurity/PublicSend
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
def gitlab_api_base
|
|
82
|
+
ENV['GITLAB_API_BASE'] || 'https://gitlab.com/api/v4'
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def ci_job_name
|
|
86
|
+
ENV['CI_JOB_NAME']
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def ci_job_url
|
|
90
|
+
ENV['CI_JOB_URL']
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def ci_project_name
|
|
94
|
+
ENV['CI_PROJECT_NAME']
|
|
95
|
+
end
|
|
96
|
+
|
|
81
97
|
def run_id
|
|
82
98
|
@run_id ||= "gitlab-qa-run-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}-#{SecureRandom.hex(4)}"
|
|
83
99
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gitlab
|
|
4
|
+
module QA
|
|
5
|
+
module Runtime
|
|
6
|
+
class TokenFinder
|
|
7
|
+
def self.find_token!(token, suffix: nil)
|
|
8
|
+
new(token, suffix).find_token!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :token, :suffix
|
|
12
|
+
|
|
13
|
+
def initialize(token, suffix)
|
|
14
|
+
@token = token
|
|
15
|
+
@suffix = suffix
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def find_token!
|
|
19
|
+
find_token_from_attrs || find_token_from_env || find_token_from_file
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def find_token_from_attrs
|
|
23
|
+
token
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def find_token_from_env
|
|
27
|
+
Env.qa_access_token
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def find_token_from_file
|
|
31
|
+
@token_from_file ||= File.read(token_file_path).strip
|
|
32
|
+
rescue Errno::ENOENT
|
|
33
|
+
fail "Please provide a valid access token with the `-t/--token` option, the `GITLAB_QA_ACCESS_TOKEN` environment variable, or in the `#{token_file_path}` file!"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def token_file_path
|
|
39
|
+
@token_file_path ||= File.expand_path("../api_token#{"_#{suffix}" if suffix}", __dir__)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
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:
|
|
4
|
+
version: 5.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Grzegorz Bizon
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-03-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: climate_control
|
|
@@ -122,6 +122,34 @@ dependencies:
|
|
|
122
122
|
- - '='
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: 3.7.0
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: activesupport
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: 6.0.2
|
|
132
|
+
type: :runtime
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: 6.0.2
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: gitlab
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - "~>"
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: 4.11.0
|
|
146
|
+
type: :runtime
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - "~>"
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: 4.11.0
|
|
125
153
|
- !ruby/object:Gem::Dependency
|
|
126
154
|
name: nokogiri
|
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -141,6 +169,7 @@ email:
|
|
|
141
169
|
- grzesiek.bizon@gmail.com
|
|
142
170
|
executables:
|
|
143
171
|
- gitlab-qa
|
|
172
|
+
- gitlab-qa-report
|
|
144
173
|
extensions: []
|
|
145
174
|
extra_rdoc_files: []
|
|
146
175
|
files:
|
|
@@ -172,6 +201,7 @@ files:
|
|
|
172
201
|
- docs/waits.md
|
|
173
202
|
- docs/what_tests_can_be_run.md
|
|
174
203
|
- exe/gitlab-qa
|
|
204
|
+
- exe/gitlab-qa-report
|
|
175
205
|
- fixtures/ldap/1_add_nodes.ldif
|
|
176
206
|
- fixtures/ldap/2_add_users.ldif
|
|
177
207
|
- fixtures/ldap/3_add_groups.ldif
|
|
@@ -195,8 +225,11 @@ files:
|
|
|
195
225
|
- lib/gitlab/qa/docker/volumes.rb
|
|
196
226
|
- lib/gitlab/qa/release.rb
|
|
197
227
|
- lib/gitlab/qa/report/prepare_stage_reports.rb
|
|
228
|
+
- lib/gitlab/qa/report/results_in_issues.rb
|
|
229
|
+
- lib/gitlab/qa/reporter.rb
|
|
198
230
|
- lib/gitlab/qa/runner.rb
|
|
199
231
|
- lib/gitlab/qa/runtime/env.rb
|
|
232
|
+
- lib/gitlab/qa/runtime/token_finder.rb
|
|
200
233
|
- lib/gitlab/qa/scenario/actable.rb
|
|
201
234
|
- lib/gitlab/qa/scenario/cli_commands.rb
|
|
202
235
|
- lib/gitlab/qa/scenario/template.rb
|