github_bot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []