allure-report-publisher 0.0.4 → 0.2.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.
@@ -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: report_urls.updated_pr_description(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, report_urls.comment_body) unless comment
59
+
60
+ client.edit_merge_request_note(project, mr_iid, comment.id, report_urls.comment_body(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
+ UrlSectionBuilder.match?(comment.body)
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
@@ -0,0 +1,97 @@
1
+ module Publisher
2
+ module Providers
3
+ # Urls section builder
4
+ #
5
+ class UrlSectionBuilder
6
+ DESCRIPTION_PATTERN = /<!-- allure -->[\s\S]+<!-- allurestop -->/.freeze
7
+ JOBS_PATTERN = /<!-- jobs -->\n([\s\S]+)\n<!-- jobs -->/.freeze
8
+
9
+ def initialize(report_url:, build_name:, sha_url:)
10
+ @report_url = report_url
11
+ @build_name = build_name
12
+ @sha_url = sha_url
13
+ end
14
+
15
+ # Matches url section pattern
16
+ #
17
+ # @param [String] urls_block
18
+ # @return [Boolean]
19
+ def self.match?(urls_block)
20
+ urls_block.match?(DESCRIPTION_PATTERN)
21
+ end
22
+
23
+ # Get urls for PR update
24
+ #
25
+ # @param [String] pr
26
+ # @return [String]
27
+ def updated_pr_description(pr_description)
28
+ return "#{pr_description}\n\n#{body}" unless pr_description.match?(DESCRIPTION_PATTERN)
29
+
30
+ job_entries = jobs_section(pr_description)
31
+ pr_description.gsub(DESCRIPTION_PATTERN, body(job_entries))
32
+ end
33
+
34
+ # Allure report url comment
35
+ #
36
+ # @return [String]
37
+ def comment_body(pr_comment = nil)
38
+ return body.gsub("---\n", "") unless pr_comment
39
+
40
+ job_entries = jobs_section(pr_comment)
41
+ body(job_entries).gsub("---\n", "")
42
+ end
43
+
44
+ attr_reader :report_url, :build_name, :sha_url
45
+
46
+ private
47
+
48
+ # Allure report url pr description
49
+ #
50
+ # @return [String]
51
+ def body(job_entries = "**#{build_name}**: 📝 [allure report](#{report_url})")
52
+ @body ||= <<~BODY.strip
53
+ <!-- allure -->
54
+ ---
55
+ #{heading}
56
+
57
+ <!-- jobs -->
58
+ #{job_entries}
59
+ <!-- jobs -->
60
+ <!-- allurestop -->
61
+ BODY
62
+ end
63
+
64
+ # Url section heading
65
+ #
66
+ # @return [String]
67
+ def heading
68
+ @heading ||= "# Allure report\n`allure-report-publisher` generated allure report for #{sha_url}!"
69
+ end
70
+
71
+ # Return updated jobs section
72
+ #
73
+ # @param [String] urls
74
+ # @return [String]
75
+ def jobs_section(urls_block)
76
+ jobs = urls_block.match(JOBS_PATTERN)[1]
77
+ return jobs.gsub(job_entry_pattern, job_entry) if jobs.match?(job_entry_pattern)
78
+
79
+ "#{jobs}\n#{job_entry}"
80
+ end
81
+
82
+ # Single job report URL entry
83
+ #
84
+ # @return [String]
85
+ def job_entry
86
+ @job_entry ||= "**#{build_name}**: 📝 [allure report](#{report_url})"
87
+ end
88
+
89
+ # Job entry pattern
90
+ #
91
+ # @return [RegExp]
92
+ def job_entry_pattern
93
+ @job_entry_pattern ||= /^\*\*#{build_name}\*\*:.*\[allure report\]\(.*\)$/
94
+ end
95
+ end
96
+ end
97
+ 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,38 +16,76 @@ module Publisher
13
16
  "retry-trend.json"
14
17
  ].freeze
15
18
 
16
- def initialize(results_glob:, bucket:, update_pr: false, prefix: nil, copy_latest: false)
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
20
23
  @update_pr = update_pr
21
- @copy_latest = Providers.provider && copy_latest # copy latest for ci only
24
+ @copy_latest = !!(Providers.provider && copy_latest) # copy latest for ci only
22
25
  end
23
26
 
24
- # :nocov:
25
-
26
27
  # Execute allure report generation and upload
27
28
  #
28
29
  # @return [void]
29
30
  def execute
30
- check_client_configured
31
-
32
- generate
31
+ generate_report
33
32
  upload
34
33
  add_url_to_pr
35
- rescue StandardError => e
36
- error(e.message)
37
34
  end
38
35
 
39
- private
36
+ # Generate allure report
37
+ #
38
+ # @return [void]
39
+ def generate_report
40
+ add_history
41
+ add_executor_info
40
42
 
41
- attr_reader :results_glob, :bucket, :prefix, :update_pr, :copy_latest
43
+ ReportGenerator.new(results_glob, results_path, report_path).generate
44
+ end
42
45
 
43
- # Validate if client is properly configured
44
- # and raise error if it is not
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
45
54
  #
46
55
  # @return [void]
47
- def check_client_configured
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
48
89
  raise("Not Implemented!")
49
90
  end
50
91
 
@@ -55,6 +96,20 @@ module Publisher
55
96
  raise("Not Implemented!")
56
97
  end
57
98
 
99
+ # Latest report url
100
+ #
101
+ # @return [String]
102
+ def latest_report_url
103
+ raise("Not Implemented!")
104
+ end
105
+
106
+ # Download allure history
107
+ #
108
+ # @return [void]
109
+ def download_history
110
+ raise("Not implemented!")
111
+ end
112
+
58
113
  # Upload history to s3
59
114
  #
60
115
  # @return [void]
@@ -81,11 +136,10 @@ module Publisher
81
136
  #
82
137
  # @return [void]
83
138
  def add_history
84
- log("Adding allure history")
85
- Helpers::Spinner.spin("adding history", exit_on_error: false) do
86
- create_history_dir
87
- yield
88
- end
139
+ create_history_dir
140
+ download_history
141
+ rescue HistoryNotFoundError
142
+ nil
89
143
  end
90
144
 
91
145
  # Add CI executor info
@@ -94,53 +148,20 @@ module Publisher
94
148
  def add_executor_info
95
149
  return unless ci_provider
96
150
 
97
- log("Adding executor info")
98
- Helpers::Spinner.spin("adding executor") do
99
- ci_provider.write_executor_info
151
+ File.open("#{results_path}/#{EXECUTOR_JSON}", "w") do |file|
152
+ file.write(ci_provider.executor_info.to_json)
100
153
  end
101
154
  end
102
155
 
103
- # Generate allure report
104
- #
105
- # @return [void]
106
- def generate
107
- add_history
108
- add_executor_info
109
-
110
- ReportGenerator.new(results_glob, results_dir, report_dir).generate
111
- end
112
-
113
- # Upload report to storage provider
114
- #
115
- # @return [void]
116
- def upload
117
- log("Uploading report")
118
- Helpers::Spinner.spin("uploading report") { run_uploads }
119
- log("Run report: #{report_url}", :green)
120
- log("Latest report: #{latest_report_url}", :green) if copy_latest
121
- end
122
-
123
156
  # Run upload commands
124
157
  #
125
158
  # @return [void]
126
159
  def run_uploads
127
- upload_history unless copy_latest # latest report will add a common history folder
160
+ upload_history unless !run_id || copy_latest
128
161
  upload_report
129
162
  upload_latest_copy if copy_latest
130
163
  end
131
164
 
132
- # Add allure report url to pull request description
133
- #
134
- # @return [void]
135
- def add_url_to_pr
136
- return unless update_pr && ci_provider
137
-
138
- log("Adding allure report link to pr description")
139
- Helpers::Spinner.spin("adding link", exit_on_error: false) do
140
- ci_provider.add_report_url
141
- end
142
- end
143
-
144
165
  # Get run id
145
166
  #
146
167
  # @return [String]
@@ -154,14 +175,14 @@ module Publisher
154
175
  def ci_provider
155
176
  return @ci_provider if defined?(@ci_provider)
156
177
 
157
- @ci_provider = Providers.provider&.new(results_dir, report_url)
178
+ @ci_provider = Providers.provider&.new(report_url: report_url, update_pr: update_pr)
158
179
  end
159
180
 
160
181
  # Fetch allure report history
161
182
  #
162
183
  # @return [void]
163
184
  def create_history_dir
164
- FileUtils.mkdir_p(path(results_dir, "history"))
185
+ FileUtils.mkdir_p(path(results_path, "history"))
165
186
  end
166
187
 
167
188
  # Report path prefix
@@ -178,15 +199,15 @@ module Publisher
178
199
  # Aggregated results directory
179
200
  #
180
201
  # @return [String]
181
- def results_dir
182
- @results_dir ||= Dir.mktmpdir("allure-results")
202
+ def results_path
203
+ @results_path ||= Dir.mktmpdir("allure-results")
183
204
  end
184
205
 
185
206
  # Allure report directory
186
207
  #
187
208
  # @return [String]
188
- def report_dir
189
- @report_dir ||= Dir.mktmpdir("allure-report")
209
+ def report_path
210
+ @report_path ||= Dir.mktmpdir("allure-report")
190
211
  end
191
212
 
192
213
  # Report files
@@ -194,7 +215,7 @@ module Publisher
194
215
  # @return [Array<Pathname>]
195
216
  def report_files
196
217
  @report_files ||= Pathname
197
- .glob("#{report_dir}/**/*")
218
+ .glob("#{report_path}/**/*")
198
219
  .reject(&:directory?)
199
220
  end
200
221
  end