blend 0.1.4
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/Rakefile +38 -0
- data/bin/blend +34 -0
- data/lib/blend.rb +28 -0
- data/lib/blend/chatbot/bot.rb +352 -0
- data/lib/blend/cli.rb +38 -0
- data/lib/blend/cli/github.rb +140 -0
- data/lib/blend/cli/heroku.rb +232 -0
- data/lib/blend/cli/hipchat.rb +61 -0
- data/lib/blend/cli/juice.rb +493 -0
- data/lib/blend/client.rb +63 -0
- data/lib/blend/client/github_client.rb +106 -0
- data/lib/blend/client/heroku_client.rb +87 -0
- data/lib/blend/client/hipchat_client.rb +78 -0
- data/lib/blend/client/juice_client.rb +400 -0
- data/lib/blend/core_ext/fixnum.rb +5 -0
- data/lib/blend/core_ext/object.rb +13 -0
- data/lib/blend/core_ext/string.rb +9 -0
- data/lib/blend/exceptions.rb +16 -0
- data/lib/blend/status/domain.rb +225 -0
- data/lib/blend/status/environment.rb +167 -0
- data/lib/blend/status/project.rb +227 -0
- data/lib/blend/status/project_resolver.rb +183 -0
- data/lib/blend/status/repo.rb +51 -0
- data/lib/blend/status/team.rb +29 -0
- data/lib/blend/version.rb +10 -0
- metadata +225 -0
data/lib/blend/cli.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'blend/cli/hipchat'
|
2
|
+
require 'blend/cli/github'
|
3
|
+
require 'blend/cli/juice'
|
4
|
+
require 'blend/cli/heroku'
|
5
|
+
|
6
|
+
module Blend
|
7
|
+
module CLI
|
8
|
+
class CLI < Thor
|
9
|
+
|
10
|
+
desc "logout", "Clear juice credentials"
|
11
|
+
def logout
|
12
|
+
Blend::CLI::Juice.new.logout
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "login", "Acquire juice credentials"
|
16
|
+
def login
|
17
|
+
Blend::CLI::Juice.new.login
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'projects', 'List of Juice projects'
|
21
|
+
def projects
|
22
|
+
Blend::CLI::Juice.new.projects
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "hipchat COMMANDS", "Hipchat Control Module"
|
26
|
+
subcommand "hipchat", Blend::CLI::Hipchat
|
27
|
+
|
28
|
+
desc "github COMMANDS", "Github Control Module"
|
29
|
+
subcommand "github", Blend::CLI::Github
|
30
|
+
|
31
|
+
desc "juice COMMANDS", "Juice Control Module"
|
32
|
+
subcommand "juice", Blend::CLI::Juice
|
33
|
+
|
34
|
+
desc "heroku COMMANDS", "Heroku Control Module"
|
35
|
+
subcommand "heroku", Blend::CLI::Heroku
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Blend
|
2
|
+
module CLI
|
3
|
+
class Github < Thor
|
4
|
+
desc "hooks REPO", "Lists out service hooks for a given repo"
|
5
|
+
def hooks( repo )
|
6
|
+
puts
|
7
|
+
printf "%-20s %-60s\n".blue.underline, 'Name', 'Config'
|
8
|
+
client.list_hooks( repo ).each do |hook|
|
9
|
+
printf "%-20s", hook.name
|
10
|
+
cfg = hook.config.collect{|k,v| [k,v]}
|
11
|
+
if hook.config.count > 0
|
12
|
+
cfg.each_with_index do |(k,v),i|
|
13
|
+
if i>0
|
14
|
+
printf "%-20s %-20s: %-20s\n", '', k, v.inspect
|
15
|
+
else
|
16
|
+
printf " %-20s: %-20s\n", k, v.inspect
|
17
|
+
end
|
18
|
+
end
|
19
|
+
else
|
20
|
+
puts "\n"
|
21
|
+
end
|
22
|
+
puts
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "add_hipchat REPO ROOM", "Add hipchat hook to repo"
|
27
|
+
def add_hipchat( repo, room )
|
28
|
+
client.add_hook( repo, "hipchat", {
|
29
|
+
auth_token: Blend::Client::get_token( :hipchat ),
|
30
|
+
notify: "1",
|
31
|
+
restrict_to_branch: "",
|
32
|
+
room: room } )
|
33
|
+
Blend::Client::hipchat_client.post_message room, "#{repo} commit hook now added"
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "repos", "Lists out the repos"
|
37
|
+
def repos
|
38
|
+
puts
|
39
|
+
printf "%-50s %-40s\n".blue.underline, 'Name', 'Description'
|
40
|
+
client.list_repos.each do |repo|
|
41
|
+
printf "%-50s %-40s\n", repo['full_name'], repo['description']
|
42
|
+
end
|
43
|
+
puts
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "repo_search NAME", "Lists out matching repos"
|
47
|
+
def repo_search( name )
|
48
|
+
puts
|
49
|
+
printf "%-50s %-40s\n".blue.underline, 'Name', 'Description'
|
50
|
+
client.list_repos(name).each do |repo|
|
51
|
+
printf "%-50s %-40s\n", repo['full_name'], repo['description']
|
52
|
+
end
|
53
|
+
puts
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "repo_create TEAM, NAME", "Create team repo"
|
57
|
+
def repo_create( team, name )
|
58
|
+
client.repo_create( team, name )
|
59
|
+
team( team )
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
desc "collaborators REPO", "List out collaborators"
|
64
|
+
def collaborators( repo )
|
65
|
+
client.list_collaborators( repo ).each do |collaborator|
|
66
|
+
puts collaborator['login']
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "teams", "List out organizations team"
|
71
|
+
def teams
|
72
|
+
client.list_teams.each do |team|
|
73
|
+
puts team.name
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "team_repos TEAM", "List out a teams repos"
|
78
|
+
def team_repos( team )
|
79
|
+
puts
|
80
|
+
printf "%-40s %-40s\n".blue.underline, 'Name', 'Description'
|
81
|
+
client.list_team_repos( team ).each do |repo|
|
82
|
+
printf "%-40s %-40s\n", repo['full_name'], repo['description']
|
83
|
+
end
|
84
|
+
puts
|
85
|
+
end
|
86
|
+
|
87
|
+
desc "team TEAM", "List out the team"
|
88
|
+
def team( team )
|
89
|
+
puts
|
90
|
+
printf "%-40s %-40s\n".blue.underline, 'Repo name', 'Description'
|
91
|
+
client.list_team_repos( team ).each do |repo|
|
92
|
+
printf "%-40s %s\n", repo['full_name'], repo['description']
|
93
|
+
end
|
94
|
+
puts
|
95
|
+
printf "%-30s\n".blue.underline, 'Github users'
|
96
|
+
client.list_team_members( team ).each do |user|
|
97
|
+
printf "%s\n", user['login']
|
98
|
+
end
|
99
|
+
puts
|
100
|
+
end
|
101
|
+
|
102
|
+
desc "team_create TEAM", "Create a team"
|
103
|
+
def team_create( team )
|
104
|
+
client.create_team( team )
|
105
|
+
team( team )
|
106
|
+
end
|
107
|
+
|
108
|
+
desc "team_add TEAM, USER", "Add a user to a team"
|
109
|
+
def team_add( team, user )
|
110
|
+
client.add_team_member( team, user )
|
111
|
+
team( team )
|
112
|
+
end
|
113
|
+
|
114
|
+
desc "team_rm TEAM, USER", "Remove a user from a team"
|
115
|
+
def team_rm( team, user)
|
116
|
+
client.remove_team_member( team, user )
|
117
|
+
team( team )
|
118
|
+
end
|
119
|
+
|
120
|
+
desc "team_repo_add TEAM, REPO", "Add a repo to a team"
|
121
|
+
def team_repo_add( team, repo )
|
122
|
+
client.add_team_repo( team, repo )
|
123
|
+
team( team )
|
124
|
+
end
|
125
|
+
|
126
|
+
desc "team_repo_rm TEAM, REPO", "Remove a repo from a team"
|
127
|
+
def team_repo_rm( team, repo )
|
128
|
+
client.remove_team_repo( team, repo )
|
129
|
+
team( team )
|
130
|
+
end
|
131
|
+
|
132
|
+
no_commands do
|
133
|
+
def client
|
134
|
+
@client ||= Blend::Client.github_client
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module Blend
|
2
|
+
module CLI
|
3
|
+
class Heroku < Thor
|
4
|
+
|
5
|
+
desc "info [APP]", "Prints info for a given app"
|
6
|
+
def info( app )
|
7
|
+
puts
|
8
|
+
_info = client.info(app)
|
9
|
+
_info.reject{|x| %w(domain_name).include? x}.each do |k,v|
|
10
|
+
printf "%35s: %-25s\n", k, v
|
11
|
+
end
|
12
|
+
puts
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'domains [APP]', 'Print domain info'
|
16
|
+
def domains(app)
|
17
|
+
d = client.domains(app)
|
18
|
+
d.each do |params|
|
19
|
+
puts "\n#{params['domain']}:".blue
|
20
|
+
params.each do |k,v|
|
21
|
+
printf "%15s: %-25s\n", k, v
|
22
|
+
end
|
23
|
+
end
|
24
|
+
puts
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'check_domains [APP]', 'Check domains'
|
28
|
+
def check_domains(app)
|
29
|
+
client.nonheroku_domains(app).each do |domain|
|
30
|
+
puts "\nChecking domain #{domain}:".blue
|
31
|
+
result = DomainChecker.check(domain)
|
32
|
+
|
33
|
+
if result.include? :error
|
34
|
+
puts result[:error].red
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
38
|
+
# Check registration
|
39
|
+
if result[:registered]
|
40
|
+
r = result[:registrar]
|
41
|
+
|
42
|
+
# Registrar:
|
43
|
+
if r.nil?
|
44
|
+
printf "%25s: %s", 'Registrar', format_result(:warn, "DNS not configured correctly for #{domain}")
|
45
|
+
else
|
46
|
+
registrar = [r['name'], r['organization'], r['url']].reject(&:nil?).join('; ')
|
47
|
+
printf "%25s: %s", 'Registrar', format_result(:pass, registrar)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
printf "%25s: %s", 'Registrar', format_result(:fail)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
unless result[:expires].nil?
|
55
|
+
# Expiration:
|
56
|
+
days_to_expiration = ((result[:expires] - Time.now)/(3600.0*24.0)).to_i
|
57
|
+
formatted_dte = "#{result[:expires].strftime("%F")} (#{days_to_expiration} days from now)"
|
58
|
+
if days_to_expiration < 30
|
59
|
+
state = :fail
|
60
|
+
elsif days_to_expiration < 30*3
|
61
|
+
state = :warn
|
62
|
+
else
|
63
|
+
state = :pass
|
64
|
+
end
|
65
|
+
|
66
|
+
printf "%25s: %s", 'Expiration date', format_result(state, formatted_dte)
|
67
|
+
end
|
68
|
+
|
69
|
+
printf "%25s:\n", 'DNS'
|
70
|
+
result[:dns].each do |k,v|
|
71
|
+
v[:domains].each do |record|
|
72
|
+
if record[:type] == 'MX'
|
73
|
+
description = "#{record[:data][0]}: #{record[:data][1].to_s}"
|
74
|
+
else
|
75
|
+
description = record[:data].to_s
|
76
|
+
end
|
77
|
+
printf "%25s ", ''
|
78
|
+
printf "%5s".green, record[:type].to_s
|
79
|
+
printf "( ttl=%i ): %s\n", record[:ttl], description
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
desc 'check_log_monitoring_addon [APP]', 'Check for log monitoring add-on'
|
87
|
+
def check_log_monitoring_addon(app)
|
88
|
+
a = client.addons(app, /(logentries|keen|papertrail|loggly|flydata)/)
|
89
|
+
names = a.collect{|x| x['name']}.join(', ')
|
90
|
+
result = a.count > 0 ? format_result(:pass, names) : result = format_result(:fail)
|
91
|
+
printf "%25s: %s", 'Log monitoring add-on', result
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'check_app_monitoring_addon [APP]', 'Check for app monitoring add-on'
|
95
|
+
def check_app_monitoring_addon(app)
|
96
|
+
a = client.addons(app, /(relic)/)
|
97
|
+
names = a.collect{|x| x['name']}.join(', ')
|
98
|
+
result = a.count > 0 ? format_result(:pass, names) : result = format_result(:fail)
|
99
|
+
printf "%25s: %s", 'App monitoring add-on', result
|
100
|
+
end
|
101
|
+
|
102
|
+
desc 'check_exception_handling_addon [APP]', 'Check for exception handling add-on'
|
103
|
+
def check_exception_handling_addon(app)
|
104
|
+
a = client.addons(app, /(airbrake|exceptional)/)
|
105
|
+
names = a.collect{|x| x['name']}.join(', ')
|
106
|
+
result = a.count > 0 ? format_result(:pass, names) : format_result(:fail)
|
107
|
+
printf "%25s: %s", 'Exception handling add-on', result
|
108
|
+
end
|
109
|
+
|
110
|
+
desc 'check_deployhooks_appon [APP]', 'Check for deployhooks handling add-on'
|
111
|
+
def check_deployhooks_appon(app)
|
112
|
+
a = client.addons(app, /deploynooks/)
|
113
|
+
names = a.collect{|x| x['name']}.join(', ')
|
114
|
+
result = a.count > 0 ? format_result(:pass, names) : format_result(:fail)
|
115
|
+
printf "%25s: %s", 'Deploy hooks add-on', result
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
desc 'check_ssl_addon [APP]', 'Check for SSL addon'
|
120
|
+
def check_ssl_addon(app)
|
121
|
+
a = client.addons(app, /ssl/)
|
122
|
+
names = a.collect{|x| x['name']}.join(', ')
|
123
|
+
result = a.count > 0 ? format_result(:pass, names) : format_result(:warn)
|
124
|
+
printf "%25s: %s", 'SSL add-on', result
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
desc "addons [APP]", "Print addons for a given app"
|
129
|
+
def addons(app)
|
130
|
+
_addons = client.addons(app)
|
131
|
+
_addons.each do |addon|
|
132
|
+
puts addon['name']
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
desc "addon_price [APP]", "Show the price of app addons"
|
137
|
+
def addon_price(app)
|
138
|
+
_addons = client.addons(app)
|
139
|
+
_addons.each do |addon|
|
140
|
+
printf "%-35s: $%10.2f / %-10s\n", addon['name'], addon['price']['cents'].to_i*0.01, addon['price']['unit']
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
desc "check_backups [APP]", "Check to make sure the app is properly set up"
|
145
|
+
def check_backups(app)
|
146
|
+
printf "%25s: ", "Database backups"
|
147
|
+
b = client.backups(app)
|
148
|
+
if b.count==0
|
149
|
+
printf format_result(:fail)
|
150
|
+
else
|
151
|
+
printf format_result(:pass, b.collect{|x| x[:plan]}.join(', '))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
desc 'check_database [APP]', 'Make sure we\'re running a production database'
|
156
|
+
def check_database(app)
|
157
|
+
printf "%25s: ", "Databases"
|
158
|
+
b = client.databases(app)
|
159
|
+
unique_plans = b.collect{|x| x[:plan]}.uniq
|
160
|
+
if b.nil?
|
161
|
+
printf format_result(:fail)
|
162
|
+
elsif unique_plans.count==1 and unique_plans[0]=='dev'
|
163
|
+
printf format_result(:warn, b.collect{|x| x[:plan]}.join(', '))
|
164
|
+
else
|
165
|
+
printf format_result(:pass, b.collect{|x| x[:plan]}.join(', '))
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
desc 'check_dyno_redundancy [APP]', 'Make sure more than one dyno is assigned'
|
170
|
+
def check_dyno_redundancy(app)
|
171
|
+
printf "%25s: ", "Dyno redundancy"
|
172
|
+
d = client.dynos(app).to_i
|
173
|
+
if d > 1
|
174
|
+
printf format_result(:pass, "#{d} dynos")
|
175
|
+
else
|
176
|
+
printf format_result(:fail, "#{d} dyno")
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
desc 'check_stack [APP]', 'Make sure app is on cedar stack'
|
181
|
+
def check_stack(app)
|
182
|
+
printf "%25s: ", "Cedar stack"
|
183
|
+
s = client.stack(app)
|
184
|
+
if s == 'cedar'
|
185
|
+
printf format_result(:pass, s)
|
186
|
+
else
|
187
|
+
printf format_result(:fail, s)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
desc 'check [APP]', 'Run production check for app'
|
193
|
+
def check(app)
|
194
|
+
puts
|
195
|
+
puts "Checking app #{app}:".blue
|
196
|
+
check_dyno_redundancy(app)
|
197
|
+
check_database(app)
|
198
|
+
check_backups(app)
|
199
|
+
check_stack(app)
|
200
|
+
check_exception_handling_addon(app)
|
201
|
+
check_deployhooks_appon(app)
|
202
|
+
check_log_monitoring_addon(app)
|
203
|
+
check_app_monitoring_addon(app)
|
204
|
+
check_ssl_addon(app)
|
205
|
+
check_domains(app)
|
206
|
+
puts
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
|
211
|
+
no_commands do
|
212
|
+
def client
|
213
|
+
@client ||= Blend::Client.heroku_client
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
no_tasks do
|
218
|
+
def format_result( state, message=nil )
|
219
|
+
case state
|
220
|
+
when :pass
|
221
|
+
sprintf "\u2713 #{message}\n".encode('utf-8').green
|
222
|
+
when :warn
|
223
|
+
sprintf "\u2718 #{message}\n".encode('utf-8').yellow
|
224
|
+
when :fail
|
225
|
+
sprintf "\u2718 #{message}\n".encode('utf-8').red
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Blend
|
2
|
+
module CLI
|
3
|
+
class Hipchat < Thor
|
4
|
+
desc "rooms", "Show all hipchat rooms"
|
5
|
+
def rooms
|
6
|
+
client.rooms.sort do
|
7
|
+
|b,a| a['last_active'].to_i <=> b['last_active'].to_i
|
8
|
+
end.each do |r|
|
9
|
+
printf "%6s %-35s %10s %s\n", r['room_id'], r['name'], r['last_active'], r['topic']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "room ROOM", "Show room info"
|
14
|
+
def room( room )
|
15
|
+
room_info = client.room_info( room )
|
16
|
+
puts "Name : #{room_info['name']}"
|
17
|
+
puts "ID : #{room_info['room_id']}"
|
18
|
+
puts "Topic : #{room_info['topic']}"
|
19
|
+
puts "Last active : #{room_info['last_active']}"
|
20
|
+
puts "Participants:"
|
21
|
+
room_info['participants'].each do |p|
|
22
|
+
puts p['name']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "users", "Show all users"
|
27
|
+
def users
|
28
|
+
client.users.sort do
|
29
|
+
|b,a| a['last_active'].to_i <=> b['last_active'].to_i
|
30
|
+
end.each do |u|
|
31
|
+
printf "%6s %-25s %-15s %-25s %-17s %-19s %s\n", u['user_id'], u['name'], u['mention_name'], u['email'], u['last_active'], u['timezone'], u['title']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "create ROOM", "Create a hipchat room"
|
36
|
+
def create( room )
|
37
|
+
puts "Creating room: #{room}"
|
38
|
+
|
39
|
+
client.create_room( room, "Let's talk about #{room}!" )
|
40
|
+
|
41
|
+
rooms
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "topic ROOM TOPIC", "Set a topic of a hipchat room"
|
45
|
+
def topic( room, topic )
|
46
|
+
client.set_topic room, topic
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "message ROOM MESSAGE", "Send a room a message"
|
50
|
+
def message( room, message )
|
51
|
+
client.post_message room, message
|
52
|
+
end
|
53
|
+
|
54
|
+
no_commands do
|
55
|
+
def client
|
56
|
+
@client ||= Blend::Client.hipchat_client
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|