allure-report-publisher 0.0.1 → 0.0.6
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 +42 -11
- data/lib/allure_report_publisher.rb +4 -5
- data/lib/allure_report_publisher/commands/upload.rb +90 -0
- data/lib/allure_report_publisher/{helpers.rb → lib/helpers/helpers.rb} +25 -25
- data/lib/allure_report_publisher/lib/helpers/spinner.rb +109 -0
- data/lib/allure_report_publisher/lib/providers/_provider.rb +111 -0
- data/lib/allure_report_publisher/lib/providers/github.rb +102 -0
- data/lib/allure_report_publisher/lib/providers/gitlab.rb +103 -0
- data/lib/allure_report_publisher/lib/report_generator.rb +4 -7
- data/lib/allure_report_publisher/lib/uploaders/_uploader.rb +146 -24
- 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 +60 -7
- data/lib/allure_report_publisher/commands/upload_s3.rb +0 -41
- data/lib/allure_report_publisher/lib/uploaders/s3_uploader.rb +0 -96
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be6f833714e1ff2ad413a9b5148cebd6454b23db763f567586b9e91222513524
|
4
|
+
data.tar.gz: 6984e37c220f693288037522a86716079f3a235634c3ccbdd360056fd186f83c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d11ad18cdfb6c1955e521d8aae653b823f5defcfae4b095ae27f9bc9d1d0c89bca0336ab647c543b4c5d2cb92d2bffcd2c23343266b254aa1331984ac02abae9
|
7
|
+
data.tar.gz: 023da790f7233de7da68520ea85e2e0388e6c840d3950ba004118a754f46d4c2c0c4570c76ca29add86cf97cc0dc40d859094df430627f4cf57316df89df6bf7
|
data/README.md
CHANGED
@@ -1,41 +1,72 @@
|
|
1
|
+
[](https://rubygems.org/gems/allure-report-publisher)
|
2
|
+
[](https://hub.docker.com/r/andrcuns/allure-report-publisher)
|
3
|
+

|
4
|
+
[](http://allure-reports-andrcuns.s3.amazonaws.com/allure-report-publisher/refs/heads/main/index.html)
|
1
5
|
[](https://codeclimate.com/github/andrcuns/allure-report-publisher/maintainability)
|
2
6
|
[](https://codeclimate.com/github/andrcuns/allure-report-publisher/test_coverage)
|
3
7
|
|
4
|
-
# allure-report-
|
8
|
+
# allure-report-publisher
|
5
9
|
|
6
10
|
Upload your report to a file storage of your choice.
|
7
11
|
|
12
|
+

|
13
|
+
|
8
14
|
## Installation
|
9
15
|
|
10
|
-
|
16
|
+
### Rubygems
|
17
|
+
|
18
|
+
```shell
|
11
19
|
gem install allure-report-uploader
|
12
20
|
```
|
13
21
|
|
22
|
+
### Docker
|
23
|
+
|
24
|
+
```shell
|
25
|
+
docker pull andrcuns/allure-report-publisher:latest
|
26
|
+
```
|
27
|
+
|
14
28
|
## Usage
|
15
29
|
|
16
|
-
|
30
|
+
allure-report-publisher will automatically detect if used in CI environment and add relevant executor info and history
|
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
|
17
33
|
|
18
34
|
```shell
|
19
|
-
$ allure-report-publisher upload
|
35
|
+
$ (allure-report-publisher|docker run --rm andrcuns/allure-report-publisher:latest) upload --help
|
20
36
|
Command:
|
21
|
-
allure-report-publisher upload
|
37
|
+
allure-report-publisher upload
|
22
38
|
|
23
39
|
Usage:
|
24
|
-
allure-report-publisher upload
|
40
|
+
allure-report-publisher upload TYPE
|
25
41
|
|
26
42
|
Description:
|
27
43
|
Generate and upload allure report
|
28
44
|
|
45
|
+
Arguments:
|
46
|
+
TYPE # REQUIRED Cloud storage type: (s3/gcs)
|
47
|
+
|
29
48
|
Options:
|
30
|
-
--
|
49
|
+
--results-glob=VALUE # Allure results files glob. Required: true
|
31
50
|
--bucket=VALUE # Bucket name. Required: true
|
32
|
-
--
|
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
|
33
55
|
--help, -h # Print this help
|
34
56
|
|
35
57
|
Examples:
|
36
|
-
allure-report-publisher upload s3 --
|
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
|
37
60
|
```
|
38
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
|
+
|
39
70
|
## Development
|
40
71
|
|
41
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.
|
@@ -44,7 +75,7 @@ To install this gem onto your local machine, run `bundle exec rake install`.
|
|
44
75
|
|
45
76
|
## Contributing
|
46
77
|
|
47
|
-
Bug reports and pull requests are welcome on GitHub at <https://github.com/andrcuns/allure-report-
|
78
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/andrcuns/allure-report-publisher>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/andrcuns/allure-report-publisher/blob/main/CODE_OF_CONDUCT.md).
|
48
79
|
|
49
80
|
## License
|
50
81
|
|
@@ -52,4 +83,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
52
83
|
|
53
84
|
## Code of Conduct
|
54
85
|
|
55
|
-
Everyone interacting in the
|
86
|
+
Everyone interacting in the allure-report-publisher project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/andrcuns/allure-report-publisher/blob/main/CODE_OF_CONDUCT.md).
|
@@ -4,7 +4,7 @@ require "require_all"
|
|
4
4
|
require "parallel"
|
5
5
|
require "dry/cli"
|
6
6
|
|
7
|
-
require_rel "allure_report_publisher/helpers"
|
7
|
+
require_rel "allure_report_publisher/lib/helpers/*.rb"
|
8
8
|
require_rel "allure_report_publisher/**/*.rb"
|
9
9
|
|
10
10
|
module Publisher
|
@@ -14,9 +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
|
20
|
+
|
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: :boolean,
|
24
|
+
default: false,
|
25
|
+
desc: "Update pull request description with url to allure report"
|
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
|
@@ -1,18 +1,37 @@
|
|
1
|
-
require "tty-spinner"
|
2
1
|
require "pastel"
|
3
2
|
require "open3"
|
4
3
|
|
5
4
|
module Publisher
|
6
|
-
#
|
5
|
+
# Helpers
|
7
6
|
#
|
8
7
|
module Helpers
|
8
|
+
# Global instance of pastel
|
9
|
+
#
|
10
|
+
# @param [Boolean] force_color
|
11
|
+
# @return [Pastel]
|
12
|
+
def self.pastel(force_color: nil)
|
13
|
+
@pastel ||= Pastel.new(enabled: force_color)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Check allure cli is installed and executable
|
17
|
+
#
|
18
|
+
# @return [void]
|
19
|
+
def self.validate_allure_cli_present
|
20
|
+
_out, status = Open3.capture2("which allure")
|
21
|
+
return if status.success?
|
22
|
+
|
23
|
+
Helpers.error(
|
24
|
+
"Allure cli is missing! See https://docs.qameta.io/allure/#_installing_a_commandline on how to install it!"
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
9
28
|
# Colorize string
|
10
29
|
#
|
11
30
|
# @param [String] message
|
12
31
|
# @param [Symbol] color
|
13
32
|
# @return [String]
|
14
33
|
def colorize(message, color)
|
15
|
-
|
34
|
+
Helpers.pastel.decorate(message, color)
|
16
35
|
end
|
17
36
|
|
18
37
|
# Log message to stdout
|
@@ -29,7 +48,7 @@ module Publisher
|
|
29
48
|
# @param [String] message
|
30
49
|
# @return [void]
|
31
50
|
def error(message)
|
32
|
-
|
51
|
+
warn colorize(message, :red)
|
33
52
|
exit(1)
|
34
53
|
end
|
35
54
|
|
@@ -41,27 +60,6 @@ module Publisher
|
|
41
60
|
File.join(args).to_s
|
42
61
|
end
|
43
62
|
|
44
|
-
# Execute code inside spinner
|
45
|
-
#
|
46
|
-
# @param [String] message
|
47
|
-
# @param [Boolean] auto_debrief
|
48
|
-
# @param [String] done_message
|
49
|
-
# @return [Boolean]
|
50
|
-
def spin(message, done_message: "done")
|
51
|
-
spinner = TTY::Spinner.new(
|
52
|
-
"[:spinner] #{message} ...",
|
53
|
-
format: :dots,
|
54
|
-
success_mark: colorize(TTY::Spinner::TICK, :green),
|
55
|
-
error_mark: colorize(TTY::Spinner::CROSS, :red)
|
56
|
-
)
|
57
|
-
spinner.auto_spin
|
58
|
-
yield
|
59
|
-
spinner.success(done_message)
|
60
|
-
rescue StandardError => e
|
61
|
-
spinner.error(colorize(e.message, :red))
|
62
|
-
exit(1)
|
63
|
-
end
|
64
|
-
|
65
63
|
# Execute shell command
|
66
64
|
#
|
67
65
|
# @param [String] command
|
@@ -72,5 +70,7 @@ module Publisher
|
|
72
70
|
|
73
71
|
out
|
74
72
|
end
|
73
|
+
|
74
|
+
module_function :colorize, :log, :error, :path, :execute_shell
|
75
75
|
end
|
76
76
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require "tty-spinner"
|
2
|
+
|
3
|
+
module Publisher
|
4
|
+
# Helpers
|
5
|
+
#
|
6
|
+
module Helpers
|
7
|
+
# Spinner helper class
|
8
|
+
#
|
9
|
+
class Spinner
|
10
|
+
include Helpers
|
11
|
+
|
12
|
+
def initialize(spinner_message, exit_on_error: true)
|
13
|
+
@spinner_message = spinner_message
|
14
|
+
@exit_on_error = exit_on_error
|
15
|
+
end
|
16
|
+
|
17
|
+
# Run code block inside spinner
|
18
|
+
#
|
19
|
+
# @param [String] spinner_message
|
20
|
+
# @param [String] done_message
|
21
|
+
# @param [Boolean] exit_on_error
|
22
|
+
# @param [Proc] &block
|
23
|
+
# @return [void]
|
24
|
+
def self.spin(spinner_message, done_message: "done", exit_on_error: true, &block)
|
25
|
+
new(spinner_message, exit_on_error: exit_on_error).spin(done_message, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Run code block inside spinner
|
29
|
+
#
|
30
|
+
# @param [String] done_message
|
31
|
+
# @return [Boolean]
|
32
|
+
def spin(done_message = "done")
|
33
|
+
spinner.auto_spin
|
34
|
+
yield
|
35
|
+
spinner_success(done_message)
|
36
|
+
rescue StandardError => e
|
37
|
+
spinner_error(e.message)
|
38
|
+
exit(1) if exit_on_error
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :spinner_message, :exit_on_error
|
45
|
+
|
46
|
+
# Error message color
|
47
|
+
#
|
48
|
+
# @return [Symbol]
|
49
|
+
def error_color
|
50
|
+
@error_color ||= exit_on_error ? :red : :yellow
|
51
|
+
end
|
52
|
+
|
53
|
+
# Success mark
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
def success_mark
|
57
|
+
@success_mark ||= colorize(TTY::Spinner::TICK, :green)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Error mark
|
61
|
+
#
|
62
|
+
# @return [String]
|
63
|
+
def error_mark
|
64
|
+
colorize(TTY::Spinner::CROSS, error_color)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Spinner instance
|
68
|
+
#
|
69
|
+
# @return [TTY::Spinner]
|
70
|
+
def spinner
|
71
|
+
@spinner ||= TTY::Spinner.new(
|
72
|
+
"[:spinner] #{spinner_message} ...",
|
73
|
+
format: :dots,
|
74
|
+
success_mark: success_mark,
|
75
|
+
error_mark: error_mark
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Check tty
|
80
|
+
#
|
81
|
+
# @return [Boolean]
|
82
|
+
def tty?
|
83
|
+
spinner.send(:tty?)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return spinner success
|
87
|
+
#
|
88
|
+
# @param [String] done_message
|
89
|
+
# @return [void]
|
90
|
+
def spinner_success(done_message)
|
91
|
+
return spinner.success(done_message) if tty?
|
92
|
+
|
93
|
+
spinner.stop
|
94
|
+
puts("[#{success_mark}] #{spinner_message} ... #{done_message}")
|
95
|
+
end
|
96
|
+
|
97
|
+
# Return spinner error
|
98
|
+
#
|
99
|
+
# @param [String] error_message
|
100
|
+
# @return [void]
|
101
|
+
def spinner_error(error_message)
|
102
|
+
colored_message = colorize(error_message, error_color)
|
103
|
+
return spinner.error(colored_message) if tty?
|
104
|
+
|
105
|
+
spinner.stop
|
106
|
+
puts("[#{error_mark}] #{spinner_message} ... #{colored_message}")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,111 @@
|
|
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
|
+
EXECUTOR_JSON = "executor.json".freeze
|
17
|
+
DESCRIPTION_PATTERN = /<!-- allure -->[\s\S]+<!-- allurestop -->/.freeze
|
18
|
+
|
19
|
+
def initialize(results_path, report_url)
|
20
|
+
@results_path = results_path
|
21
|
+
@report_url = report_url
|
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
|
+
# :nocov:
|
33
|
+
|
34
|
+
# Write executor info file
|
35
|
+
#
|
36
|
+
# @return [void]
|
37
|
+
def write_executor_info
|
38
|
+
File.open("#{results_path}/#{EXECUTOR_JSON}", "w") do |file|
|
39
|
+
file.write(executor_info.to_json)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Add report url to pull request description
|
44
|
+
#
|
45
|
+
# @return [void]
|
46
|
+
def add_report_url
|
47
|
+
raise("Not a pull request, skipped!") unless pr?
|
48
|
+
|
49
|
+
reported = pr_description.match?(DESCRIPTION_PATTERN)
|
50
|
+
return update_pr_description(pr_description.gsub(DESCRIPTION_PATTERN, description_template).strip) if reported
|
51
|
+
|
52
|
+
update_pr_description("#{pr_description}\n\n#{description_template}".strip)
|
53
|
+
end
|
54
|
+
|
55
|
+
# :nocov:
|
56
|
+
|
57
|
+
# Pull request run
|
58
|
+
#
|
59
|
+
# @return [Boolean]
|
60
|
+
def pr?
|
61
|
+
raise("Not implemented!")
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
attr_reader :results_path, :report_url
|
67
|
+
|
68
|
+
# Get executor info
|
69
|
+
#
|
70
|
+
# @return [Hash]
|
71
|
+
def executor_info
|
72
|
+
raise("Not implemented!")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Current pull request description
|
76
|
+
#
|
77
|
+
# @return [String]
|
78
|
+
def pr_description
|
79
|
+
raise("Not implemented!")
|
80
|
+
end
|
81
|
+
|
82
|
+
# Update pull request description
|
83
|
+
#
|
84
|
+
# @param [String] _desc
|
85
|
+
# @return [void]
|
86
|
+
def update_pr_description(_desc)
|
87
|
+
raise("Not implemented!")
|
88
|
+
end
|
89
|
+
# :nocov:
|
90
|
+
|
91
|
+
# CI run id
|
92
|
+
#
|
93
|
+
# @return [String]
|
94
|
+
def run_id
|
95
|
+
self.class.run_id
|
96
|
+
end
|
97
|
+
|
98
|
+
# Allure report url pr description
|
99
|
+
#
|
100
|
+
# @return [String]
|
101
|
+
def description_template
|
102
|
+
<<~DESC
|
103
|
+
<!-- allure -->
|
104
|
+
---
|
105
|
+
📝 [Latest allure report](#{report_url})
|
106
|
+
<!-- allurestop -->
|
107
|
+
DESC
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|