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 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