allure-report-publisher 0.0.2 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7267f00f39421165f5b1fb7c4e975cff6e856d7ff321bf5b90ac7ab629fff6a
4
- data.tar.gz: 6189da5c7b8743ee4fb102b01f07e9e419996a249b90a8a43601658f2c1ef6cb
3
+ metadata.gz: 2212afd20c080df89b88eab18ae8fa96a6b8d00027af95b2b606e9b851b83dde
4
+ data.tar.gz: b6473ed381ddb267af1fcaee0ef6080a3c8063dac4d56ea5cbf0589457c7598e
5
5
  SHA512:
6
- metadata.gz: 52e25db16ed4068c4556162fd4bfe917b138f2e814dd2d0bf6a019b8dd462014ba7af39cbc6ebe62f0d547bee4ca5ea0813e0719f138fb4cc0890ee2f501ea33
7
- data.tar.gz: 6e1a0903f7923fd2abb0538819f24fce3530827280dd1e568be1dc16942f8bdbdca2c03fe4816e7d3bb00d3b631cfe8a7d4bea0f9af77f3e6c8a9afcfed4ab62
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
- ### AWS S3
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 s3 --help
35
+ $ (allure-report-publisher|docker run --rm andrcuns/allure-report-publisher:latest) upload --help
35
36
  Command:
36
- allure-report-publisher upload s3
37
+ allure-report-publisher upload
37
38
 
38
39
  Usage:
39
- allure-report-publisher upload s3
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
- --[no-]color # Toggle color output
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 --result-files-glob='path/to/allure-result/**/*' --bucket=my-bucket
53
- allure-report-publisher upload s3 --result-files-glob='path/to/allure-result/**/*' --bucket=my-bucket --project=my-project/prs
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 s3") { Publisher::Helpers.validate_allure_cli_present }
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
- puts colorize(message, :red)
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