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