allure-report-publisher 0.0.1 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1362930b746c980e6cc2130d5a304c1be870a6e79658df5143bda833035773a8
4
- data.tar.gz: e23baf8887d7acee4dd919e0e4b71282c6e462476cf0ddd037a6c82d7248a290
3
+ metadata.gz: be6f833714e1ff2ad413a9b5148cebd6454b23db763f567586b9e91222513524
4
+ data.tar.gz: 6984e37c220f693288037522a86716079f3a235634c3ccbdd360056fd186f83c
5
5
  SHA512:
6
- metadata.gz: 2574999aa80c7d2eb499da7f65e8bc42c3f917331d4c10d014d49f04d75f77e0d0c6d4ada2944eb988bb3f70dc7ebac199200e76bc4be86a723381b4624d23a3
7
- data.tar.gz: 28dd428d4fe4d9e52ab381f039772293317d0c667f9580388dd74886dddfaf3fda11b55bd59fcf65e1d6040805bdadeb2ed9118c96e252e7875f2259e7984e90
6
+ metadata.gz: d11ad18cdfb6c1955e521d8aae653b823f5defcfae4b095ae27f9bc9d1d0c89bca0336ab647c543b4c5d2cb92d2bffcd2c23343266b254aa1331984ac02abae9
7
+ data.tar.gz: 023da790f7233de7da68520ea85e2e0388e6c840d3950ba004118a754f46d4c2c0c4570c76ca29add86cf97cc0dc40d859094df430627f4cf57316df89df6bf7
data/README.md CHANGED
@@ -1,41 +1,72 @@
1
+ [![Gem Version](https://img.shields.io/gem/v/allure-report-publisher?color=red)](https://rubygems.org/gems/allure-report-publisher)
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
+ ![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)
1
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/210eaa4f74588fb08313/maintainability)](https://codeclimate.com/github/andrcuns/allure-report-publisher/maintainability)
2
6
  [![Test Coverage](https://api.codeclimate.com/v1/badges/210eaa4f74588fb08313/test_coverage)](https://codeclimate.com/github/andrcuns/allure-report-publisher/test_coverage)
3
7
 
4
- # allure-report-uploader
8
+ # allure-report-publisher
5
9
 
6
10
  Upload your report to a file storage of your choice.
7
11
 
12
+ ![Demo](demo.gif)
13
+
8
14
  ## Installation
9
15
 
10
- ```bash
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
- ### AWS S3
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 s3 --help
35
+ $ (allure-report-publisher|docker run --rm andrcuns/allure-report-publisher:latest) upload --help
20
36
  Command:
21
- allure-report-publisher upload s3
37
+ allure-report-publisher upload
22
38
 
23
39
  Usage:
24
- allure-report-publisher upload s3
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
- --result-files-glob=VALUE # Allure results files glob. Required: true
49
+ --results-glob=VALUE # Allure results files glob. Required: true
31
50
  --bucket=VALUE # Bucket name. Required: true
32
- --project=VALUE # Project name for multiple reports inside single bucket. Required: false
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 --result-files-glob='path/to/allure-result/**/*' --bucket=my-bucket
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-uploader>. 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/[USERNAME]/allure-report-uploader/blob/main/CODE_OF_CONDUCT.md).
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 Allure::Report::Uploader project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/allure-report-uploader/blob/main/CODE_OF_CONDUCT.md).
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
- # General helpers
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
- Pastel.new.decorate(message, color)
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
- puts colorize(message, :red)
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