reinarb 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 +7 -0
- data/bin/reina +53 -0
- data/lib/reina.rb +14 -0
- data/lib/reina/app.rb +190 -0
- data/lib/reina/config.rb +22 -0
- data/lib/reina/controller.rb +132 -0
- data/lib/reina/github_controller.rb +176 -0
- data/lib/reina/server.rb +27 -0
- data/lib/reina/version.rb +3 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a415a1d33bc56baa8ccd2e7d3ae8fd3fe6f426baa36031a7cfcd692035835970
|
4
|
+
data.tar.gz: 8e87077ee747bef1a456aa7cf6f1574dbf6db9f52303837b201a63809cf5a5d7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ac3d85dc8e66d9c16ecf7ad14155786c355a044ab02803dcb5fe4622588f2abd2927dba92975b72533194077d293a27e712a02862e34f152aee730259cea5ddd
|
7
|
+
data.tar.gz: 6d67ffd0e61f719250ad0f28ebef6dfcbbd9724034fa160181202733c3c1216db1355ee6e5d5b50f35e394f32b473f17499d8f871e0a67a74ddca069ca4f1077
|
data/bin/reina
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'reina'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
options = {
|
6
|
+
force: false,
|
7
|
+
strict: false
|
8
|
+
}
|
9
|
+
|
10
|
+
OptionParser.new do |opts|
|
11
|
+
opts.banner = "Usage: reina [options]"
|
12
|
+
|
13
|
+
opts.on('-f', '--force', 'Delete the existing apps without asking for deletion') do
|
14
|
+
options[:force] = true
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on('-s', '--strict', 'Enable the strict mode in which only the apps given in the CLI are considered rather than the whole suite') do
|
18
|
+
options[:strict] = true
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on('-v', '--version', 'Print the version') do
|
22
|
+
puts Reina::VERSION
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
end.parse!
|
26
|
+
|
27
|
+
reina = Reina::Controller.new(ARGV.dup, options[:strict])
|
28
|
+
|
29
|
+
if reina.existing_apps.present?
|
30
|
+
puts 'The following apps already exist on Heroku:'
|
31
|
+
puts reina.existing_apps.map { |a| "- #{a}" }
|
32
|
+
|
33
|
+
unless options[:force]
|
34
|
+
require 'readline'
|
35
|
+
abort if Readline.readline('Type "OK" to delete the apps above: ', true).strip != 'OK'
|
36
|
+
end
|
37
|
+
|
38
|
+
reina.delete_existing_apps!
|
39
|
+
end
|
40
|
+
|
41
|
+
apps_count = reina.apps.size
|
42
|
+
|
43
|
+
if apps_count > 1
|
44
|
+
puts "Starting to deploy #{apps_count} apps..."
|
45
|
+
else
|
46
|
+
puts "Starting to deploy one app..."
|
47
|
+
end
|
48
|
+
|
49
|
+
reina.deploy_parallel_apps!
|
50
|
+
reina.deploy_non_parallel_apps!
|
51
|
+
|
52
|
+
s = 's'.freeze if apps_count > 1
|
53
|
+
puts "Deployment#{s} finished. Live at #{url}."
|
data/lib/reina.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'excon'
|
2
|
+
require 'platform-api'
|
3
|
+
require 'git'
|
4
|
+
require 'active_support/all'
|
5
|
+
require 'parallel'
|
6
|
+
require 'sinatra/base'
|
7
|
+
require 'octokit'
|
8
|
+
|
9
|
+
require 'reina/config'
|
10
|
+
require 'reina/app'
|
11
|
+
require 'reina/controller'
|
12
|
+
require 'reina/github_controller'
|
13
|
+
require 'reina/server'
|
14
|
+
require 'reina/version'
|
data/lib/reina/app.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
module Reina
|
2
|
+
class App
|
3
|
+
DEFAULT_REGION = 'eu'.freeze
|
4
|
+
DEFAULT_STAGE = 'staging'.freeze
|
5
|
+
DEFAULT_APP_NAME_PREFIX = CONFIG[:app_name_prefix].freeze
|
6
|
+
|
7
|
+
attr_reader :heroku, :name, :project, :issue_number, :branch, :g
|
8
|
+
|
9
|
+
def initialize(heroku, name, project, issue_number, branch)
|
10
|
+
@heroku = heroku
|
11
|
+
@name = name.to_s
|
12
|
+
@project = project
|
13
|
+
@issue_number = issue_number
|
14
|
+
@branch = branch
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch_repository
|
18
|
+
if Dir.exists?(name)
|
19
|
+
@g = Git.open(name)
|
20
|
+
else
|
21
|
+
@g = Git.clone(github_url, name)
|
22
|
+
end
|
23
|
+
|
24
|
+
g.pull('origin', branch)
|
25
|
+
g.checkout(g.branch(branch))
|
26
|
+
|
27
|
+
unless g.remotes.map(&:name).include?(remote_name)
|
28
|
+
g.add_remote(remote_name, remote_url)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_app
|
33
|
+
heroku.app.create(
|
34
|
+
'name' => app_name,
|
35
|
+
'region' => project.fetch(:region, DEFAULT_REGION)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def install_addons
|
40
|
+
addons = project.fetch(:addons, []) + app_json.fetch('addons', [])
|
41
|
+
addons.each do |addon|
|
42
|
+
if addon.is_a?(Hash) && addon.has_key?('options')
|
43
|
+
addon['config'] = addon.extract!('options')
|
44
|
+
else
|
45
|
+
addon = { 'plan' => addon }
|
46
|
+
end
|
47
|
+
|
48
|
+
heroku.addon.create(app_name, addon)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_buildpacks
|
53
|
+
buildpacks = project.fetch(:buildpacks, []).map do |buildpack|
|
54
|
+
{ 'buildpack' => buildpack }
|
55
|
+
end + app_json.fetch('buildpacks', []).map do |buildpack|
|
56
|
+
{ 'buildpack' => buildpack['url'] }
|
57
|
+
end
|
58
|
+
|
59
|
+
heroku.buildpack_installation.update(app_name, 'updates' => buildpacks.uniq)
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_env_vars
|
63
|
+
config_vars = project.fetch(:config_vars, {})
|
64
|
+
except = config_vars[:except]
|
65
|
+
|
66
|
+
if config_vars.has_key?(:from)
|
67
|
+
copy = config_vars.fetch(:copy, [])
|
68
|
+
config_vars = heroku.config_var.info_for_app(config_vars[:from])
|
69
|
+
|
70
|
+
vars_cache = {}
|
71
|
+
copy.each do |h|
|
72
|
+
unless h[:from].include?('#')
|
73
|
+
s = config_vars[h[:from]]
|
74
|
+
s << h[:append] if h[:append].present?
|
75
|
+
config_vars[h[:to]] = s
|
76
|
+
next
|
77
|
+
end
|
78
|
+
|
79
|
+
source, var = h[:from].split('#')
|
80
|
+
source_app_name = app_name_for(source)
|
81
|
+
|
82
|
+
if var == 'url'.freeze
|
83
|
+
config_vars[h[:to]] = "https://#{domain_name_for(source_app_name)}"
|
84
|
+
else
|
85
|
+
vars_cache[source_app_name] ||= heroku.config_var.info_for_app(source_app_name)
|
86
|
+
config_vars[h[:to]] = vars_cache[source_app_name][var]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
config_vars.except!(*except) if except.present?
|
92
|
+
|
93
|
+
config_vars['APP_NAME'] = app_name
|
94
|
+
config_vars['HEROKU_APP_NAME'] = app_name
|
95
|
+
config_vars['DOMAIN_NAME'] = domain_name
|
96
|
+
config_vars['COOKIE_DOMAIN'] = '.herokuapp.com'.freeze
|
97
|
+
|
98
|
+
app_json.fetch('env', {}).each do |key, hash|
|
99
|
+
next if hash['value'].blank? || config_vars[key].present?
|
100
|
+
config_vars[key] = hash['value']
|
101
|
+
end
|
102
|
+
|
103
|
+
heroku.config_var.update(app_name, config_vars)
|
104
|
+
end
|
105
|
+
|
106
|
+
def setup_dynos
|
107
|
+
formation = app_json.fetch('formation', {})
|
108
|
+
return if formation.blank?
|
109
|
+
|
110
|
+
formation.each do |k, h|
|
111
|
+
h['size'] = 'free'
|
112
|
+
|
113
|
+
heroku.formation.update(app_name, k, h)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def add_to_pipeline
|
118
|
+
return if project[:pipeline].blank?
|
119
|
+
|
120
|
+
pipeline_id = heroku.pipeline.info(project[:pipeline])['id']
|
121
|
+
heroku.pipeline_coupling.create(
|
122
|
+
'app' => app_name,
|
123
|
+
'pipeline' => pipeline_id,
|
124
|
+
'stage' => DEFAULT_STAGE
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
def execute_postdeploy_scripts
|
129
|
+
script = app_json.dig('scripts', 'postdeploy')
|
130
|
+
return if script.blank?
|
131
|
+
|
132
|
+
return if heroku? && ENV['HEROKU_API_KEY'].blank?
|
133
|
+
|
134
|
+
`heroku run #{script} --app #{app_name}`
|
135
|
+
end
|
136
|
+
|
137
|
+
def deploy
|
138
|
+
g.push(remote_name, 'master')
|
139
|
+
end
|
140
|
+
|
141
|
+
def app_json
|
142
|
+
return @app_json if @app_json
|
143
|
+
|
144
|
+
f = File.join(name, 'app.json')
|
145
|
+
return {} unless File.exists?(f)
|
146
|
+
|
147
|
+
@app_json = JSON.parse(File.read(f))
|
148
|
+
end
|
149
|
+
|
150
|
+
def domain_name
|
151
|
+
domain_name_for(app_name)
|
152
|
+
end
|
153
|
+
|
154
|
+
def app_name
|
155
|
+
app_name_for(name)
|
156
|
+
end
|
157
|
+
|
158
|
+
def remote_name
|
159
|
+
"heroku-#{app_name}"
|
160
|
+
end
|
161
|
+
|
162
|
+
def parallel?
|
163
|
+
project[:parallel] != false
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def domain_name_for(s)
|
169
|
+
"#{s}.herokuapp.com"
|
170
|
+
end
|
171
|
+
|
172
|
+
def app_name_for(s)
|
173
|
+
"#{DEFAULT_APP_NAME_PREFIX}#{s}-#{issue_number}"
|
174
|
+
end
|
175
|
+
|
176
|
+
def github_url
|
177
|
+
return "https://github.com/#{project[:github]}" if ENV['GITHUB_AUTH'].blank?
|
178
|
+
|
179
|
+
"https://#{ENV['GITHUB_AUTH']}@github.com/#{project[:github]}"
|
180
|
+
end
|
181
|
+
|
182
|
+
def remote_url
|
183
|
+
"https://git.heroku.com/#{app_name}.git"
|
184
|
+
end
|
185
|
+
|
186
|
+
def heroku?
|
187
|
+
ENV['DYNO'].present?
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
data/lib/reina/config.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Reina
|
2
|
+
config_file = File.join(Dir.pwd, 'config.rb')
|
3
|
+
|
4
|
+
if File.exists?(config_file)
|
5
|
+
if ENV['CONFIG'].blank? || ENV['APPS'].blank?
|
6
|
+
require config_file
|
7
|
+
Reina::CONFIG = CONFIG
|
8
|
+
Reina::APPS = APPS
|
9
|
+
else
|
10
|
+
self.class.send(:remove_const, 'CONFIG')
|
11
|
+
self.class.send(:remove_const, 'APPS')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
CONFIG = ActiveSupport::HashWithIndifferentAccess.new(
|
16
|
+
JSON.parse(ENV['CONFIG'])
|
17
|
+
) if ENV['CONFIG'].present?
|
18
|
+
|
19
|
+
APPS = ActiveSupport::HashWithIndifferentAccess.new(
|
20
|
+
JSON.parse(ENV['APPS'])
|
21
|
+
) if ENV['APPS'].present?
|
22
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Reina
|
2
|
+
class Controller
|
3
|
+
APP_COOLDOWN = 7 # seconds
|
4
|
+
|
5
|
+
def initialize(params, strict = false)
|
6
|
+
@params = params
|
7
|
+
@strict = strict
|
8
|
+
|
9
|
+
abort 'Please provide $PLATFORM_API' if CONFIG[:platform_api].blank?
|
10
|
+
abort 'Given PR number should be greater than 0' if issue_number <= 0
|
11
|
+
|
12
|
+
oversize = apps.select { |app| app.app_name.length >= 30 }.first
|
13
|
+
abort "#{oversize.app_name} is too long" if oversize.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_netrc
|
17
|
+
return if ENV['GITHUB_AUTH'].blank?
|
18
|
+
|
19
|
+
`git config --global user.name "#{ENV['GITHUB_NAME']}"`
|
20
|
+
`git config --global user.email "#{ENV['GITHUB_EMAIL']}"`
|
21
|
+
|
22
|
+
return if File.exists?('.netrc')
|
23
|
+
|
24
|
+
File.write(
|
25
|
+
'.netrc',
|
26
|
+
"machine git.heroku.com login #{ENV['GITHUB_EMAIL']} password #{ENV['HEROKU_API_KEY']}"
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def deploy_parallel_apps!
|
31
|
+
Parallel.each(apps.select(&:parallel?)) do |app|
|
32
|
+
begin
|
33
|
+
deploy!(app)
|
34
|
+
rescue Git::GitExecuteError => e
|
35
|
+
puts "#{app.name}: #{e.message}"
|
36
|
+
rescue Exception => e
|
37
|
+
msg = e.respond_to?(:response) ? e.response.body : e.message
|
38
|
+
puts "#{app.name}: #{msg}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def deploy_non_parallel_apps!
|
44
|
+
apps.reject(&:parallel?).each do |app|
|
45
|
+
begin
|
46
|
+
deploy!(app)
|
47
|
+
rescue Git::GitExecuteError => e
|
48
|
+
puts "#{app.name}: #{e.message}"
|
49
|
+
rescue Exception => e
|
50
|
+
msg = e.respond_to?(:response) ? e.response.body : e.message
|
51
|
+
puts "#{app.name}: #{msg}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete_existing_apps!
|
57
|
+
existing_apps.each do |app|
|
58
|
+
puts "Deleting #{app}"
|
59
|
+
heroku.app.delete(app)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def existing_apps
|
64
|
+
# apps in common between heroku's list and ours
|
65
|
+
@_existing_apps ||= heroku.app.list.map { |a| a['name'] } & apps.map(&:app_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def heroku?
|
69
|
+
ENV['DYNO'].present?
|
70
|
+
end
|
71
|
+
|
72
|
+
def apps
|
73
|
+
return @_apps if @_apps.present?
|
74
|
+
|
75
|
+
# strict is when we only take in consideration the apps
|
76
|
+
# that are in both `params` and `APPS`
|
77
|
+
_apps = if strict
|
78
|
+
app_names = branches.keys
|
79
|
+
APPS.select { |name, _| app_names.include?(name) }
|
80
|
+
else
|
81
|
+
APPS
|
82
|
+
end
|
83
|
+
|
84
|
+
@_apps = _apps.map do |name, project|
|
85
|
+
branch = branches[name.to_s].presence || 'master'.freeze
|
86
|
+
App.new(heroku, name, project, issue_number, branch)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
attr_reader :params, :strict
|
93
|
+
|
94
|
+
def heroku
|
95
|
+
@_heroku ||= PlatformAPI.connect_oauth(CONFIG[:platform_api])
|
96
|
+
end
|
97
|
+
|
98
|
+
def issue_number
|
99
|
+
@_issue_number ||= params.shift.to_i
|
100
|
+
end
|
101
|
+
|
102
|
+
def branches
|
103
|
+
@_branches ||= params.map { |param| param.split('#', 2) }.to_h
|
104
|
+
end
|
105
|
+
|
106
|
+
def deploy!(app)
|
107
|
+
puts "#{app.name}: Fetching from #{app.project[:github]}..."
|
108
|
+
app.fetch_repository
|
109
|
+
|
110
|
+
puts "#{app.name}: Provisioning #{app.app_name} on Heroku..."
|
111
|
+
app.create_app
|
112
|
+
app.install_addons
|
113
|
+
app.add_buildpacks
|
114
|
+
app.set_env_vars
|
115
|
+
|
116
|
+
puts "#{app.name}: Deploying to https://#{app.domain_name}..."
|
117
|
+
app.deploy
|
118
|
+
|
119
|
+
puts "#{app.name}: Cooldown..."
|
120
|
+
Kernel.sleep APP_COOLDOWN
|
121
|
+
|
122
|
+
puts "#{app.name}: Executing postdeploy scripts..."
|
123
|
+
app.execute_postdeploy_scripts
|
124
|
+
|
125
|
+
puts "#{app.name}: Setting up dynos..."
|
126
|
+
app.setup_dynos
|
127
|
+
|
128
|
+
puts "#{app.name}: Adding to pipeline..."
|
129
|
+
app.add_to_pipeline
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module Reina
|
2
|
+
class SignatureError < StandardError
|
3
|
+
def message
|
4
|
+
'Signatures do not match'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class UnsupportedEventError < StandardError; end
|
9
|
+
|
10
|
+
class GitHubController
|
11
|
+
DEPLOY_TRIGGER = 'reina: d '.freeze
|
12
|
+
SINGLE_DEPLOY_TRIGGER = 'reina: r '.freeze
|
13
|
+
EVENTS = %w(issues issue_comment).freeze
|
14
|
+
|
15
|
+
def initialize(config)
|
16
|
+
@config = config
|
17
|
+
end
|
18
|
+
|
19
|
+
def dispatch(request)
|
20
|
+
@request = request
|
21
|
+
|
22
|
+
raise UnsupportedEventError unless EVENTS.include?(event)
|
23
|
+
|
24
|
+
authenticate!
|
25
|
+
|
26
|
+
if deploy_requested?
|
27
|
+
deploy!
|
28
|
+
elsif single_deploy_requested?
|
29
|
+
deploy!(true)
|
30
|
+
elsif issue_closed?
|
31
|
+
destroy!
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def deployed_url
|
36
|
+
[
|
37
|
+
'https://', CONFIG[:app_name_prefix], repo_name, '-', issue_number, '.herokuapp.com'
|
38
|
+
].join
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :config, :request
|
44
|
+
|
45
|
+
def authenticate!
|
46
|
+
hash = OpenSSL::HMAC.hexdigest(hmac_digest, config[:webhook_secret], raw_payload)
|
47
|
+
hash.prepend('sha1=')
|
48
|
+
raise SignatureError unless Rack::Utils.secure_compare(hash, signature)
|
49
|
+
end
|
50
|
+
|
51
|
+
def deploy_requested?
|
52
|
+
action == 'created'.freeze && comment_body.start_with?(DEPLOY_TRIGGER)
|
53
|
+
end
|
54
|
+
|
55
|
+
def single_deploy_requested?
|
56
|
+
action == 'created'.freeze && comment_body.start_with?(SINGLE_DEPLOY_TRIGGER)
|
57
|
+
end
|
58
|
+
|
59
|
+
def issue_closed?
|
60
|
+
action == 'closed'.freeze
|
61
|
+
end
|
62
|
+
|
63
|
+
def deploy!(strict = false)
|
64
|
+
reina = Controller.new(params, strict)
|
65
|
+
should_comment = config[:oauth_token].present?
|
66
|
+
reply = ->(msg) { octokit.add_comment(repo_full_name, issue_number, msg) }
|
67
|
+
url = deployed_url
|
68
|
+
|
69
|
+
fork do
|
70
|
+
apps_count = reina.apps.size
|
71
|
+
|
72
|
+
if should_comment
|
73
|
+
if apps_count > 1
|
74
|
+
reply.call("Starting to deploy #{apps_count} apps...")
|
75
|
+
else
|
76
|
+
reply.call("Starting to deploy one app...")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
reina.create_netrc if reina.heroku?
|
81
|
+
reina.delete_existing_apps!
|
82
|
+
reina.deploy_parallel_apps!
|
83
|
+
reina.deploy_non_parallel_apps!
|
84
|
+
|
85
|
+
s = 's'.freeze if apps_count > 1
|
86
|
+
reply.call("Deployment#{s} finished. Live at #{url}.") if should_comment
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def destroy!
|
91
|
+
reina = Controller.new(params)
|
92
|
+
return if reina.existing_apps.empty?
|
93
|
+
|
94
|
+
should_comment = config[:oauth_token].present?
|
95
|
+
reply = ->(msg) { octokit.add_comment(repo_full_name, issue_number, msg) }
|
96
|
+
|
97
|
+
fork do
|
98
|
+
reina.create_netrc if reina.heroku?
|
99
|
+
reina.delete_existing_apps!
|
100
|
+
|
101
|
+
reply.call('All the staging apps related to this issue have been deleted.') if should_comment
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def octokit
|
106
|
+
return @_octokit if @_octokit.present?
|
107
|
+
|
108
|
+
client = Octokit::Client.new(access_token: config[:oauth_token])
|
109
|
+
user = client.user
|
110
|
+
user.login
|
111
|
+
@_octokit = client
|
112
|
+
end
|
113
|
+
|
114
|
+
def params
|
115
|
+
return [issue_number] if comment_body.blank?
|
116
|
+
|
117
|
+
[
|
118
|
+
issue_number,
|
119
|
+
comment_body
|
120
|
+
.lines[0]
|
121
|
+
.split(/#{DEPLOY_TRIGGER}|#{SINGLE_DEPLOY_TRIGGER}/)[1]
|
122
|
+
.split(' ')
|
123
|
+
.reject(&:blank?)
|
124
|
+
].flatten
|
125
|
+
end
|
126
|
+
|
127
|
+
def signature
|
128
|
+
request.env['HTTP_X_HUB_SIGNATURE']
|
129
|
+
end
|
130
|
+
|
131
|
+
def event
|
132
|
+
request.env['HTTP_X_GITHUB_EVENT']
|
133
|
+
end
|
134
|
+
|
135
|
+
def action
|
136
|
+
payload['action']
|
137
|
+
end
|
138
|
+
|
139
|
+
def issue_number
|
140
|
+
payload.dig('issue', 'number')
|
141
|
+
end
|
142
|
+
|
143
|
+
def repo_name
|
144
|
+
payload.dig('repository', 'name')
|
145
|
+
end
|
146
|
+
|
147
|
+
def repo_full_name
|
148
|
+
payload.dig('repository', 'full_name')
|
149
|
+
end
|
150
|
+
|
151
|
+
def comment_body
|
152
|
+
payload.dig('comment', 'body')&.strip.to_s
|
153
|
+
end
|
154
|
+
|
155
|
+
def comment_author
|
156
|
+
payload.dig('comment', 'user', 'login')
|
157
|
+
end
|
158
|
+
|
159
|
+
def payload
|
160
|
+
return @_payload if @_payload.present?
|
161
|
+
|
162
|
+
@_payload ||= JSON.parse(raw_payload)
|
163
|
+
end
|
164
|
+
|
165
|
+
def raw_payload
|
166
|
+
return @_raw_payload if @_raw_payload.present?
|
167
|
+
|
168
|
+
request.body.rewind
|
169
|
+
@_raw_payload ||= request.body.read
|
170
|
+
end
|
171
|
+
|
172
|
+
def hmac_digest
|
173
|
+
@_hmac_digest ||= OpenSSL::Digest.new('sha1')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/lib/reina/server.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Reina
|
2
|
+
class Server < Sinatra::Base
|
3
|
+
set :show_exceptions, false
|
4
|
+
|
5
|
+
error SignatureError do
|
6
|
+
halt 403, env['sinatra.error'].message
|
7
|
+
end
|
8
|
+
|
9
|
+
error UnsupportedEventError do
|
10
|
+
halt 403, env['sinatra.error'].message
|
11
|
+
end
|
12
|
+
|
13
|
+
error Exception do
|
14
|
+
halt 500, 'Something bad happened... probably'
|
15
|
+
end
|
16
|
+
|
17
|
+
get '/' do
|
18
|
+
'<img src="https://i.imgur.com/UDxbOsz.png?1">'
|
19
|
+
end
|
20
|
+
|
21
|
+
post '/github' do
|
22
|
+
GitHubController.new(CONFIG[:github]).dispatch(request)
|
23
|
+
status 202
|
24
|
+
body ''
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reinarb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Giovanni Capuano
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-30 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Either used as GitHub bot or a CLI tool, reina performs setup and deployment
|
14
|
+
of your applications on Heroku.
|
15
|
+
email: webmaster@giovannicapuano.net
|
16
|
+
executables:
|
17
|
+
- reina
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/reina
|
22
|
+
- lib/reina.rb
|
23
|
+
- lib/reina/app.rb
|
24
|
+
- lib/reina/config.rb
|
25
|
+
- lib/reina/controller.rb
|
26
|
+
- lib/reina/github_controller.rb
|
27
|
+
- lib/reina/server.rb
|
28
|
+
- lib/reina/version.rb
|
29
|
+
homepage: http://github.com/honeypotio/reina
|
30
|
+
licenses:
|
31
|
+
- BSD-2-Clause
|
32
|
+
metadata: {}
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 2.7.6
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: Bot to handle deploys and orchestrations of feature stagings hosted on Heroku.
|
53
|
+
test_files: []
|