allure-report-publisher 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2212afd20c080df89b88eab18ae8fa96a6b8d00027af95b2b606e9b851b83dde
|
4
|
+
data.tar.gz: b6473ed381ddb267af1fcaee0ef6080a3c8063dac4d56ea5cbf0589457c7598e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e28a752bdaaf619bf293c6a4f995a17e7cdea913c6c1c89be07dcb14c575a2b78264bbb6fad03d2b1ec574c2d0c72fd04ce84431e7920e6ac7180b5f4006c54c
|
7
|
+
data.tar.gz: aa8f768c03494a14b6dcafc9ada76764b028f750f6ff104627bb669c431321074fbac76ae81ab84df6510988d7cf161a9863c796c6615e343193ae2708979419
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
[![Gem Version](https://img.shields.io/gem/v/allure-report-publisher?color=red)](https://rubygems.org/gems/allure-report-publisher)
|
2
2
|
[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/andrcuns/allure-report-publisher?color=blue&label=docker&sort=semver)](https://hub.docker.com/r/andrcuns/allure-report-publisher)
|
3
3
|
![Workflow status](https://github.com/andrcuns/allure-report-publisher/workflows/Test/badge.svg)
|
4
|
+
[![Test Report](https://img.shields.io/badge/report-allure-blue.svg)](http://allure-reports-andrcuns.s3.amazonaws.com/allure-report-publisher/refs/heads/main/index.html)
|
4
5
|
[![Maintainability](https://api.codeclimate.com/v1/badges/210eaa4f74588fb08313/maintainability)](https://codeclimate.com/github/andrcuns/allure-report-publisher/maintainability)
|
5
6
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/210eaa4f74588fb08313/test_coverage)](https://codeclimate.com/github/andrcuns/allure-report-publisher/test_coverage)
|
6
7
|
|
@@ -28,31 +29,44 @@ docker pull andrcuns/allure-report-publisher:latest
|
|
28
29
|
|
29
30
|
allure-report-publisher will automatically detect if used in CI environment and add relevant executor info and history
|
30
31
|
|
31
|
-
|
32
|
+
- `Allure report link`: requires `GITHUB_AUTH_TOKEN` or `GITLAB_AUTH_TOKEN` in order to update pull request description with link to latest report
|
32
33
|
|
33
34
|
```shell
|
34
|
-
$ (allure-report-publisher|docker run --rm andrcuns/allure-report-publisher:latest) upload
|
35
|
+
$ (allure-report-publisher|docker run --rm andrcuns/allure-report-publisher:latest) upload --help
|
35
36
|
Command:
|
36
|
-
allure-report-publisher upload
|
37
|
+
allure-report-publisher upload
|
37
38
|
|
38
39
|
Usage:
|
39
|
-
allure-report-publisher upload
|
40
|
+
allure-report-publisher upload TYPE
|
40
41
|
|
41
42
|
Description:
|
42
43
|
Generate and upload allure report
|
43
44
|
|
45
|
+
Arguments:
|
46
|
+
TYPE # REQUIRED Cloud storage type: (s3/gcs)
|
47
|
+
|
44
48
|
Options:
|
45
|
-
--
|
46
|
-
--result-files-glob=VALUE # Allure results files glob. Required: true
|
49
|
+
--results-glob=VALUE # Allure results files glob. Required: true
|
47
50
|
--bucket=VALUE # Bucket name. Required: true
|
48
51
|
--prefix=VALUE # Optional prefix for report path. Required: false
|
52
|
+
--[no-]update-pr # Update pull request description with url to allure report, default: false
|
53
|
+
--[no-]copy-latest # Keep copy of latest report at base prefix path, default: false
|
54
|
+
--[no-]color # Toggle color output, default: false
|
49
55
|
--help, -h # Print this help
|
50
56
|
|
51
57
|
Examples:
|
52
|
-
allure-report-publisher upload s3 --
|
53
|
-
allure-report-publisher upload
|
58
|
+
allure-report-publisher upload s3 --results-glob='path/to/allure-result/**/*' --bucket=my-bucket
|
59
|
+
allure-report-publisher upload gcs --results-glob='path/to/allure-result/**/*' --bucket=my-bucket --prefix=my-project/prs
|
54
60
|
```
|
55
61
|
|
62
|
+
### AWS S3
|
63
|
+
|
64
|
+
- `AWS authentication`: requires environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` or credentials file `~/.aws/credentials`
|
65
|
+
|
66
|
+
### Google Cloud Storage
|
67
|
+
|
68
|
+
- `GCS authentication`: requires environment variable `GOOGLE_CLOUD_CREDENTIALS_JSON` with contents of credentials.json
|
69
|
+
|
56
70
|
## Development
|
57
71
|
|
58
72
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -14,11 +14,8 @@ module Publisher
|
|
14
14
|
extend Dry::CLI::Registry
|
15
15
|
|
16
16
|
register "version", Version, aliases: ["-v", "--version"]
|
17
|
-
|
18
|
-
register "upload" do |prefix|
|
19
|
-
prefix.register "s3", UploadS3
|
20
|
-
end
|
17
|
+
register "upload", Upload, aliases: ["u"]
|
21
18
|
end
|
22
19
|
end
|
23
20
|
|
24
|
-
Publisher::Commands.before("upload
|
21
|
+
Publisher::Commands.before("upload") { Publisher::Helpers.validate_allure_cli_present }
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Publisher
|
2
|
+
module Commands
|
3
|
+
# Upload allure report
|
4
|
+
#
|
5
|
+
class Upload < Dry::CLI::Command
|
6
|
+
include Helpers
|
7
|
+
|
8
|
+
desc "Generate and upload allure report"
|
9
|
+
|
10
|
+
argument :type,
|
11
|
+
type: :string,
|
12
|
+
required: true,
|
13
|
+
values: %w[s3 gcs],
|
14
|
+
desc: "Cloud storage type"
|
15
|
+
|
16
|
+
option :results_glob,
|
17
|
+
desc: "Allure results files glob. Required: true"
|
18
|
+
option :bucket,
|
19
|
+
desc: "Bucket name. Required: true"
|
20
|
+
option :prefix,
|
21
|
+
desc: "Optional prefix for report path. Required: false"
|
22
|
+
option :update_pr,
|
23
|
+
type: :string,
|
24
|
+
values: %w[comment description],
|
25
|
+
desc: "Add report url to PR via comment or description update"
|
26
|
+
option :copy_latest,
|
27
|
+
type: :boolean,
|
28
|
+
default: false,
|
29
|
+
desc: "Keep copy of latest report at base prefix path"
|
30
|
+
option :color,
|
31
|
+
type: :boolean,
|
32
|
+
default: false,
|
33
|
+
desc: "Toggle color output"
|
34
|
+
|
35
|
+
example [
|
36
|
+
"s3 --results-glob='path/to/allure-result/**/*' --bucket=my-bucket",
|
37
|
+
"gcs --results-glob='path/to/allure-result/**/*' --bucket=my-bucket --prefix=my-project/prs"
|
38
|
+
]
|
39
|
+
|
40
|
+
def call(**args)
|
41
|
+
validate_args(args)
|
42
|
+
validate_result_files(args[:results_glob])
|
43
|
+
Helpers.pastel(force_color: args[:color] || nil)
|
44
|
+
|
45
|
+
uploader = uploaders(args[:type]).new(**args.slice(:results_glob, :bucket, :prefix, :copy_latest, :update_pr))
|
46
|
+
|
47
|
+
log("Generating allure report")
|
48
|
+
Spinner.spin("generating") { uploader.generate_report }
|
49
|
+
|
50
|
+
log("Uploading allure report to #{args[:type]}")
|
51
|
+
Spinner.spin("uploading") { uploader.upload }
|
52
|
+
uploader.report_urls.each { |k, v| log("#{k}: #{v}", :green) }
|
53
|
+
return unless args[:update_pr] && uploader.pr?
|
54
|
+
|
55
|
+
log("Updating pull request description")
|
56
|
+
Spinner.spin("updating", exit_on_error: false) { uploader.add_url_to_pr }
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Uploader class
|
62
|
+
#
|
63
|
+
# @param [String] uploader
|
64
|
+
# @return [Publisher::Uploaders::Uploader]
|
65
|
+
def uploaders(uploader)
|
66
|
+
{
|
67
|
+
"s3" => Uploaders::S3,
|
68
|
+
"gcs" => Uploaders::GCS
|
69
|
+
}[uploader]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Validate required args
|
73
|
+
#
|
74
|
+
# @param [Hash] args
|
75
|
+
# @return [void]
|
76
|
+
def validate_args(args)
|
77
|
+
error("Missing argument --results-glob!") unless args[:results_glob]
|
78
|
+
error("Missing argument --bucket!") unless args[:bucket]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Check if allure results present
|
82
|
+
#
|
83
|
+
# @param [String] results_glob
|
84
|
+
# @return [void]
|
85
|
+
def validate_result_files(results_glob)
|
86
|
+
Dir.glob(results_glob).empty? && error("Glob '#{results_glob}' did not match any files!")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -10,7 +10,7 @@ module Publisher
|
|
10
10
|
# @param [Boolean] force_color
|
11
11
|
# @return [Pastel]
|
12
12
|
def self.pastel(force_color: nil)
|
13
|
-
@pastel ||= Pastel.new(enabled: force_color)
|
13
|
+
@pastel ||= Pastel.new(enabled: force_color, eachline: "\n")
|
14
14
|
end
|
15
15
|
|
16
16
|
# Check allure cli is installed and executable
|
@@ -48,7 +48,7 @@ module Publisher
|
|
48
48
|
# @param [String] message
|
49
49
|
# @return [void]
|
50
50
|
def error(message)
|
51
|
-
|
51
|
+
warn colorize(message, :red)
|
52
52
|
exit(1)
|
53
53
|
end
|
54
54
|
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module Publisher
|
2
|
+
# Namespace for providers executing tests
|
3
|
+
#
|
4
|
+
module Providers
|
5
|
+
# Detect CI provider
|
6
|
+
#
|
7
|
+
# @return [Publisher::Providers::Base]
|
8
|
+
def self.provider
|
9
|
+
return Github if ENV["GITHUB_WORKFLOW"]
|
10
|
+
return Gitlab if ENV["GITLAB_CI"]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Base class for CI executor info
|
14
|
+
#
|
15
|
+
class Provider
|
16
|
+
DESCRIPTION_PATTERN = /<!-- allure -->[\s\S]+<!-- allurestop -->/.freeze
|
17
|
+
ALLURE_JOB_NAME = "ALLURE_JOB_NAME".freeze
|
18
|
+
|
19
|
+
def initialize(report_url:, update_pr:)
|
20
|
+
@report_url = report_url
|
21
|
+
@update_pr = update_pr
|
22
|
+
end
|
23
|
+
|
24
|
+
# :nocov:
|
25
|
+
|
26
|
+
# Get ci run ID without creating instance of ci provider
|
27
|
+
#
|
28
|
+
# @return [String]
|
29
|
+
def self.run_id
|
30
|
+
raise("Not implemented!")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get executor info
|
34
|
+
#
|
35
|
+
# @return [Hash]
|
36
|
+
def executor_info
|
37
|
+
raise("Not implemented!")
|
38
|
+
end
|
39
|
+
# :nocov:
|
40
|
+
|
41
|
+
# Add report url to pull request description
|
42
|
+
#
|
43
|
+
# @return [void]
|
44
|
+
def add_report_url
|
45
|
+
raise("Not a pull request, skipped!") unless pr?
|
46
|
+
return add_comment if comment?
|
47
|
+
|
48
|
+
update_pr_description
|
49
|
+
end
|
50
|
+
|
51
|
+
# :nocov:
|
52
|
+
|
53
|
+
# Pull request run
|
54
|
+
#
|
55
|
+
# @return [Boolean]
|
56
|
+
def pr?
|
57
|
+
raise("Not implemented!")
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :report_url, :update_pr
|
63
|
+
|
64
|
+
# Current pull request description
|
65
|
+
#
|
66
|
+
# @return [String]
|
67
|
+
def pr_description
|
68
|
+
raise("Not implemented!")
|
69
|
+
end
|
70
|
+
|
71
|
+
# Update pull request description
|
72
|
+
#
|
73
|
+
# @return [void]
|
74
|
+
def update_pr_description
|
75
|
+
raise("Not implemented!")
|
76
|
+
end
|
77
|
+
|
78
|
+
# Add comment with report url
|
79
|
+
#
|
80
|
+
# @return [void]
|
81
|
+
def add_comment
|
82
|
+
raise("Not implemented!")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Commit SHA url
|
86
|
+
#
|
87
|
+
# @return [String]
|
88
|
+
def sha_url
|
89
|
+
raise("Not implemented!")
|
90
|
+
end
|
91
|
+
# :nocov:
|
92
|
+
|
93
|
+
# Add report url as comment
|
94
|
+
#
|
95
|
+
# @return [Boolean]
|
96
|
+
def comment?
|
97
|
+
update_pr == "comment"
|
98
|
+
end
|
99
|
+
|
100
|
+
# CI run id
|
101
|
+
#
|
102
|
+
# @return [String]
|
103
|
+
def run_id
|
104
|
+
self.class.run_id
|
105
|
+
end
|
106
|
+
|
107
|
+
# Check if PR already has report urls
|
108
|
+
#
|
109
|
+
# @return [Boolean]
|
110
|
+
def reported?
|
111
|
+
@reported ||= pr_description.match?(DESCRIPTION_PATTERN)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Full PR description
|
115
|
+
#
|
116
|
+
# @return [String]
|
117
|
+
def updated_pr_description
|
118
|
+
reported? ? existing_pr_description : initial_pr_descripion
|
119
|
+
end
|
120
|
+
|
121
|
+
# Updated PR description
|
122
|
+
#
|
123
|
+
# @return [String]
|
124
|
+
def existing_pr_description
|
125
|
+
pr_description.gsub(DESCRIPTION_PATTERN, pr_body).strip
|
126
|
+
end
|
127
|
+
|
128
|
+
# Initial PR description
|
129
|
+
#
|
130
|
+
# @return [String]
|
131
|
+
def initial_pr_descripion
|
132
|
+
"#{pr_description}\n\n#{pr_body}".strip
|
133
|
+
end
|
134
|
+
|
135
|
+
# Heading for report urls
|
136
|
+
#
|
137
|
+
# @return [String]
|
138
|
+
def heading
|
139
|
+
@heading ||= <<~HEADING.strip
|
140
|
+
# Allure report
|
141
|
+
`allure-report-publisher` generated allure report for #{sha_url}!
|
142
|
+
HEADING
|
143
|
+
end
|
144
|
+
|
145
|
+
# Allure report url pr description
|
146
|
+
#
|
147
|
+
# @return [String]
|
148
|
+
def pr_body
|
149
|
+
@pr_body ||= <<~DESC
|
150
|
+
<!-- allure -->
|
151
|
+
---
|
152
|
+
#{heading}
|
153
|
+
|
154
|
+
#{job_entry}
|
155
|
+
<!-- allurestop -->
|
156
|
+
DESC
|
157
|
+
end
|
158
|
+
|
159
|
+
# Allure report url comment body
|
160
|
+
#
|
161
|
+
# @return [String]
|
162
|
+
def comment_body
|
163
|
+
@comment_body ||= pr_body.gsub("---\n", "")
|
164
|
+
end
|
165
|
+
|
166
|
+
# Single job report URL entry
|
167
|
+
#
|
168
|
+
# @return [String]
|
169
|
+
def job_entry
|
170
|
+
@job_entry ||= "**#{build_name}**: 📝 [allure report](#{report_url})"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require "octokit"
|
2
|
+
|
3
|
+
module Publisher
|
4
|
+
module Providers
|
5
|
+
# Github implementation
|
6
|
+
#
|
7
|
+
class Github < Provider
|
8
|
+
# Set octokit to autopaginate
|
9
|
+
#
|
10
|
+
Octokit.configure do |config|
|
11
|
+
config.auto_paginate = true
|
12
|
+
end
|
13
|
+
|
14
|
+
# Run id
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
def self.run_id
|
18
|
+
@run_id ||= ENV["GITHUB_RUN_ID"]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Pull request run
|
22
|
+
#
|
23
|
+
# @return [Boolean]
|
24
|
+
def pr?
|
25
|
+
ENV["GITHUB_EVENT_NAME"] == "pull_request"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Executor info
|
29
|
+
#
|
30
|
+
# @return [Hash]
|
31
|
+
def executor_info
|
32
|
+
{
|
33
|
+
name: "Github",
|
34
|
+
type: "github",
|
35
|
+
reportName: "AllureReport",
|
36
|
+
url: server_url,
|
37
|
+
reportUrl: report_url,
|
38
|
+
buildUrl: build_url,
|
39
|
+
buildOrder: run_id,
|
40
|
+
buildName: build_name
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Github api client
|
47
|
+
#
|
48
|
+
# @return [Octokit::Client]
|
49
|
+
def client
|
50
|
+
@client ||= begin
|
51
|
+
raise("Missing GITHUB_AUTH_TOKEN environment variable!") unless ENV["GITHUB_AUTH_TOKEN"]
|
52
|
+
|
53
|
+
Octokit::Client.new(access_token: ENV["GITHUB_AUTH_TOKEN"], api_endpoint: ENV["GITHUB_API_URL"])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Update pull request description
|
58
|
+
#
|
59
|
+
# @return [void]
|
60
|
+
def update_pr_description
|
61
|
+
client.update_pull_request(repository, pr_id, body: updated_pr_description)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Add comment with report url
|
65
|
+
#
|
66
|
+
# @return [void]
|
67
|
+
def add_comment
|
68
|
+
return client.add_comment(repository, pr_id, comment_body) unless comment
|
69
|
+
|
70
|
+
client.update_comment(repository, comment[:id], comment_body)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Existing comment with allure urls
|
74
|
+
#
|
75
|
+
# @return [Sawyer::Resource]
|
76
|
+
def comment
|
77
|
+
@comment ||= client.issue_comments(repository, pr_id).detect do |comment|
|
78
|
+
comment[:body].match?(DESCRIPTION_PATTERN)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Github event
|
83
|
+
#
|
84
|
+
# @return [Hash]
|
85
|
+
def github_event
|
86
|
+
@github_event ||= JSON.parse(File.read(ENV["GITHUB_EVENT_PATH"]), symbolize_names: true)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Pull request description
|
90
|
+
#
|
91
|
+
# @return [String]
|
92
|
+
def pr_description
|
93
|
+
@pr_description ||= client.pull_request(repository, pr_id)[:body]
|
94
|
+
end
|
95
|
+
|
96
|
+
# Pull request id
|
97
|
+
#
|
98
|
+
# @return [Integer]
|
99
|
+
def pr_id
|
100
|
+
@pr_id ||= github_event[:number]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Server url
|
104
|
+
#
|
105
|
+
# @return [String]
|
106
|
+
def server_url
|
107
|
+
@server_url ||= ENV["GITHUB_SERVER_URL"]
|
108
|
+
end
|
109
|
+
|
110
|
+
# Build url
|
111
|
+
#
|
112
|
+
# @return [String]
|
113
|
+
def build_url
|
114
|
+
@build_url ||= "#{server_url}/#{repository}/actions/runs/#{run_id}"
|
115
|
+
end
|
116
|
+
|
117
|
+
# Job name
|
118
|
+
#
|
119
|
+
# @return [String]
|
120
|
+
def build_name
|
121
|
+
@build_name ||= ENV[ALLURE_JOB_NAME] || ENV["GITHUB_JOB"]
|
122
|
+
end
|
123
|
+
|
124
|
+
# Github repository
|
125
|
+
#
|
126
|
+
# @return [String]
|
127
|
+
def repository
|
128
|
+
@repository ||= ENV["GITHUB_REPOSITORY"]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Commit sha url
|
132
|
+
#
|
133
|
+
# @return [String]
|
134
|
+
def sha_url
|
135
|
+
sha = github_event.dig(:pull_request, :head, :sha)
|
136
|
+
short_sha = sha[0..7]
|
137
|
+
|
138
|
+
"[#{short_sha}](#{server_url}/#{repository}/pull/#{pr_id}/commits/#{sha})"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|