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.
@@ -4,7 +4,7 @@ module Publisher
4
4
  module Providers
5
5
  # Gitlab implementation
6
6
  #
7
- class Gitlab < Base
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(desc)
43
- client.update_merge_request(project, mr_iid, description: desc)
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
- # Github repository
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, results_dir, report_dir)
13
+ def initialize(results_glob, results_path, report_path)
14
14
  @results_glob = results_glob
15
- @results_dir = results_dir
16
- @report_dir = report_dir
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
- log("Generating allure report")
24
- Helpers::Spinner.spin("generating report") do
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, :results_dir, :report_dir
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
- error("Missing allure results") if results.empty?
36
+ raise(NoAllureResultsError, "Missing allure results") if results.empty?
40
37
 
41
- FileUtils.cp(results, results_dir)
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 #{report_dir} #{results_dir}"
46
+ "allure generate --clean --output #{report_path} #{results_path}"
50
47
  )
51
- error(out) unless status.success?
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, bucket, prefix = nil)
19
+ def initialize(results_glob:, bucket:, update_pr: nil, prefix: nil, copy_latest: false)
17
20
  @results_glob = results_glob
18
- @bucket = bucket
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(update_pr: false)
28
- check_client_configured
29
-
30
+ def execute
30
31
  generate_report
31
- upload_history_and_report
32
- add_report_url if update_pr
33
- rescue StandardError => e
34
- error(e.message)
32
+ upload
33
+ add_url_to_pr
35
34
  end
36
35
 
37
- private
36
+ # Generate allure report
37
+ #
38
+ # @return [void]
39
+ def generate_report
40
+ add_history
41
+ add_executor_info
38
42
 
39
- attr_reader :results_glob, :bucket, :prefix
43
+ ReportGenerator.new(results_glob, results_path, report_path).generate
44
+ end
40
45
 
41
- # Validate if client is properly configured
42
- # and raise error if it is not
46
+ # Upload report to storage provider
43
47
  #
44
48
  # @return [void]
45
- def check_client_configured
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
- # Upload report to s3
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 upload_history_and_report
109
+ def download_history
60
110
  raise("Not implemented!")
61
111
  end
62
112
 
63
- # Add allure history
113
+ # Upload history to s3
64
114
  #
65
115
  # @return [void]
66
- def add_history
67
- log("Adding allure history")
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
- # Add CI executor info
120
+ # Upload report to s3
75
121
  #
76
122
  # @return [void]
77
- def add_executor_info
78
- return unless ci_provider
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
- # Generate allure report
127
+ # Upload copy of latest run
87
128
  #
88
129
  # @return [void]
89
- def generate_report
90
- add_history
91
- add_executor_info
130
+ def upload_latest_copy
131
+ raise("Not implemented!")
132
+ end
133
+ # :nocov:
92
134
 
93
- ReportGenerator.new(results_glob, results_dir, report_dir).generate
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 allure report url to pull request description
145
+ # Add CI executor info
97
146
  #
98
147
  # @return [void]
99
- def add_report_url
148
+ def add_executor_info
100
149
  return unless ci_provider
101
150
 
102
- log("Adding allure report link to pr description")
103
- Helpers::Spinner.spin("adding link", exit_on_error: false) do
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
- # :nocov:
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(results_dir, report_url)
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(results_dir, "history"))
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 results_dir
147
- @results_dir ||= Dir.mktmpdir("allure-results")
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 report_dir
154
- @report_dir ||= Dir.mktmpdir("allure-report")
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("#{report_dir}/**/*")
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