allure-report-publisher 0.0.2 → 0.1.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/README.md +22 -8
- 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 +2 -2
- data/lib/allure_report_publisher/lib/providers/_provider.rb +174 -0
- data/lib/allure_report_publisher/lib/providers/github.rb +142 -0
- data/lib/allure_report_publisher/lib/providers/gitlab.rb +132 -0
- data/lib/allure_report_publisher/lib/report_generator.rb +10 -13
- data/lib/allure_report_publisher/lib/uploaders/_uploader.rb +127 -41
- data/lib/allure_report_publisher/lib/uploaders/gcs.rb +104 -0
- data/lib/allure_report_publisher/lib/uploaders/s3.rb +107 -0
- data/lib/allure_report_publisher/version.rb +1 -1
- metadata +58 -9
- data/lib/allure_report_publisher/commands/base.rb +0 -13
- data/lib/allure_report_publisher/commands/upload_s3.rb +0 -43
- data/lib/allure_report_publisher/lib/ci/_base.rb +0 -63
- data/lib/allure_report_publisher/lib/ci/github_actions.rb +0 -50
- data/lib/allure_report_publisher/lib/uploaders/s3_uploader.rb +0 -94
@@ -0,0 +1,132 @@
|
|
1
|
+
require "gitlab"
|
2
|
+
|
3
|
+
module Publisher
|
4
|
+
module Providers
|
5
|
+
# Gitlab implementation
|
6
|
+
#
|
7
|
+
class Gitlab < Provider
|
8
|
+
# Get ci run ID without creating instance of ci provider
|
9
|
+
#
|
10
|
+
# @return [String]
|
11
|
+
def self.run_id
|
12
|
+
@run_id ||= ENV["CI_PIPELINE_ID"]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Pull request run
|
16
|
+
#
|
17
|
+
# @return [Boolean]
|
18
|
+
def pr?
|
19
|
+
ENV["CI_PIPELINE_SOURCE"] == "merge_request_event"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Get executor info
|
23
|
+
#
|
24
|
+
# @return [Hash]
|
25
|
+
def executor_info
|
26
|
+
{
|
27
|
+
name: "Gitlab",
|
28
|
+
type: "gitlab",
|
29
|
+
reportName: "AllureReport",
|
30
|
+
url: server_url,
|
31
|
+
reportUrl: report_url,
|
32
|
+
buildUrl: build_url,
|
33
|
+
buildOrder: run_id,
|
34
|
+
buildName: build_name
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Current pull request description
|
41
|
+
#
|
42
|
+
# @return [String]
|
43
|
+
def pr_description
|
44
|
+
@pr_description ||= client.merge_request(project, mr_iid).description
|
45
|
+
end
|
46
|
+
|
47
|
+
# Update pull request description
|
48
|
+
#
|
49
|
+
# @return [void]
|
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
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get gitlab client
|
73
|
+
#
|
74
|
+
# @return [Gitlab::Client]
|
75
|
+
def client
|
76
|
+
@client ||= begin
|
77
|
+
raise("Missing GITLAB_AUTH_TOKEN environment variable!") unless ENV["GITLAB_AUTH_TOKEN"]
|
78
|
+
|
79
|
+
::Gitlab::Client.new(
|
80
|
+
endpoint: "#{server_url}/api/v4",
|
81
|
+
private_token: ENV["GITLAB_AUTH_TOKEN"]
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Merge request iid
|
87
|
+
#
|
88
|
+
# @return [Integer]
|
89
|
+
def mr_iid
|
90
|
+
@mr_iid ||= ENV["CI_MERGE_REQUEST_IID"]
|
91
|
+
end
|
92
|
+
|
93
|
+
# Server url
|
94
|
+
#
|
95
|
+
# @return [String]
|
96
|
+
def server_url
|
97
|
+
@server_url ||= ENV["CI_SERVER_URL"]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Build url
|
101
|
+
#
|
102
|
+
# @return [String]
|
103
|
+
def build_url
|
104
|
+
@build_url ||= ENV["CI_PIPELINE_URL"]
|
105
|
+
end
|
106
|
+
|
107
|
+
# Job name
|
108
|
+
#
|
109
|
+
# @return [String]
|
110
|
+
def build_name
|
111
|
+
@build_name ||= ENV[ALLURE_JOB_NAME] || ENV["CI_JOB_NAME"]
|
112
|
+
end
|
113
|
+
|
114
|
+
# Gitlab repository
|
115
|
+
#
|
116
|
+
# @return [String]
|
117
|
+
def project
|
118
|
+
@project ||= ENV["CI_PROJECT_PATH"]
|
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
|
130
|
+
end
|
131
|
+
end
|
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,61 +16,130 @@ 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
30
|
def execute
|
28
|
-
|
31
|
+
generate_report
|
32
|
+
upload
|
33
|
+
add_url_to_pr
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generate allure report
|
37
|
+
#
|
38
|
+
# @return [void]
|
39
|
+
def generate_report
|
40
|
+
add_history
|
41
|
+
add_executor_info
|
42
|
+
|
43
|
+
ReportGenerator.new(results_glob, results_path, report_path).generate
|
44
|
+
end
|
45
|
+
|
46
|
+
# Upload report to storage provider
|
47
|
+
#
|
48
|
+
# @return [void]
|
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?
|
29
77
|
end
|
30
|
-
# :nocov:
|
31
78
|
|
32
79
|
private
|
33
80
|
|
34
|
-
attr_reader :results_glob, :
|
81
|
+
attr_reader :results_glob, :bucket_name, :prefix, :update_pr, :copy_latest
|
35
82
|
|
36
83
|
# :nocov:
|
37
84
|
|
85
|
+
# Cloud provider client
|
86
|
+
#
|
87
|
+
# @return [Object]
|
88
|
+
def client
|
89
|
+
raise("Not Implemented!")
|
90
|
+
end
|
91
|
+
|
38
92
|
# Report url
|
39
93
|
#
|
40
94
|
# @return [String]
|
41
95
|
def report_url
|
42
|
-
raise(
|
96
|
+
raise("Not Implemented!")
|
43
97
|
end
|
44
|
-
# :nocov:
|
45
98
|
|
46
|
-
#
|
99
|
+
# Latest report url
|
47
100
|
#
|
48
101
|
# @return [String]
|
49
|
-
def
|
50
|
-
|
102
|
+
def latest_report_url
|
103
|
+
raise("Not Implemented!")
|
51
104
|
end
|
52
105
|
|
53
|
-
#
|
106
|
+
# Download allure history
|
54
107
|
#
|
55
|
-
# @return [
|
56
|
-
def
|
57
|
-
|
108
|
+
# @return [void]
|
109
|
+
def download_history
|
110
|
+
raise("Not implemented!")
|
111
|
+
end
|
112
|
+
|
113
|
+
# Upload history to s3
|
114
|
+
#
|
115
|
+
# @return [void]
|
116
|
+
def upload_history
|
117
|
+
raise("Not implemented!")
|
118
|
+
end
|
58
119
|
|
59
|
-
|
120
|
+
# Upload report to s3
|
121
|
+
#
|
122
|
+
# @return [void]
|
123
|
+
def upload_report
|
124
|
+
raise("Not implemented!")
|
125
|
+
end
|
126
|
+
|
127
|
+
# Upload copy of latest run
|
128
|
+
#
|
129
|
+
# @return [void]
|
130
|
+
def upload_latest_copy
|
131
|
+
raise("Not implemented!")
|
60
132
|
end
|
133
|
+
# :nocov:
|
61
134
|
|
62
135
|
# Add allure history
|
63
136
|
#
|
64
137
|
# @return [void]
|
65
138
|
def add_history
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
139
|
+
create_history_dir
|
140
|
+
download_history
|
141
|
+
rescue HistoryNotFoundError
|
142
|
+
nil
|
71
143
|
end
|
72
144
|
|
73
145
|
# Add CI executor info
|
@@ -76,17 +148,41 @@ module Publisher
|
|
76
148
|
def add_executor_info
|
77
149
|
return unless ci_provider
|
78
150
|
|
79
|
-
|
80
|
-
|
81
|
-
ci_provider.write_executor_info
|
151
|
+
File.open("#{results_path}/#{EXECUTOR_JSON}", "w") do |file|
|
152
|
+
file.write(ci_provider.executor_info.to_json)
|
82
153
|
end
|
83
154
|
end
|
84
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
|
164
|
+
|
165
|
+
# Get run id
|
166
|
+
#
|
167
|
+
# @return [String]
|
168
|
+
def run_id
|
169
|
+
@run_id ||= Providers.provider&.run_id
|
170
|
+
end
|
171
|
+
|
172
|
+
# Get CI provider
|
173
|
+
#
|
174
|
+
# @return [Publisher::Providers::Base]
|
175
|
+
def ci_provider
|
176
|
+
return @ci_provider if defined?(@ci_provider)
|
177
|
+
|
178
|
+
@ci_provider = Providers.provider&.new(report_url: report_url, update_pr: update_pr)
|
179
|
+
end
|
180
|
+
|
85
181
|
# Fetch allure report history
|
86
182
|
#
|
87
183
|
# @return [void]
|
88
184
|
def create_history_dir
|
89
|
-
FileUtils.mkdir_p(path(
|
185
|
+
FileUtils.mkdir_p(path(results_path, "history"))
|
90
186
|
end
|
91
187
|
|
92
188
|
# Report path prefix
|
@@ -103,15 +199,15 @@ module Publisher
|
|
103
199
|
# Aggregated results directory
|
104
200
|
#
|
105
201
|
# @return [String]
|
106
|
-
def
|
107
|
-
@
|
202
|
+
def results_path
|
203
|
+
@results_path ||= Dir.mktmpdir("allure-results")
|
108
204
|
end
|
109
205
|
|
110
206
|
# Allure report directory
|
111
207
|
#
|
112
208
|
# @return [String]
|
113
|
-
def
|
114
|
-
@
|
209
|
+
def report_path
|
210
|
+
@report_path ||= Dir.mktmpdir("allure-report")
|
115
211
|
end
|
116
212
|
|
117
213
|
# Report files
|
@@ -119,19 +215,9 @@ module Publisher
|
|
119
215
|
# @return [Array<Pathname>]
|
120
216
|
def report_files
|
121
217
|
@report_files ||= Pathname
|
122
|
-
.glob("#{
|
218
|
+
.glob("#{report_path}/**/*")
|
123
219
|
.reject(&:directory?)
|
124
220
|
end
|
125
|
-
|
126
|
-
# Generate allure report
|
127
|
-
#
|
128
|
-
# @return [void]
|
129
|
-
def generate_report
|
130
|
-
add_history
|
131
|
-
add_executor_info
|
132
|
-
|
133
|
-
ReportGenerator.new(results_glob, results_dir, report_dir).generate
|
134
|
-
end
|
135
221
|
end
|
136
222
|
end
|
137
223
|
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
|