blend 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|