allure-report-publisher 0.0.3 → 0.1.1
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/README.md +43 -14
- data/lib/allure_report_publisher.rb +2 -5
- data/lib/allure_report_publisher/commands/upload.rb +90 -0
- data/lib/allure_report_publisher/lib/helpers/helpers.rb +1 -1
- data/lib/allure_report_publisher/lib/helpers/spinner.rb +1 -1
- data/lib/allure_report_publisher/lib/providers/_provider.rb +174 -0
- data/lib/allure_report_publisher/lib/providers/github.rb +56 -16
- data/lib/allure_report_publisher/lib/providers/gitlab.rb +42 -13
- data/lib/allure_report_publisher/lib/report_generator.rb +10 -13
- data/lib/allure_report_publisher/lib/uploaders/_uploader.rb +107 -51
- data/lib/allure_report_publisher/lib/uploaders/gcs.rb +104 -0
- data/lib/allure_report_publisher/lib/uploaders/{s3_uploader.rb → s3.rb} +45 -44
- data/lib/allure_report_publisher/version.rb +1 -1
- metadata +30 -10
- data/lib/allure_report_publisher/commands/base.rb +0 -17
- data/lib/allure_report_publisher/commands/upload_s3.rb +0 -43
- data/lib/allure_report_publisher/lib/providers/_base.rb +0 -111
@@ -4,7 +4,7 @@ module Publisher
|
|
4
4
|
module Providers
|
5
5
|
# Gitlab implementation
|
6
6
|
#
|
7
|
-
class Gitlab <
|
7
|
+
class Gitlab < Provider
|
8
8
|
# Get ci run ID without creating instance of ci provider
|
9
9
|
#
|
10
10
|
# @return [String]
|
@@ -12,6 +12,13 @@ module Publisher
|
|
12
12
|
@run_id ||= ENV["CI_PIPELINE_ID"]
|
13
13
|
end
|
14
14
|
|
15
|
+
# Pull request run
|
16
|
+
#
|
17
|
+
# @return [Boolean]
|
18
|
+
def pr?
|
19
|
+
ENV["CI_PIPELINE_SOURCE"] == "merge_request_event"
|
20
|
+
end
|
21
|
+
|
15
22
|
# Get executor info
|
16
23
|
#
|
17
24
|
# @return [Hash]
|
@@ -28,6 +35,8 @@ module Publisher
|
|
28
35
|
}
|
29
36
|
end
|
30
37
|
|
38
|
+
private
|
39
|
+
|
31
40
|
# Current pull request description
|
32
41
|
#
|
33
42
|
# @return [String]
|
@@ -37,10 +46,27 @@ module Publisher
|
|
37
46
|
|
38
47
|
# Update pull request description
|
39
48
|
#
|
40
|
-
# @param [String] desc
|
41
49
|
# @return [void]
|
42
|
-
def update_pr_description
|
43
|
-
client.update_merge_request(project, mr_iid, description:
|
50
|
+
def update_pr_description
|
51
|
+
client.update_merge_request(project, mr_iid, description: updated_pr_description)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add comment with report url
|
55
|
+
#
|
56
|
+
# @return [void]
|
57
|
+
def add_comment
|
58
|
+
return client.create_merge_request_comment(project, mr_iid, comment_body) unless comment
|
59
|
+
|
60
|
+
client.edit_merge_request_note(project, mr_iid, comment.id, comment_body)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Existing comment with allure urls
|
64
|
+
#
|
65
|
+
# @return [Gitlab::ObjectifiedHash]
|
66
|
+
def comment
|
67
|
+
client.merge_request_comments(project, mr_iid).auto_paginate.detect do |comment|
|
68
|
+
comment.body.match?(DESCRIPTION_PATTERN)
|
69
|
+
end
|
44
70
|
end
|
45
71
|
|
46
72
|
# Get gitlab client
|
@@ -57,13 +83,6 @@ module Publisher
|
|
57
83
|
end
|
58
84
|
end
|
59
85
|
|
60
|
-
# Pull request run
|
61
|
-
#
|
62
|
-
# @return [Boolean]
|
63
|
-
def pr?
|
64
|
-
ENV["CI_PIPELINE_SOURCE"] == "merge_request_event"
|
65
|
-
end
|
66
|
-
|
67
86
|
# Merge request iid
|
68
87
|
#
|
69
88
|
# @return [Integer]
|
@@ -89,15 +108,25 @@ module Publisher
|
|
89
108
|
#
|
90
109
|
# @return [String]
|
91
110
|
def build_name
|
92
|
-
@build_name ||= ENV["CI_JOB_NAME"]
|
111
|
+
@build_name ||= ENV[ALLURE_JOB_NAME] || ENV["CI_JOB_NAME"]
|
93
112
|
end
|
94
113
|
|
95
|
-
#
|
114
|
+
# Gitlab repository
|
96
115
|
#
|
97
116
|
# @return [String]
|
98
117
|
def project
|
99
118
|
@project ||= ENV["CI_PROJECT_PATH"]
|
100
119
|
end
|
120
|
+
|
121
|
+
# Commit sha url
|
122
|
+
#
|
123
|
+
# @return [String]
|
124
|
+
def sha_url
|
125
|
+
sha = ENV["CI_MERGE_REQUEST_SOURCE_BRANCH_SHA"] || ENV["CI_COMMIT_SHA"]
|
126
|
+
short_sha = sha[0..7]
|
127
|
+
|
128
|
+
"[#{short_sha}](#{server_url}/#{project}/-/merge_requests/#{mr_iid}/diffs?commit_id=#{sha})"
|
129
|
+
end
|
101
130
|
end
|
102
131
|
end
|
103
132
|
end
|
@@ -10,35 +10,32 @@ module Publisher
|
|
10
10
|
class ReportGenerator
|
11
11
|
include Helpers
|
12
12
|
|
13
|
-
def initialize(results_glob,
|
13
|
+
def initialize(results_glob, results_path, report_path)
|
14
14
|
@results_glob = results_glob
|
15
|
-
@
|
16
|
-
@
|
15
|
+
@results_path = results_path
|
16
|
+
@report_path = report_path
|
17
17
|
end
|
18
18
|
|
19
19
|
# Generate allure report
|
20
20
|
#
|
21
21
|
# @return [void]
|
22
22
|
def generate
|
23
|
-
|
24
|
-
|
25
|
-
aggregate_results
|
26
|
-
generate_report
|
27
|
-
end
|
23
|
+
aggregate_results
|
24
|
+
generate_report
|
28
25
|
end
|
29
26
|
|
30
27
|
private
|
31
28
|
|
32
|
-
attr_reader :results_glob, :
|
29
|
+
attr_reader :results_glob, :results_path, :report_path
|
33
30
|
|
34
31
|
# Copy all results files to results directory
|
35
32
|
#
|
36
33
|
# @return [void]
|
37
34
|
def aggregate_results
|
38
35
|
results = Dir.glob(results_glob)
|
39
|
-
|
36
|
+
raise(NoAllureResultsError, "Missing allure results") if results.empty?
|
40
37
|
|
41
|
-
FileUtils.cp(results,
|
38
|
+
FileUtils.cp(results, results_path)
|
42
39
|
end
|
43
40
|
|
44
41
|
# Generate allure report
|
@@ -46,9 +43,9 @@ module Publisher
|
|
46
43
|
# @return [void]
|
47
44
|
def generate_report
|
48
45
|
out, _err, status = Open3.capture3(
|
49
|
-
"allure generate --clean --output #{
|
46
|
+
"allure generate --clean --output #{report_path} #{results_path}"
|
50
47
|
)
|
51
|
-
|
48
|
+
raise(AllureError, out) unless status.success?
|
52
49
|
end
|
53
50
|
end
|
54
51
|
end
|
@@ -1,10 +1,13 @@
|
|
1
1
|
module Publisher
|
2
2
|
module Uploaders
|
3
|
+
class HistoryNotFoundError < StandardError; end
|
4
|
+
|
3
5
|
# Uploader implementation
|
4
6
|
#
|
5
7
|
class Uploader
|
6
8
|
include Helpers
|
7
9
|
|
10
|
+
EXECUTOR_JSON = "executor.json".freeze
|
8
11
|
HISTORY = [
|
9
12
|
"categories-trend.json",
|
10
13
|
"duration-trend.json",
|
@@ -13,36 +16,76 @@ module Publisher
|
|
13
16
|
"retry-trend.json"
|
14
17
|
].freeze
|
15
18
|
|
16
|
-
def initialize(results_glob
|
19
|
+
def initialize(results_glob:, bucket:, update_pr: nil, prefix: nil, copy_latest: false)
|
17
20
|
@results_glob = results_glob
|
18
|
-
@
|
21
|
+
@bucket_name = bucket
|
19
22
|
@prefix = prefix
|
23
|
+
@update_pr = update_pr
|
24
|
+
@copy_latest = !!(Providers.provider && copy_latest) # copy latest for ci only
|
20
25
|
end
|
21
26
|
|
22
|
-
# :nocov:
|
23
|
-
|
24
27
|
# Execute allure report generation and upload
|
25
28
|
#
|
26
29
|
# @return [void]
|
27
|
-
def execute
|
28
|
-
check_client_configured
|
29
|
-
|
30
|
+
def execute
|
30
31
|
generate_report
|
31
|
-
|
32
|
-
|
33
|
-
rescue StandardError => e
|
34
|
-
error(e.message)
|
32
|
+
upload
|
33
|
+
add_url_to_pr
|
35
34
|
end
|
36
35
|
|
37
|
-
|
36
|
+
# Generate allure report
|
37
|
+
#
|
38
|
+
# @return [void]
|
39
|
+
def generate_report
|
40
|
+
add_history
|
41
|
+
add_executor_info
|
38
42
|
|
39
|
-
|
43
|
+
ReportGenerator.new(results_glob, results_path, report_path).generate
|
44
|
+
end
|
40
45
|
|
41
|
-
#
|
42
|
-
# and raise error if it is not
|
46
|
+
# Upload report to storage provider
|
43
47
|
#
|
44
48
|
# @return [void]
|
45
|
-
def
|
49
|
+
def upload
|
50
|
+
run_uploads
|
51
|
+
end
|
52
|
+
|
53
|
+
# Add allure report url to pull request description
|
54
|
+
#
|
55
|
+
# @return [void]
|
56
|
+
def add_url_to_pr
|
57
|
+
return unless update_pr && ci_provider
|
58
|
+
|
59
|
+
ci_provider.add_report_url
|
60
|
+
end
|
61
|
+
|
62
|
+
# Uploaded report urls
|
63
|
+
#
|
64
|
+
# @return [Hash<String, String>] uploaded report urls
|
65
|
+
def report_urls
|
66
|
+
urls = { "Report url" => report_url }
|
67
|
+
urls["Latest report url"] = latest_report_url if copy_latest
|
68
|
+
|
69
|
+
urls
|
70
|
+
end
|
71
|
+
|
72
|
+
# Executed in PR pipeline
|
73
|
+
#
|
74
|
+
# @return [Boolean]
|
75
|
+
def pr?
|
76
|
+
ci_provider&.pr?
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
attr_reader :results_glob, :bucket_name, :prefix, :update_pr, :copy_latest
|
82
|
+
|
83
|
+
# :nocov:
|
84
|
+
|
85
|
+
# Cloud provider client
|
86
|
+
#
|
87
|
+
# @return [Object]
|
88
|
+
def client
|
46
89
|
raise("Not Implemented!")
|
47
90
|
end
|
48
91
|
|
@@ -53,58 +96,71 @@ module Publisher
|
|
53
96
|
raise("Not Implemented!")
|
54
97
|
end
|
55
98
|
|
56
|
-
#
|
99
|
+
# Latest report url
|
100
|
+
#
|
101
|
+
# @return [String]
|
102
|
+
def latest_report_url
|
103
|
+
raise("Not Implemented!")
|
104
|
+
end
|
105
|
+
|
106
|
+
# Download allure history
|
57
107
|
#
|
58
108
|
# @return [void]
|
59
|
-
def
|
109
|
+
def download_history
|
60
110
|
raise("Not implemented!")
|
61
111
|
end
|
62
112
|
|
63
|
-
#
|
113
|
+
# Upload history to s3
|
64
114
|
#
|
65
115
|
# @return [void]
|
66
|
-
def
|
67
|
-
|
68
|
-
Helpers::Spinner.spin("adding history", exit_on_error: false) do
|
69
|
-
create_history_dir
|
70
|
-
yield
|
71
|
-
end
|
116
|
+
def upload_history
|
117
|
+
raise("Not implemented!")
|
72
118
|
end
|
73
119
|
|
74
|
-
#
|
120
|
+
# Upload report to s3
|
75
121
|
#
|
76
122
|
# @return [void]
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
log("Adding executor info")
|
81
|
-
Helpers::Spinner.spin("adding executor") do
|
82
|
-
ci_provider.write_executor_info
|
83
|
-
end
|
123
|
+
def upload_report
|
124
|
+
raise("Not implemented!")
|
84
125
|
end
|
85
126
|
|
86
|
-
#
|
127
|
+
# Upload copy of latest run
|
87
128
|
#
|
88
129
|
# @return [void]
|
89
|
-
def
|
90
|
-
|
91
|
-
|
130
|
+
def upload_latest_copy
|
131
|
+
raise("Not implemented!")
|
132
|
+
end
|
133
|
+
# :nocov:
|
92
134
|
|
93
|
-
|
135
|
+
# Add allure history
|
136
|
+
#
|
137
|
+
# @return [void]
|
138
|
+
def add_history
|
139
|
+
create_history_dir
|
140
|
+
download_history
|
141
|
+
rescue HistoryNotFoundError
|
142
|
+
nil
|
94
143
|
end
|
95
144
|
|
96
|
-
# Add
|
145
|
+
# Add CI executor info
|
97
146
|
#
|
98
147
|
# @return [void]
|
99
|
-
def
|
148
|
+
def add_executor_info
|
100
149
|
return unless ci_provider
|
101
150
|
|
102
|
-
|
103
|
-
|
104
|
-
ci_provider.add_report_url
|
151
|
+
File.open("#{results_path}/#{EXECUTOR_JSON}", "w") do |file|
|
152
|
+
file.write(ci_provider.executor_info.to_json)
|
105
153
|
end
|
106
154
|
end
|
107
|
-
|
155
|
+
|
156
|
+
# Run upload commands
|
157
|
+
#
|
158
|
+
# @return [void]
|
159
|
+
def run_uploads
|
160
|
+
upload_history unless !run_id || copy_latest
|
161
|
+
upload_report
|
162
|
+
upload_latest_copy if copy_latest
|
163
|
+
end
|
108
164
|
|
109
165
|
# Get run id
|
110
166
|
#
|
@@ -119,14 +175,14 @@ module Publisher
|
|
119
175
|
def ci_provider
|
120
176
|
return @ci_provider if defined?(@ci_provider)
|
121
177
|
|
122
|
-
@ci_provider = Providers.provider&.new(
|
178
|
+
@ci_provider = Providers.provider&.new(report_url: report_url, update_pr: update_pr)
|
123
179
|
end
|
124
180
|
|
125
181
|
# Fetch allure report history
|
126
182
|
#
|
127
183
|
# @return [void]
|
128
184
|
def create_history_dir
|
129
|
-
FileUtils.mkdir_p(path(
|
185
|
+
FileUtils.mkdir_p(path(results_path, "history"))
|
130
186
|
end
|
131
187
|
|
132
188
|
# Report path prefix
|
@@ -143,15 +199,15 @@ module Publisher
|
|
143
199
|
# Aggregated results directory
|
144
200
|
#
|
145
201
|
# @return [String]
|
146
|
-
def
|
147
|
-
@
|
202
|
+
def results_path
|
203
|
+
@results_path ||= Dir.mktmpdir("allure-results")
|
148
204
|
end
|
149
205
|
|
150
206
|
# Allure report directory
|
151
207
|
#
|
152
208
|
# @return [String]
|
153
|
-
def
|
154
|
-
@
|
209
|
+
def report_path
|
210
|
+
@report_path ||= Dir.mktmpdir("allure-report")
|
155
211
|
end
|
156
212
|
|
157
213
|
# Report files
|
@@ -159,7 +215,7 @@ module Publisher
|
|
159
215
|
# @return [Array<Pathname>]
|
160
216
|
def report_files
|
161
217
|
@report_files ||= Pathname
|
162
|
-
.glob("#{
|
218
|
+
.glob("#{report_path}/**/*")
|
163
219
|
.reject(&:directory?)
|
164
220
|
end
|
165
221
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require "google/cloud/storage"
|
2
|
+
|
3
|
+
module Publisher
|
4
|
+
module Uploaders
|
5
|
+
# Google cloud storage uploader implementation
|
6
|
+
#
|
7
|
+
class GCS < Uploader
|
8
|
+
private
|
9
|
+
|
10
|
+
# GCS client
|
11
|
+
#
|
12
|
+
# @return [Google::Cloud::Storage::Project]
|
13
|
+
def client
|
14
|
+
@client ||= Google::Cloud::Storage.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# GCS bucket
|
18
|
+
#
|
19
|
+
# @return [Google::Cloud::Storage::Bucket]
|
20
|
+
def bucket
|
21
|
+
@bucket ||= client.bucket(bucket_name, skip_lookup: true)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Report url
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
def report_url
|
28
|
+
@report_url ||= url(full_prefix)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Latest report url
|
32
|
+
#
|
33
|
+
# @return [String]
|
34
|
+
def latest_report_url
|
35
|
+
@latest_report_url ||= url(prefix)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Download allure history
|
39
|
+
#
|
40
|
+
# @return [void]
|
41
|
+
def download_history
|
42
|
+
HISTORY.each do |file_name|
|
43
|
+
file = bucket.file(key(prefix, "history", file_name))
|
44
|
+
raise(HistoryNotFoundError, "Allure history from previous runs not found!") unless file
|
45
|
+
|
46
|
+
file.download(path(results_path, "history", file_name))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Upload allure history
|
51
|
+
#
|
52
|
+
# @return [void]
|
53
|
+
def upload_history
|
54
|
+
upload_to_gcs(report_files.select { |file| file.fnmatch?("*/history/*") }, prefix)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Upload allure report
|
58
|
+
#
|
59
|
+
# @return [void]
|
60
|
+
def upload_report
|
61
|
+
upload_to_gcs(report_files, full_prefix)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Upload copy of latest run
|
65
|
+
#
|
66
|
+
# @return [void]
|
67
|
+
def upload_latest_copy
|
68
|
+
upload_to_gcs(report_files, prefix)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Upload files to s3
|
72
|
+
#
|
73
|
+
# @param [Array<Pathname>] files
|
74
|
+
# @param [String] key_prefix
|
75
|
+
# @return [Array<Hash>]
|
76
|
+
def upload_to_gcs(files, key_prefix)
|
77
|
+
args = files.map do |file|
|
78
|
+
{
|
79
|
+
file: file.to_s,
|
80
|
+
path: key(key_prefix, file.relative_path_from(report_path))
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
Parallel.each(args, in_threads: 8) { |obj| bucket.create_file(obj[:file], obj[:path]) }
|
85
|
+
end
|
86
|
+
|
87
|
+
# Fabricate key for s3 object
|
88
|
+
#
|
89
|
+
# @param [String] *args
|
90
|
+
# @return [String]
|
91
|
+
def key(*args)
|
92
|
+
args.compact.join("/")
|
93
|
+
end
|
94
|
+
|
95
|
+
# Report url
|
96
|
+
#
|
97
|
+
# @param [String] path_prefix
|
98
|
+
# @return [String]
|
99
|
+
def url(path_prefix)
|
100
|
+
["https://storage.googleapis.com", bucket_name, path_prefix, "index.html"].compact.join("/")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|