github_bot 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.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Only include in the context of a rails application
4
+ return unless defined?(Rails)
5
+
6
+ require 'octokit'
7
+
8
+ module GithubBot
9
+ # Public: Railtie for injecting configuration of Octokit in a Rails environment
10
+ class Railtie < ::Rails::Railtie
11
+ config.after_initialize do
12
+ Octokit.configure { |c| c.api_endpoint = "https://#{ENV['GITHUB_HOST']}/api/v3/" } if ENV['GITHUB_HOST'].present?
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GithubBot
4
+ module Tasks
5
+ # Public: Base class for establishing tasks to be executed via a validator
6
+ class Base
7
+ # Public: Returns the validator associated to the task execution
8
+ attr_reader :validator
9
+
10
+ # Public: Initialize the task from a specific validator
11
+ #
12
+ # @param validator [GithubBot::Validator::Base] An instance of the validator that is asking for the task
13
+ def initialize(validator)
14
+ @validator = validator
15
+ end
16
+
17
+ private
18
+
19
+ # because tasks are executed by a validator, some methods are relayed across
20
+ # from the task back to the validator. this override checks for that existence
21
+ def method_missing(method, *args, &block)
22
+ return super unless respond_to_missing?(method)
23
+
24
+ @validator.send(method, *args, &block)
25
+ end
26
+
27
+ # because tasks are executed by a validator, some methods are relayed across
28
+ # from the task back to the validator. this override checks for that existence
29
+ def respond_to_missing?(method, *args)
30
+ @validator.respond_to?(method, *args)
31
+ end
32
+
33
+ # returns the github client api established either by the task or the validator
34
+ def client_api
35
+ # evaluate if the validator has defined the api and delegate accordingly
36
+ return validator.client_api if validator.respond_to?(:client_api)
37
+
38
+ ::GithubBot::Github::Client.instance
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GithubBot
4
+ module Validator
5
+ # Public: Base class for validators to extend to provide helpful
6
+ # methods for interactions with GitHub
7
+ class Base
8
+ class << self
9
+ # Public: Validation method that every consumer will need to override
10
+ def validate; end
11
+ end
12
+
13
+ # Public: Initializes an instance of the validator
14
+ def initialize
15
+ @feedback = []
16
+ @check = nil
17
+ end
18
+
19
+ # Public: Provide feedback to be generated upon completion of the check run
20
+ # see https://docs.github.com/en/rest/reference/checks#create-a-check-run for additional
21
+ # details on available parameters for the 'annotations' item
22
+ def feedback(path:, start_line: 0, end_line: 0, annotation_level:, message:, **opts)
23
+ @feedback << {
24
+ path: path,
25
+ start_line: start_line,
26
+ end_line: end_line,
27
+ annotation_level: annotation_level,
28
+ message: message,
29
+ **opts
30
+ }
31
+ end
32
+
33
+ # Public: Moves the check run into the in progress status
34
+ def in_progress(**opts)
35
+ raise StandardError, 'check run has not been established' unless @check
36
+
37
+ defaults = {
38
+ output: {
39
+ title: "#{@check.name} validation is in progress...",
40
+ summary: 'We\'re currently validating this request. Please stand by.'
41
+ }
42
+ }
43
+
44
+ @check.in_progress!(defaults.deep_merge(opts))
45
+ end
46
+
47
+ # Public: Block for execution logic that is the be evaluated and finalized
48
+ # within the GitHub check run content
49
+ def check_run(name:, **_opts)
50
+ # queue request for processing
51
+ @check = client_api.create_check_run(
52
+ name: name,
53
+ output: {
54
+ title: "#{name} validation has been queued...",
55
+ summary: "We're awaiting processing."
56
+ }
57
+ )
58
+
59
+ yield if block_given?
60
+
61
+ if @feedback.empty?
62
+ @check.complete!(
63
+ output: {
64
+ title: "#{name} validation is complete...",
65
+ summary: "#{name} validation passed!"
66
+ }
67
+ )
68
+
69
+ else
70
+ # because limitation of 50 checks per API call execution, break up annotations
71
+ # https://developer.github.com/v3/checks/runs/
72
+ @feedback.each_slice(50).to_a.each do |annotations|
73
+ @check.action_required!(
74
+ output: {
75
+ title: "#{name} validation is complete...",
76
+ summary: "#{name} validation determined there are changes that need attention.",
77
+ annotations: annotations
78
+ }
79
+ )
80
+ end
81
+ end
82
+ rescue StandardError => e
83
+ Rails.logger.error message: 'Error occurred during check run', exception: e
84
+ @check.action_required!(
85
+ output: {
86
+ title: "#{name} validation failed...",
87
+ summary: "# Message\n```\n#{e.message}\n```\n# Exception\n```\n#{e.backtrace.join("\n")}\n```"
88
+ }
89
+ )
90
+ end
91
+
92
+ # Public: Return all files from request.
93
+ def files
94
+ client_api.files
95
+ end
96
+
97
+ # Public: Return the modified files from request.
98
+ def modified_files
99
+ client_api.modified_files
100
+ end
101
+
102
+ # Public: Validates the file extension for the provided file
103
+ #
104
+ # @param file [Sawyer::Resource] The file to evaluate
105
+ # @param extension [String] The extension type to evaluate
106
+ def validate_file_extension(file, extension)
107
+ return if File.extname(file.filename) == ".#{extension}"
108
+
109
+ feedback(
110
+ path: file.filename,
111
+ annotation_level: 'failure',
112
+ message: "File suffix is incorrect, please use '.#{extension}'"
113
+ )
114
+ end
115
+
116
+ # Public: Loads the yaml of the file provided
117
+ #
118
+ # @param file [Sawyer::Resource] The file to load
119
+ def load_yaml(file)
120
+ YAML.safe_load(client_api.file_content(file)).with_indifferent_access
121
+ rescue StandardError => e
122
+ feedback(
123
+ path: file.filename,
124
+ annotation_level: 'failure',
125
+ message: 'Malformed yaml content'\
126
+ "exception: #{e.message}"
127
+ )
128
+
129
+ {}
130
+ end
131
+
132
+ # Public: Validates for the provided file at a given hash point that the
133
+ # required and allowed fields are met
134
+ #
135
+ # @param file [Sawyer::Resource] The file for reference that is being evaluated
136
+ # @param ident [String/Symbol] The text identifier for the hash being evaluated
137
+ # @param hash [Hash] The hash to review keys
138
+ # @param required_fields [Array] An array of required elements
139
+ # @param allowed_fields [Array] An array of allowed fields
140
+ def validate_fields(file:, ident:, hash:, required_fields:, allowed_fields:, **opts)
141
+ unless hash
142
+ feedback(
143
+ path: file.filename,
144
+ annotation_level: 'failure',
145
+ message: 'Element is empty or nil'
146
+ )
147
+
148
+ return
149
+ end
150
+
151
+ validate_required_fields(
152
+ file: file,
153
+ ident: ident,
154
+ hash: hash,
155
+ required_fields: required_fields,
156
+ **opts
157
+ )
158
+ validate_allowed_fields(
159
+ file: file,
160
+ ident: ident,
161
+ hash: hash,
162
+ allowed_fields: allowed_fields,
163
+ **opts
164
+ )
165
+ end
166
+
167
+ # Public: Validates for the provided file at a given hash point that the
168
+ # required fields are met
169
+ #
170
+ # @param file [Sawyer::Resource] The file for reference that is being evaluated
171
+ # @param ident [String/Symbol] The text identifier for the hash being evaluated
172
+ # @param hash [Hash] The hash to review keys
173
+ # @param required_fields [Array] An array of required elements
174
+ def validate_required_fields(file:, ident:, hash:, required_fields:, **_opts)
175
+ unless hash
176
+ feedback(
177
+ path: file.filename,
178
+ annotation_level: 'failure',
179
+ message: "#{ident}: Element is empty or nil"
180
+ )
181
+
182
+ return
183
+ end
184
+
185
+ required_fields.each do |key|
186
+ next unless hash[key].nil?
187
+
188
+ feedback(
189
+ path: file.filename,
190
+ annotation_level: 'failure',
191
+ message: "#{ident}: Missing required element '#{key}'"
192
+ )
193
+ end
194
+ end
195
+
196
+ # Public: Validates for the provided file at a given hash point that the
197
+ # allowed fields are met
198
+ #
199
+ # @param file [Sawyer::Resource] The file for reference that is being evaluated
200
+ # @param ident [String/Symbol] The text identifier for the hash being evaluated
201
+ # @param hash [Hash] The hash to review keys
202
+ # @param allowed_fields [Array] An array of allowed fields
203
+ def validate_allowed_fields(file:, ident:, hash:, allowed_fields:, **_opts)
204
+ unless hash
205
+ feedback(
206
+ path: file.filename,
207
+ annotation_level: 'failure',
208
+ message: "#{ident}: Element is empty or nil"
209
+ )
210
+
211
+ return
212
+ end
213
+
214
+ hash.keys&.each do |key|
215
+ unless allowed_fields.include?(key)
216
+ feedback(
217
+ path: file.filename,
218
+ annotation_level: 'failure',
219
+ message: "#{ident}: Key '#{key}' not in the allowed fields [#{allowed_fields.join(', ')}]"
220
+ )
221
+ end
222
+ end
223
+ end
224
+
225
+ private
226
+
227
+ def client_api
228
+ ::GithubBot::Github::Client.instance
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GithubBot
4
+ VERSION = '0.1.0'
5
+
6
+ # version module
7
+ module Version
8
+ MAJOR, MINOR, PATCH, *BUILD = VERSION.split '.'
9
+ NUMBERS = [MAJOR, MINOR, PATCH, *BUILD].freeze
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github_bot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Greg Howdeshell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-06-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: git
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jwt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: octokit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.18'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.18'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 5.0.0.1
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: 7.0.0
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 5.0.0.1
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: 7.0.0
75
+ description: 'A rubygem designed to assist in the creation of GitHub bot applications.
76
+
77
+ '
78
+ email:
79
+ - greg.howdeshell@gmail.com
80
+ executables:
81
+ - bundle-audit
82
+ - bundler-audit
83
+ - rails
84
+ - rake
85
+ - rspec
86
+ - rubocop
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
91
+ - ".github/ISSUE_TEMPLATE/config.yml"
92
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
93
+ - ".github/pull_request_template.md"
94
+ - ".github/workflows/cd.yml"
95
+ - ".github/workflows/ci.yml"
96
+ - ".gitignore"
97
+ - ".rspec"
98
+ - ".rubocop.yml"
99
+ - ".ruby-gemset"
100
+ - ".ruby-version"
101
+ - CHANGELOG.md
102
+ - CODE_OF_CONDUCT.md
103
+ - CONTRIBUTING.md
104
+ - CONTRIBUTORS.md
105
+ - Gemfile
106
+ - LICENSE
107
+ - NOTICE
108
+ - README.md
109
+ - Rakefile
110
+ - app/controllers/github_bot/application_controller.rb
111
+ - app/controllers/github_bot/concerns/response.rb
112
+ - app/controllers/github_bot/webhooks/github_controller.rb
113
+ - app/helpers/github_bot/github_request_helper.rb
114
+ - bin/bundle-audit
115
+ - bin/bundler-audit
116
+ - bin/rails
117
+ - bin/rake
118
+ - bin/rspec
119
+ - bin/rubocop
120
+ - config/routes.rb
121
+ - github_bot.gemspec
122
+ - lib/github_bot.rb
123
+ - lib/github_bot/engine.rb
124
+ - lib/github_bot/github/check_run.rb
125
+ - lib/github_bot/github/client.rb
126
+ - lib/github_bot/github/payload.rb
127
+ - lib/github_bot/rails/railtie.rb
128
+ - lib/github_bot/tasks/base.rb
129
+ - lib/github_bot/validator/base.rb
130
+ - lib/github_bot/version.rb
131
+ homepage: https://github.com/cerner/github_bot-ruby
132
+ licenses:
133
+ - Apache-2.0
134
+ metadata: {}
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 2.6.2
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubygems_version: 3.0.3.1
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: A rubygem designed to assist in the creation of GitHub bot applications.
154
+ test_files: []