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/client.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'blend/client/hipchat_client'
|
2
|
+
require 'blend/client/github_client'
|
3
|
+
require 'blend/client/juice_client'
|
4
|
+
require 'blend/client/heroku_client'
|
5
|
+
|
6
|
+
module Blend
|
7
|
+
module Client
|
8
|
+
class << self
|
9
|
+
def hipchat_client
|
10
|
+
@hipchat_client ||= Blend::Client::HipchatClient.new( HipChat::API.new( get_token( :hipchat ) ) )
|
11
|
+
end
|
12
|
+
|
13
|
+
def github_client
|
14
|
+
@github_client ||= Blend::Client::GithubClient.new( Github.new( oauth_token: get_token( :github ) ) )
|
15
|
+
end
|
16
|
+
|
17
|
+
def juice_client( options = {} )
|
18
|
+
if( options[:auth_token] )
|
19
|
+
if( @juice_client && @juice_client.auth_token != options[:auth_token] )
|
20
|
+
@juice_client = nil
|
21
|
+
@hipchat_client = nil
|
22
|
+
@github_client = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
@juice_client ||= Blend::Client::JuiceClient.new options
|
26
|
+
end
|
27
|
+
|
28
|
+
@juice_client ||= Blend::Client::JuiceClient.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def heroku_client
|
32
|
+
@heroku_client ||= Blend::Client::HerokuClient.new( Heroku::API.new( api_key: get_token( :heroku ) ) )
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_token( type )
|
36
|
+
token = case type
|
37
|
+
|
38
|
+
#when :juice
|
39
|
+
# Don't use this. This is already handled together with the login
|
40
|
+
# stuff in JuiceClient. It could be refactored to fit in with this
|
41
|
+
# method a little better, but what'd be the point? It works and
|
42
|
+
# it's straightforward.
|
43
|
+
when :hipchat
|
44
|
+
ENV['HIPCHAT_API_TOKEN'] || juice_client.hipchat_api
|
45
|
+
when :github
|
46
|
+
ENV['GITHUB_API_TOKEN'] || juice_client.auth( "github" )
|
47
|
+
when :heroku
|
48
|
+
# Opt for the organization-wide heroku api token:
|
49
|
+
ENV['HEROKU_API_TOKEN'] || juice_client.heroku_api
|
50
|
+
# Instead of per-user heroku auth:
|
51
|
+
#ENV['HEROKU_API_TOKEN'] || juice_client.auth( "heroku" )
|
52
|
+
else
|
53
|
+
throw "Unknown token type #{type}".red
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
throw "Token not found for #{type}" if token.nil? || token == ""
|
58
|
+
|
59
|
+
token
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Blend
|
2
|
+
module Client
|
3
|
+
class GithubClient
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize( client )
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
def list_repos( filter = nil )
|
11
|
+
return @repos if @repos && filter.nil?
|
12
|
+
|
13
|
+
repos = []
|
14
|
+
@client.repos.list( org: "sublimeguile" ).each_page do |page|
|
15
|
+
page.each do |r|
|
16
|
+
repos << r
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if filter
|
21
|
+
repos = repos.select { |x| x['name'] =~ /#{filter}/ }
|
22
|
+
end
|
23
|
+
|
24
|
+
repos = repos.sort do |a,b|
|
25
|
+
a['name'] <=> b['name']
|
26
|
+
end
|
27
|
+
|
28
|
+
@repos = repos if filter.nil?
|
29
|
+
repos
|
30
|
+
end
|
31
|
+
|
32
|
+
def repo_create( team, name )
|
33
|
+
require 'pp'
|
34
|
+
pp @client.repos.create org: "sublimeguile", name: name, public: false, private: true
|
35
|
+
add_team_repo( team, "sublimeguile/#{name}" )
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def list_collaborators( repo )
|
40
|
+
@client.repos.collaborators.list *repo.split( /\// )
|
41
|
+
end
|
42
|
+
|
43
|
+
def list_hooks( repo )
|
44
|
+
@client.repos.hooks.list( *repo.split( /\// ) )
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_hook( repo, name, config )
|
48
|
+
@client.repos.hooks.create *repo.split( /\// ), name: name, config: config
|
49
|
+
end
|
50
|
+
|
51
|
+
def list_teams
|
52
|
+
@client.orgs.teams.list "sublimeguile"
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_team_from_name( name )
|
56
|
+
list_teams.select { |x| x['name'] == name }
|
57
|
+
end
|
58
|
+
|
59
|
+
def list_team( team )
|
60
|
+
find_team_from_name( team ).first
|
61
|
+
end
|
62
|
+
|
63
|
+
def list_team_repos( team )
|
64
|
+
find_team_from_name(team).each do |x|
|
65
|
+
return @client.orgs.teams.repos x.id
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def list_team_members( team )
|
70
|
+
find_team_from_name(team).each do |x|
|
71
|
+
return @client.orgs.teams.list_members x.id
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def create_team( team )
|
76
|
+
@client.orgs.teams.create "sublimeguile", { name: team, permission: "push" }
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_team_member( team, user )
|
80
|
+
find_team_from_name( team ).each do |x|
|
81
|
+
return @client.orgs.teams.add_member x.id, user
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def remove_team_member( team, user)
|
86
|
+
find_team_from_name( team ).each do |x|
|
87
|
+
return @client.orgs.teams.remove_member x.id, user
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_team_repo( team, repo )
|
92
|
+
user,name = repo.split( /\// )
|
93
|
+
find_team_from_name( team ).each do |x|
|
94
|
+
return @client.orgs.teams.add_repo x.id, user, name
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def remove_team_repo( team, repo )
|
99
|
+
user,name = repo.split( /\// )
|
100
|
+
find_team_from_name( team ).each do |x|
|
101
|
+
return @client.orgs.teams.remove_repo x.id, user, name
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
|
3
|
+
module Blend
|
4
|
+
module Client
|
5
|
+
class HerokuClient
|
6
|
+
|
7
|
+
attr_accessor :client
|
8
|
+
|
9
|
+
def initialize( client )
|
10
|
+
@client = client
|
11
|
+
@addons = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def info( app )
|
15
|
+
client.get_app(app).body
|
16
|
+
end
|
17
|
+
|
18
|
+
def domains( app )
|
19
|
+
client.get_domains(app).body
|
20
|
+
end
|
21
|
+
|
22
|
+
def nonheroku_domains( app )
|
23
|
+
domains(app).reject{|x| x['base_domain']=="herokuapp.com" || x['domain'] =~ /^www/}.collect{|x| x['domain']}
|
24
|
+
end
|
25
|
+
|
26
|
+
def addons( app, filter=nil )
|
27
|
+
@addons[app] ||= client.get_addons(app).body
|
28
|
+
if filter.nil?
|
29
|
+
@addons[app]
|
30
|
+
else
|
31
|
+
@addons[app].select{|x| x['name'] =~ filter}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def addon_names( app, filter )
|
36
|
+
addons(app, filter).collect do |addon|
|
37
|
+
name,plan = addon['name'].split(/:/)
|
38
|
+
{name: name, plan: plan}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def addon_matches( app, addon_name )
|
43
|
+
addons(app).select{|x| x[:name] =~ addon_name}
|
44
|
+
end
|
45
|
+
|
46
|
+
def stack(app)
|
47
|
+
info(app)['stack']
|
48
|
+
end
|
49
|
+
|
50
|
+
def dynos( app )
|
51
|
+
info(app)['dynos']
|
52
|
+
end
|
53
|
+
|
54
|
+
def backups( app )
|
55
|
+
addon_names(app, /pgbackups/)
|
56
|
+
end
|
57
|
+
|
58
|
+
def databases( app )
|
59
|
+
addon_names(app, /postgresq/)
|
60
|
+
end
|
61
|
+
|
62
|
+
def run_domain_checker( app )
|
63
|
+
results = {}
|
64
|
+
nonheroku_domains(app).map{|x| x['domain']}.each do |d|
|
65
|
+
|
66
|
+
|
67
|
+
DomainChecker.check(d)
|
68
|
+
|
69
|
+
result = {}
|
70
|
+
results[d] = result
|
71
|
+
|
72
|
+
# Check registered
|
73
|
+
result[:registered] = dc.registered?
|
74
|
+
|
75
|
+
# Check registrar
|
76
|
+
result[:registrar] = dc.registrar
|
77
|
+
|
78
|
+
result[:ssl] = dc.ssl
|
79
|
+
end
|
80
|
+
|
81
|
+
results
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Blend
|
2
|
+
module Client
|
3
|
+
class HipchatClient
|
4
|
+
attr_accessor :api
|
5
|
+
|
6
|
+
def initialize( api )
|
7
|
+
@api = api
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_room( name, topic )
|
11
|
+
creator = user_by_name 'Will Schenk'
|
12
|
+
|
13
|
+
ret = api.rooms_create name, creator['user_id'], 'public', topic
|
14
|
+
|
15
|
+
if ret["error"]
|
16
|
+
pp ret["error"]
|
17
|
+
end
|
18
|
+
|
19
|
+
@rooms = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def rooms
|
23
|
+
@rooms ||= api.rooms_list.parsed_response['rooms']
|
24
|
+
raise Exceptions::HipchatAuthenticationFailure if @rooms.nil?
|
25
|
+
@rooms
|
26
|
+
end
|
27
|
+
|
28
|
+
def room_name_from_xmpp_jid( name )
|
29
|
+
rooms.select{|x| x['xmpp_jid'] == name}.first['name'] rescue nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def room_by_name( name )
|
33
|
+
rooms.each do |r|
|
34
|
+
return r if r['name'] == name
|
35
|
+
end
|
36
|
+
|
37
|
+
puts "Room #{name} not found".red
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def room_info( name )
|
42
|
+
room = room_by_name name
|
43
|
+
|
44
|
+
if( room )
|
45
|
+
api.rooms_show( room['room_id'] ).parsed_response['room']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def users
|
50
|
+
@users ||= api.users_list.parsed_response['users']
|
51
|
+
end
|
52
|
+
|
53
|
+
def user_by_name( name )
|
54
|
+
users.each do |u|
|
55
|
+
return u if u['name'] == name
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "User #{name} not found".red
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_topic( room, topic )
|
62
|
+
room = room_by_name room
|
63
|
+
|
64
|
+
if room
|
65
|
+
api.rooms_topic( room['room_id'], topic)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def post_message( room, message, color = 'yellow', notify = false, type = 'text' )
|
70
|
+
room = room_by_name room
|
71
|
+
|
72
|
+
if room
|
73
|
+
api.rooms_message room['room_id'], 'hfc', message, notify ? "1" : "0", color, type
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,400 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
|
3
|
+
module Blend
|
4
|
+
module Client
|
5
|
+
class JuiceClient
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Wrap HHTParty's get method so we can actually catch errors
|
11
|
+
def get( url, *args )
|
12
|
+
response = super( url, *args )
|
13
|
+
case response.code
|
14
|
+
when 401
|
15
|
+
raise Exceptions::AuthenticationFailure
|
16
|
+
end
|
17
|
+
return response
|
18
|
+
end
|
19
|
+
|
20
|
+
def post( url, *args )
|
21
|
+
response = super( url, *args )
|
22
|
+
case response.code
|
23
|
+
when 401
|
24
|
+
raise Exceptions::AuthenticationFailure
|
25
|
+
end
|
26
|
+
return response
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(opts={})
|
32
|
+
@options = {}
|
33
|
+
@options['auth_token'] = opts[:auth_token] if opts.include? :auth_token
|
34
|
+
end
|
35
|
+
|
36
|
+
base_uri ENV['JUICE_API_ENDPOINT'] || 'http://happyfunjuice.com/api'
|
37
|
+
# debug_output $stderr
|
38
|
+
|
39
|
+
def login
|
40
|
+
query_login
|
41
|
+
end
|
42
|
+
|
43
|
+
def logout
|
44
|
+
destroy_auth_token
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_project( name )
|
48
|
+
auth_token
|
49
|
+
pp self.class.post "/organizations/1/projects", { query: { "project[name]" => name } }
|
50
|
+
@projects = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def profile
|
54
|
+
auth_token
|
55
|
+
@projects ||= self.class.get( "/user.json" )
|
56
|
+
end
|
57
|
+
|
58
|
+
def projects
|
59
|
+
auth_token
|
60
|
+
@projects ||= self.class.get( "/projects.json" ).each do |x|
|
61
|
+
x['blend_config'] ||= {}
|
62
|
+
x['blend_config']['teams'] ||= []
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def summary
|
67
|
+
auth_token
|
68
|
+
@projects ||= self.class.get( "/projects/summary.json" ).each do |x|
|
69
|
+
x['blend_config'] ||= {}
|
70
|
+
x['blend_config']['teams'] ||= []
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def organizations
|
75
|
+
auth_token
|
76
|
+
@organizations ||= self.class.get( "/organizations.json" ).each do |o|
|
77
|
+
o['blend_config'] ||= {}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def project_info_from_room( room )
|
82
|
+
auth_token
|
83
|
+
if room =~ /[0-9]+_[^@]+@.*/
|
84
|
+
room_name = Blend::Client.hipchat_client.room_name_from_xmpp_jid( room )
|
85
|
+
else
|
86
|
+
room_name = room
|
87
|
+
end
|
88
|
+
return nil if room_name.nil? or room_name.length==0
|
89
|
+
projects.select{|x| (x['blend_config']['hipchat_room'].downcase rescue nil) == room_name.downcase}.first
|
90
|
+
end
|
91
|
+
|
92
|
+
def project_id_from_room( room )
|
93
|
+
@project_ids_from_rooms ||= {}
|
94
|
+
p = project_info_from_room( room )
|
95
|
+
if p.nil?
|
96
|
+
nil
|
97
|
+
else
|
98
|
+
@project_ids_from_rooms[room] ||= p['id']
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def project_id_from_name( name )
|
103
|
+
return name if name.to_s =~ /^[0-9]+$/
|
104
|
+
|
105
|
+
projects.each do |project|
|
106
|
+
return project['id'] if project['name'].projectize == name.projectize
|
107
|
+
end
|
108
|
+
|
109
|
+
puts "Couldn't find project #{name.projectize}"
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
|
113
|
+
def project( id )
|
114
|
+
return nil if id.nil?
|
115
|
+
|
116
|
+
auth_token
|
117
|
+
data = self.class.get "/projects/#{id}.json"
|
118
|
+
|
119
|
+
data['blend_config'] ||= {}
|
120
|
+
data['blend_config']['teams'] ||= []
|
121
|
+
|
122
|
+
data
|
123
|
+
end
|
124
|
+
|
125
|
+
def lookup_user( query )
|
126
|
+
auth_token
|
127
|
+
self.class.get "/users/lookup.json", { query: { query: query } }
|
128
|
+
end
|
129
|
+
|
130
|
+
def search_users( query )
|
131
|
+
auth_token
|
132
|
+
self.class.get "/users/search.json", { query: { query: query } }
|
133
|
+
end
|
134
|
+
|
135
|
+
def user_from_github_user( github )
|
136
|
+
@github_users ||= {}
|
137
|
+
@github_users[github] ||= search_users(github).select { |x| x['github_handle'] == github }.first
|
138
|
+
end
|
139
|
+
|
140
|
+
def organization_users( id )
|
141
|
+
auth_token
|
142
|
+
self.class.get "/organizations/#{id}/users.json"
|
143
|
+
end
|
144
|
+
|
145
|
+
def project_users( id )
|
146
|
+
auth_token
|
147
|
+
self.class.get "/projects/#{id}/users.json"
|
148
|
+
end
|
149
|
+
|
150
|
+
def project_add_user( project_id, user_id )
|
151
|
+
auth_token
|
152
|
+
self.class.post "/projects/#{project_id}/users/#{user_id}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def project_config( id, config = nil )
|
156
|
+
auth_token
|
157
|
+
if( config.nil? )
|
158
|
+
puts "Loading config"
|
159
|
+
project = self.class.get "/projects/#{id}.json"
|
160
|
+
project['blend_config'] || {}
|
161
|
+
else
|
162
|
+
puts "Setting config #{config}"
|
163
|
+
self.class.put "/projects/#{id}.json", { query: { project: { blend_config: config } } }
|
164
|
+
@projects = nil
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def project_add_team( id, github_team )
|
169
|
+
config = project_config( id )
|
170
|
+
config['teams'] ||= []
|
171
|
+
config['teams'] << github_team
|
172
|
+
project_config( id, config )
|
173
|
+
end
|
174
|
+
|
175
|
+
def project_add_hipchat( id, hipchat )
|
176
|
+
config = project_config( id )
|
177
|
+
config['hipchat_room'] = hipchat
|
178
|
+
project_config( id, config )
|
179
|
+
end
|
180
|
+
|
181
|
+
def feeds( id )
|
182
|
+
auth_token
|
183
|
+
self.class.get "/projects/#{id}/feeds.json"
|
184
|
+
end
|
185
|
+
|
186
|
+
def add_feed( project_id, feed_type, key, environment = nil )
|
187
|
+
auth_token
|
188
|
+
self.class.post "/projects/#{project_id}/feeds", {query: { feed_type: feed_type, key: key } }
|
189
|
+
end
|
190
|
+
|
191
|
+
def environments( id )
|
192
|
+
auth_token
|
193
|
+
self.class.get "/projects/#{id}/environments.json"
|
194
|
+
end
|
195
|
+
|
196
|
+
def heroku_apps( id )
|
197
|
+
auth_token
|
198
|
+
f = feeds( id ).group_by{|x| x['feed_name']}
|
199
|
+
(f['heroku'] || []).group_by { |x| x['environment']['name'].downcase}
|
200
|
+
end
|
201
|
+
|
202
|
+
def auths
|
203
|
+
auth_token
|
204
|
+
@auths ||= self.class.get "/profile/authentications.json"
|
205
|
+
end
|
206
|
+
|
207
|
+
def auth( provider )
|
208
|
+
auths.select do |x|
|
209
|
+
return x['token'] if x['provider'] == provider
|
210
|
+
end
|
211
|
+
|
212
|
+
nil
|
213
|
+
end
|
214
|
+
|
215
|
+
def activities( project_id, after, before = Time.now )
|
216
|
+
auth_token
|
217
|
+
activities = self.class.get "/projects/#{project_id}/activities.json", {query: { after: after.to_i, before: before.to_i } }
|
218
|
+
activities_stats activities, after, before # <-- so activities actually returns activities_stats? That's... weird.
|
219
|
+
end
|
220
|
+
|
221
|
+
def activities_stats( activities, after, before )
|
222
|
+
stats = { type: {},
|
223
|
+
actors_activites: {},
|
224
|
+
activities: activities,
|
225
|
+
after: after,
|
226
|
+
before: before,
|
227
|
+
active_tickets: {},
|
228
|
+
closed_tickets: {}}
|
229
|
+
|
230
|
+
activities.each do |x|
|
231
|
+
x['happened_at'] = Time.parse x['happened_at']
|
232
|
+
end.sort do |a,b|
|
233
|
+
a['happened_at'] <=> b['happened_at']
|
234
|
+
end.each do |a|
|
235
|
+
actor = a['actor_identifier']
|
236
|
+
type = a['activity_type']
|
237
|
+
stats[:type][type] ||= []
|
238
|
+
stats[:type][type] << a
|
239
|
+
|
240
|
+
if( !(type =~ /^juice/) && !(type =~ /tweet/) )
|
241
|
+
stats[:actors_activites][type] ||= {}
|
242
|
+
stats[:actors_activites][type][actor] ||= []
|
243
|
+
stats[:actors_activites][type][actor] << a
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
stats
|
248
|
+
end
|
249
|
+
|
250
|
+
def heroku_api( token = nil )
|
251
|
+
auth_token
|
252
|
+
ret = {}
|
253
|
+
if( token )
|
254
|
+
ret = self.class.post "/organizations/1/heroku/api_token.json", {query: {heroku_api_token: token}}
|
255
|
+
else
|
256
|
+
ret = self.class.get "/organizations/1/heroku/api_token.json"
|
257
|
+
end
|
258
|
+
ret['heroku_api_token']
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
def hipchat_api( token = nil )
|
263
|
+
auth_token
|
264
|
+
ret = {}
|
265
|
+
if( token )
|
266
|
+
ret = self.class.post "/organizations/1/hipchat/auth_token.json", {query: {hipchat_auth_token: token}}
|
267
|
+
else
|
268
|
+
ret = self.class.get "/organizations/1/hipchat/auth_token.json"
|
269
|
+
end
|
270
|
+
ret['hipchat_auth_token']
|
271
|
+
end
|
272
|
+
|
273
|
+
def hipchat_check
|
274
|
+
hipchat_client = Blend::Client.hipchat_client
|
275
|
+
|
276
|
+
rooms = {}
|
277
|
+
hipchat_client.rooms.each { |x| rooms[x['name']] = [] }
|
278
|
+
|
279
|
+
projects.each do |project|
|
280
|
+
room = project['blend_config']['hipchat_room']
|
281
|
+
if room
|
282
|
+
rooms[room] ||= []
|
283
|
+
rooms[room] << project['name']
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
rooms.keys.sort { |a,b| a <=> b }.collect do |room|
|
288
|
+
if rooms[room].size == 0
|
289
|
+
rooms[room] << "_unassigned_"
|
290
|
+
end
|
291
|
+
{ room: room, projects: rooms[room] }
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def github_team_check
|
296
|
+
github_client = Blend::Client.github_client
|
297
|
+
|
298
|
+
teams = {}
|
299
|
+
github_client.list_teams.each do |team|
|
300
|
+
teams[team.name] = []
|
301
|
+
end
|
302
|
+
|
303
|
+
projects.each do |project|
|
304
|
+
project['blend_config']['teams'].each do |x|
|
305
|
+
teams[x] << project
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
teams
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
def config_file
|
314
|
+
"#{ENV['HOME']}/.juice.yml"
|
315
|
+
end
|
316
|
+
|
317
|
+
def destroy_auth_token
|
318
|
+
_prev_options = @options
|
319
|
+
begin
|
320
|
+
return File.delete config_file
|
321
|
+
rescue Errno::ENOENT
|
322
|
+
raise Exceptions::AlreadyLoggedOut
|
323
|
+
ensure
|
324
|
+
@options = {}
|
325
|
+
end
|
326
|
+
_prev_options
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
def check_credentials( username, password )
|
331
|
+
response = self.class.post '/auth', {body: {username: username, password: password}}
|
332
|
+
raise Exceptions::LoginAuthenticationFailure if response['auth_token'].nil?
|
333
|
+
@options = response.parsed_response if @options['auth_token'].nil?
|
334
|
+
end
|
335
|
+
|
336
|
+
|
337
|
+
def query_login
|
338
|
+
|
339
|
+
begin
|
340
|
+
response = read_auth_token
|
341
|
+
@options = response
|
342
|
+
|
343
|
+
# If it doesn't contain an auth_token, it's as good as not existing:
|
344
|
+
|
345
|
+
raise Exceptions::AlreadyLoggedIn
|
346
|
+
|
347
|
+
rescue Errno::ENOENT
|
348
|
+
username = ask( "Username : " ) { |q| q.echo = true }
|
349
|
+
password = ask( "Password : " ) { |q| q.echo = '.' }
|
350
|
+
|
351
|
+
@options = check_credentials( username, password )
|
352
|
+
|
353
|
+
write_auth_token( @options )
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
def read_auth_token
|
359
|
+
begin
|
360
|
+
response = YAML.load( File.read( config_file ) )
|
361
|
+
raise StandardError.new unless response['auth_token']
|
362
|
+
response
|
363
|
+
rescue StandardError
|
364
|
+
raise Errno::ENOENT
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
|
369
|
+
def write_auth_token( options )
|
370
|
+
File.open( config_file, "w" ) do |o|
|
371
|
+
o.puts YAML.dump( options || {} )
|
372
|
+
end
|
373
|
+
return options
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
def auth_token
|
378
|
+
|
379
|
+
# Just return it if it's been set manually. This is accomplished when client is
|
380
|
+
# instantiated with JuiceClient.new(auth_token: ---)
|
381
|
+
if !@options['auth_token'].nil?
|
382
|
+
self.class.default_params auth_token: @options['auth_token']
|
383
|
+
return @options['auth_token']
|
384
|
+
end
|
385
|
+
|
386
|
+
if ENV['JUICE_AUTH_TOKEN'].nil?
|
387
|
+
begin
|
388
|
+
query_login
|
389
|
+
rescue Exceptions::AlreadyLoggedIn
|
390
|
+
end
|
391
|
+
else
|
392
|
+
@options = { 'auth_token' => ENV['JUICE_AUTH_TOKEN'] }
|
393
|
+
end
|
394
|
+
self.class.default_params auth_token: @options['auth_token']
|
395
|
+
@options['auth_token']
|
396
|
+
end
|
397
|
+
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|