pagoda 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +2 -0
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +39 -0
- data/README +3 -0
- data/Rakefile +11 -0
- data/bin/pagoda +13 -0
- data/lib/pagoda/client.rb +221 -0
- data/lib/pagoda/command.rb +93 -0
- data/lib/pagoda/commands/app.rb +243 -0
- data/lib/pagoda/commands/auth.rb +149 -0
- data/lib/pagoda/commands/base.rb +184 -0
- data/lib/pagoda/commands/help.rb +100 -0
- data/lib/pagoda/commands/tunnel.rb +49 -0
- data/lib/pagoda/helpers.rb +127 -0
- data/lib/pagoda/tunnel_proxy.rb +130 -0
- data/lib/pagoda/version.rb +3 -0
- data/lib/pagoda.rb +3 -0
- data/pagoda.gemspec +29 -0
- data/spec/base.rb +21 -0
- data/spec/client_spec.rb +255 -0
- data/spec/command_spec.rb +26 -0
- data/spec/commands/auth_spec.rb +57 -0
- metadata +167 -0
data/.bundle/config
ADDED
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pagoda-*.*.*.gem
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pagoda (0.0.1)
|
5
|
+
crack
|
6
|
+
iniparse
|
7
|
+
json_pure
|
8
|
+
rest-client
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.2.5)
|
14
|
+
crack (0.1.8)
|
15
|
+
diff-lcs (1.1.2)
|
16
|
+
iniparse (1.1.4)
|
17
|
+
json_pure (1.5.1)
|
18
|
+
mime-types (1.16)
|
19
|
+
rest-client (1.6.1)
|
20
|
+
mime-types (>= 1.16)
|
21
|
+
rspec (2.5.0)
|
22
|
+
rspec-core (~> 2.5.0)
|
23
|
+
rspec-expectations (~> 2.5.0)
|
24
|
+
rspec-mocks (~> 2.5.0)
|
25
|
+
rspec-core (2.5.1)
|
26
|
+
rspec-expectations (2.5.0)
|
27
|
+
diff-lcs (~> 1.1.2)
|
28
|
+
rspec-mocks (2.5.0)
|
29
|
+
webmock (1.6.2)
|
30
|
+
addressable (>= 2.2.2)
|
31
|
+
crack (>= 0.1.7)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
pagoda!
|
38
|
+
rspec
|
39
|
+
webmock
|
data/README
ADDED
data/Rakefile
ADDED
data/bin/pagoda
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'pagoda/client'
|
7
|
+
require 'pagoda/command'
|
8
|
+
|
9
|
+
args = ARGV.dup
|
10
|
+
ARGV.clear
|
11
|
+
command = args.shift.strip rescue 'help'
|
12
|
+
|
13
|
+
Pagoda::Command.run(command, args)
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'pagoda/version'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'rest_client'
|
4
|
+
require 'uri'
|
5
|
+
require 'json/pure' unless {}.respond_to?(:to_json)
|
6
|
+
|
7
|
+
class Pagoda::Client
|
8
|
+
|
9
|
+
attr_reader :user, :password
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def version
|
13
|
+
Pagoda::VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
def gem_version_string
|
17
|
+
"pagoda-gem/#{version}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(user, password)
|
22
|
+
@user = user
|
23
|
+
@password = password
|
24
|
+
end
|
25
|
+
|
26
|
+
def app_list
|
27
|
+
doc = xml(get("/apps.xml").to_s)
|
28
|
+
doc.elements['apps'].elements.to_a('//app/').inject([]) do |list, app|
|
29
|
+
list << {
|
30
|
+
:name => app.elements['name'].text,
|
31
|
+
:instances => app.elements['instances'].text,
|
32
|
+
:git_url => app.elements['git-url'].text
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def database_exists?(app, mysql_instance)
|
38
|
+
begin
|
39
|
+
response = get("/apps/#{app}/databases/#{mysql_instance}.xml")
|
40
|
+
true
|
41
|
+
rescue RestClient::ResourceNotFound => e
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def app_create(name, git_url)
|
48
|
+
doc = xml(post("/apps.xml", {:app => {:name => name, :git_url => git_url}}).to_s)
|
49
|
+
doc.elements.to_a('//app/*').inject({}) do |hash, element|
|
50
|
+
case element.name
|
51
|
+
when "owner"
|
52
|
+
hash[:owner] = {:username => element.elements['username'].text, :email => element.elements['email'].text}
|
53
|
+
when "collaborators"
|
54
|
+
hash[:collaborators] = element.elements.to_a('//collaborator/').inject([]) do |list, collaborator|
|
55
|
+
list << {:username => collaborator.elements['username'].text, :email => collaborator.elements['email'].text}
|
56
|
+
end
|
57
|
+
when "transactions"
|
58
|
+
hash[:transactions] = element.elements.to_a('//transaction/').inject([]) do |list, transaction|
|
59
|
+
list << {
|
60
|
+
:id => transaction.elements["id"].text,
|
61
|
+
:name => transaction.elements["name"].text,
|
62
|
+
:description => transaction.elements["description"].text,
|
63
|
+
:state => transaction.elements["state"].text,
|
64
|
+
:status => transaction.elements["status"].text
|
65
|
+
}
|
66
|
+
end
|
67
|
+
else
|
68
|
+
hash[element.name.gsub(/-/, '_').to_sym] = element.text
|
69
|
+
end
|
70
|
+
hash
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def app_info(app)
|
75
|
+
doc = xml(get("/apps/#{app}.xml").to_s)
|
76
|
+
doc.elements.to_a('//app/*').inject({}) do |hash, element|
|
77
|
+
case element.name
|
78
|
+
when "owner"
|
79
|
+
hash[:owner] = {:username => element.elements['username'].text, :email => element.elements['email'].text}
|
80
|
+
when "collaborators"
|
81
|
+
hash[:collaborators] = element.elements.to_a('//collaborator/').inject([]) do |list, collaborator|
|
82
|
+
list << {:username => collaborator.elements['username'].text, :email => collaborator.elements['email'].text}
|
83
|
+
end
|
84
|
+
when "transactions"
|
85
|
+
hash[:transactions] = element.elements.to_a('//transaction/').inject([]) do |list, transaction|
|
86
|
+
list << {
|
87
|
+
:id => transaction.elements["id"].text,
|
88
|
+
:name => transaction.elements["name"].text,
|
89
|
+
:description => transaction.elements["description"].text,
|
90
|
+
:state => transaction.elements["state"].text,
|
91
|
+
:status => transaction.elements["status"].text
|
92
|
+
}
|
93
|
+
end
|
94
|
+
else
|
95
|
+
hash[element.name.gsub(/-/, '_').to_sym] = element.text
|
96
|
+
end
|
97
|
+
hash
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def app_update(app, updates)
|
102
|
+
put("/apps/#{app}.xml", {:update => updates}).to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
def app_destroy(app)
|
106
|
+
delete("/apps/#{app}.xml").to_s
|
107
|
+
end
|
108
|
+
|
109
|
+
def transaction_list(app)
|
110
|
+
doc = xml(get("/apps/#{app}/transactions.xml").to_s)
|
111
|
+
doc.elements['transactions'].elements.to_a('//transaction/').inject([]) do |list, transaction|
|
112
|
+
list << {
|
113
|
+
:id => transaction.elements['id'].text,
|
114
|
+
:name => transaction.elements['name'].text,
|
115
|
+
:description => transaction.elements['description'].text,
|
116
|
+
:state => transaction.elements['state'].text,
|
117
|
+
:status => transaction.elements['status'].text
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def transaction_status(app, transaction)
|
123
|
+
doc = xml(get("/apps/#{app}/transactions/#{transaction}.xml").to_s)
|
124
|
+
doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
|
125
|
+
end
|
126
|
+
|
127
|
+
def rewind(app, places=1)
|
128
|
+
doc = xml(put("/apps/#{app}/rewind.xml", {:places => places}).to_s)
|
129
|
+
doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
|
130
|
+
end
|
131
|
+
|
132
|
+
def fast_forward(app, places=1)
|
133
|
+
doc = xml(put("/apps/#{app}/fast-forward.xml", {:places => places}).to_s)
|
134
|
+
doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
|
135
|
+
end
|
136
|
+
|
137
|
+
def rollback(app)
|
138
|
+
get("/apps/#{app}/rollback.xml").to_s
|
139
|
+
end
|
140
|
+
|
141
|
+
def deploy_latest(app)
|
142
|
+
doc = xml(post("/apps/#{app}/deploy.xml").to_s)
|
143
|
+
doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
|
144
|
+
end
|
145
|
+
|
146
|
+
def deploy(app, branch, commit)
|
147
|
+
doc = xml(post("/apps/#{app}/deploy.xml", {:deploy => {:branch => branch, :commit => commit}}).to_s)
|
148
|
+
doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
|
149
|
+
end
|
150
|
+
|
151
|
+
# def deploy(app)
|
152
|
+
# doc = xml(put("/apps/#{app}/deploy.xml").to_s)
|
153
|
+
# doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
|
154
|
+
# end
|
155
|
+
|
156
|
+
def scale_up(app, qty=1)
|
157
|
+
doc = xml(put("/apps/#{app}/scale-up.xml").to_s)
|
158
|
+
doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
|
159
|
+
end
|
160
|
+
|
161
|
+
def scale_down(app, qty=1)
|
162
|
+
doc = xml(put("/apps/#{app}/scale-down.xml").to_s)
|
163
|
+
doc.elements.to_a('//transaction/*').inject({}) { |hash, element| hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash }
|
164
|
+
end
|
165
|
+
|
166
|
+
def app_databases(app)
|
167
|
+
doc = xml(get("/apps/#{app}/databases.xml").to_s)
|
168
|
+
doc.elements['databases'].elements.to_a('//database/').inject([]) {|list, instance| list << {:name => instance.elements['name'].text} }
|
169
|
+
end
|
170
|
+
|
171
|
+
def on_warning(&blk)
|
172
|
+
@warning_callback = blk
|
173
|
+
end
|
174
|
+
|
175
|
+
protected
|
176
|
+
|
177
|
+
def resource(uri)
|
178
|
+
RestClient.proxy = ENV['HTTP_PROXY'] || ENV['http_proxy']
|
179
|
+
if uri =~ /^https?/
|
180
|
+
RestClient::Resource.new(uri, @user, @password)
|
181
|
+
else
|
182
|
+
# RestClient::Resource.new("http://127.0.0.1:3000#{uri}", @user, @password)
|
183
|
+
RestClient::Resource.new("https://dashboard.pagodabox.com#{uri}", @user, @password)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def get(uri, extra_headers={})
|
188
|
+
process(:get, uri, extra_headers)
|
189
|
+
end
|
190
|
+
|
191
|
+
def post(uri, payload="", extra_headers={})
|
192
|
+
process(:post, uri, extra_headers, payload)
|
193
|
+
end
|
194
|
+
|
195
|
+
def put(uri, payload="", extra_headers={})
|
196
|
+
process(:put, uri, extra_headers, payload)
|
197
|
+
end
|
198
|
+
|
199
|
+
def delete(uri, extra_headers={})
|
200
|
+
process(:delete, uri, extra_headers)
|
201
|
+
end
|
202
|
+
|
203
|
+
def process(method, uri, extra_headers={}, payload=nil)
|
204
|
+
headers = pagoda_headers.merge(extra_headers)
|
205
|
+
args = [method, payload, headers].compact
|
206
|
+
response = resource(uri).send(*args)
|
207
|
+
end
|
208
|
+
|
209
|
+
def pagoda_headers
|
210
|
+
{
|
211
|
+
'User-Agent' => self.class.gem_version_string,
|
212
|
+
'X-Ruby-Version' => RUBY_VERSION,
|
213
|
+
'X-Ruby-Platform' => RUBY_PLATFORM,
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
def xml(raw) # :nodoc:
|
218
|
+
REXML::Document.new(raw)
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'pagoda/helpers'
|
2
|
+
require 'pagoda/tunnel_proxy'
|
3
|
+
require 'pagoda/commands/base'
|
4
|
+
|
5
|
+
Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
|
6
|
+
|
7
|
+
module Pagoda
|
8
|
+
module Command
|
9
|
+
class InvalidCommand < RuntimeError; end
|
10
|
+
class CommandFailed < RuntimeError; end
|
11
|
+
|
12
|
+
extend Pagoda::Helpers
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def run(command, args, retries=0)
|
17
|
+
begin
|
18
|
+
run_internal 'auth:reauthorize', args.dup if retries > 0
|
19
|
+
run_internal(command, args.dup)
|
20
|
+
rescue InvalidCommand
|
21
|
+
error "Unknown command: #{command}. Run 'pagoda help' for usage information."
|
22
|
+
rescue RestClient::Unauthorized
|
23
|
+
if retries < 3
|
24
|
+
STDERR.puts "Authentication failure"
|
25
|
+
run(command, args, retries+1)
|
26
|
+
else
|
27
|
+
error "Authentication failure"
|
28
|
+
end
|
29
|
+
rescue RestClient::ResourceNotFound => e
|
30
|
+
error extract_not_found(e.http_body)
|
31
|
+
rescue RestClient::RequestFailed => e
|
32
|
+
error extract_error(e.http_body) unless e.http_code == 402 || e.http_code == 102
|
33
|
+
rescue RestClient::RequestTimeout
|
34
|
+
error "API request timed out. Please try again, or contact support@pagodagrid.com if this issue persists."
|
35
|
+
rescue CommandFailed => e
|
36
|
+
error e.message
|
37
|
+
rescue Interrupt => e
|
38
|
+
error "\n[canceled]"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def run_internal(command, args)
|
43
|
+
klass, method = parse(command)
|
44
|
+
runner = klass.new(args)
|
45
|
+
raise InvalidCommand unless runner.respond_to?(method)
|
46
|
+
runner.send(method)
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse(command)
|
50
|
+
parts = command.split(':')
|
51
|
+
case parts.size
|
52
|
+
when 1
|
53
|
+
begin
|
54
|
+
return eval("Pagoda::Command::#{command.capitalize}"), :index
|
55
|
+
rescue NameError, NoMethodError
|
56
|
+
return Pagoda::Command::App, command.to_sym
|
57
|
+
end
|
58
|
+
else
|
59
|
+
begin
|
60
|
+
const = Pagoda::Command
|
61
|
+
command = parts.pop
|
62
|
+
parts.each { |part| const = const.const_get(part.capitalize) }
|
63
|
+
return const, command.to_sym
|
64
|
+
rescue NameError
|
65
|
+
raise InvalidCommand
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def extract_not_found(body)
|
71
|
+
body =~ /^[\w\s]+ not found$/ ? body : "Resource not found"
|
72
|
+
end
|
73
|
+
|
74
|
+
def extract_error(body)
|
75
|
+
msg = parse_error_xml(body) || parse_error_json(body) || 'Internal server error'
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_error_xml(body)
|
79
|
+
xml_errors = REXML::Document.new(body).elements.to_a("//errors/error")
|
80
|
+
msg = xml_errors.map { |a| a.text }.join(" / ")
|
81
|
+
return msg unless msg.empty?
|
82
|
+
rescue Exception
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_error_json(body)
|
86
|
+
json = JSON.parse(body.to_s)
|
87
|
+
json['error']
|
88
|
+
rescue JSON::ParserError
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
module Pagoda::Command
|
2
|
+
class App < Base
|
3
|
+
|
4
|
+
def list
|
5
|
+
apps = client.app_list
|
6
|
+
if !apps.empty?
|
7
|
+
display
|
8
|
+
display "APPS"
|
9
|
+
display "//////////////////////////////////"
|
10
|
+
display
|
11
|
+
apps.each do |app|
|
12
|
+
display "- #{app[:name]}"
|
13
|
+
end
|
14
|
+
else
|
15
|
+
error ["looks like you haven't launched any apps", "type 'pagoda launch' to launch this project"]
|
16
|
+
end
|
17
|
+
display
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
if app_name = app(true)
|
22
|
+
error ["This project is already launched and paired to #{app_name}.", "To unpair run 'pagoda unpair'"]
|
23
|
+
end
|
24
|
+
|
25
|
+
unless locate_app_root
|
26
|
+
error ["Unable to find git config in this directory or in any parent directory"]
|
27
|
+
end
|
28
|
+
|
29
|
+
unless clone_url = extract_git_clone_url
|
30
|
+
errors = []
|
31
|
+
errors << "It appears you are using git (fantastic)."
|
32
|
+
errors << "However we only support git repos hosted with github."
|
33
|
+
errors << "Please ensure your repo is hosted with github."
|
34
|
+
error errors
|
35
|
+
end
|
36
|
+
|
37
|
+
unless name = args.dup.shift
|
38
|
+
error "Please Specify an app name ie. 'pagoda launch awesomeapp'"
|
39
|
+
end
|
40
|
+
|
41
|
+
display
|
42
|
+
display "+> Registering #{name}"
|
43
|
+
app = client.app_create(name, clone_url)
|
44
|
+
display "+> Launching...", false
|
45
|
+
loop_transaction(name)
|
46
|
+
add_app(name, clone_url)
|
47
|
+
display "+> #{name} launched"
|
48
|
+
|
49
|
+
unless option_value(nil, "--latest")
|
50
|
+
Pagoda::Command.run_internal("app:deploy", args)
|
51
|
+
end
|
52
|
+
|
53
|
+
display "-----------------------------------------------"
|
54
|
+
display
|
55
|
+
display "LIVE URL : http://#{name}.pagodabox.com"
|
56
|
+
display "ADMIN PANEL : http://dashboard.pagodabox.com"
|
57
|
+
display
|
58
|
+
display "-----------------------------------------------"
|
59
|
+
display
|
60
|
+
|
61
|
+
end
|
62
|
+
alias :launch :create
|
63
|
+
alias :register :create
|
64
|
+
|
65
|
+
def destroy
|
66
|
+
display
|
67
|
+
if confirm ["Are you totally completely sure you want to delete #{app} forever and ever?", "THIS CANNOT BE UNDONE! (y/n)"]
|
68
|
+
display "+> Destroying #{app}"
|
69
|
+
client.app_destroy(app)
|
70
|
+
display "+> #{app} has been successfully destroyed. RIP #{app}."
|
71
|
+
remove_app(app)
|
72
|
+
end
|
73
|
+
display
|
74
|
+
end
|
75
|
+
alias :delete :destroy
|
76
|
+
|
77
|
+
def info
|
78
|
+
display
|
79
|
+
info = client.app_info(app)
|
80
|
+
display "INFO - #{info[:name]}"
|
81
|
+
display "//////////////////////////////////"
|
82
|
+
display "name : #{info[:name]}"
|
83
|
+
display "clone_url : #{info[:git_url]}"
|
84
|
+
display
|
85
|
+
display "owner"
|
86
|
+
display "username : #{info[:owner][:username]}", true, 2
|
87
|
+
display "email : #{info[:owner][:email]}", true, 2
|
88
|
+
display
|
89
|
+
display "collaborators"
|
90
|
+
if info[:collaborators].any?
|
91
|
+
info[:collaborators].each_with_index do |collaborator, index|
|
92
|
+
display "username : #{collaborator[:username]}", true, 2
|
93
|
+
display "email : #{collaborator[:email]}", true, 2
|
94
|
+
end
|
95
|
+
else
|
96
|
+
display "(none)", true, 2
|
97
|
+
end
|
98
|
+
display
|
99
|
+
end
|
100
|
+
|
101
|
+
def pair
|
102
|
+
|
103
|
+
if app_name = app(true)
|
104
|
+
error ["This project is paired to #{app_name}.", "To unpair run 'pagoda unpair'"]
|
105
|
+
end
|
106
|
+
|
107
|
+
unless locate_app_root
|
108
|
+
error ["Unable to find git config in this directory or in any parent directory"]
|
109
|
+
end
|
110
|
+
|
111
|
+
unless my_repo = extract_git_clone_url
|
112
|
+
errors = []
|
113
|
+
errors << "It appears you are using git (fantastic)."
|
114
|
+
errors << "However we only support git repos hosted with github."
|
115
|
+
errors << "Please ensure your repo is hosted with github."
|
116
|
+
error errors
|
117
|
+
end
|
118
|
+
|
119
|
+
display
|
120
|
+
display "+> Locating deployed app with matching git repo"
|
121
|
+
|
122
|
+
apps = client.app_list
|
123
|
+
|
124
|
+
matching_apps = []
|
125
|
+
apps.each do |a|
|
126
|
+
if a[:git_url] == my_repo
|
127
|
+
matching_apps.push a
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
if matching_apps.count > 1
|
132
|
+
if name = app(true) || args.dup.shift
|
133
|
+
assign_app = nil
|
134
|
+
matching_apps.each do |a|
|
135
|
+
assign_app = a if a[:name] == name
|
136
|
+
end
|
137
|
+
if assign_app
|
138
|
+
display "+> Pairing this repo to deployed app - #{assign_app[:name]}"
|
139
|
+
pair_with_remote(assign_app)
|
140
|
+
display "+> Repo is now paired to '#{assign_app[:name]}'"
|
141
|
+
display
|
142
|
+
else
|
143
|
+
error "#{name} is not found among your launched app list"
|
144
|
+
end
|
145
|
+
else
|
146
|
+
errors = []
|
147
|
+
errors << "Multiple matches found"
|
148
|
+
errors << ""
|
149
|
+
matching_apps.each do |match|
|
150
|
+
errors << "-> #{match[:name]}"
|
151
|
+
end
|
152
|
+
errors << ""
|
153
|
+
errors << "You have more then one app that uses this repo."
|
154
|
+
errors << "Please specify which app you would like to pair to."
|
155
|
+
errors << ""
|
156
|
+
errors << "ex: pagoda pair #{matching_apps[0][:name]}"
|
157
|
+
error errors
|
158
|
+
end
|
159
|
+
elsif matching_apps.count == 1
|
160
|
+
match = matching_apps.first
|
161
|
+
display "+> Pairing this repo to deployed app - #{match[:name]}"
|
162
|
+
pair_with_remote match
|
163
|
+
display "+> Repo is now paired to '#{match[:name]}'"
|
164
|
+
display
|
165
|
+
else
|
166
|
+
error "Current git repo doesn't match any launched app repos"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def unpair
|
171
|
+
app
|
172
|
+
display
|
173
|
+
display "+> Unpairing this repo"
|
174
|
+
remove_app(app)
|
175
|
+
display "+> Free at last!"
|
176
|
+
display
|
177
|
+
end
|
178
|
+
|
179
|
+
def deploy
|
180
|
+
app
|
181
|
+
display
|
182
|
+
branch = parse_branch
|
183
|
+
commit = parse_commit
|
184
|
+
if option_value(nil, "--latest")
|
185
|
+
client.deploy_latest(app)
|
186
|
+
display "+> deploying to latest commit point on github...", false
|
187
|
+
loop_transaction
|
188
|
+
display "+> deployed"
|
189
|
+
display
|
190
|
+
else
|
191
|
+
client.deploy(app, branch, commit)
|
192
|
+
display "+> deploying to match current branch and commit...", false
|
193
|
+
loop_transaction
|
194
|
+
display "+> deployed"
|
195
|
+
display
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def rewind
|
200
|
+
app
|
201
|
+
display
|
202
|
+
transaction = client.rewind(app)
|
203
|
+
display "+> undo...", false
|
204
|
+
loop_transaction
|
205
|
+
display "+> done"
|
206
|
+
display
|
207
|
+
end
|
208
|
+
alias :rollback :rewind
|
209
|
+
alias :undo :rewind
|
210
|
+
|
211
|
+
def fast_forward
|
212
|
+
app
|
213
|
+
display
|
214
|
+
transaction = client.fast_forward(app)
|
215
|
+
display "+> redo...", false
|
216
|
+
loop_transaction
|
217
|
+
display "+> done"
|
218
|
+
display
|
219
|
+
end
|
220
|
+
alias :fastforward :fast_forward
|
221
|
+
alias :forward :fast_forward
|
222
|
+
alias :redo :fast_forward
|
223
|
+
|
224
|
+
protected
|
225
|
+
|
226
|
+
def pair_with_remote(app)
|
227
|
+
my_app_list = read_apps
|
228
|
+
current_root = locate_app_root
|
229
|
+
in_list = false
|
230
|
+
my_app_list.each do |app_str|
|
231
|
+
app_arr = app_str.split(" ")
|
232
|
+
if app[:git_url] == app_arr[1] && app[:name] == app_arr[0] || app_arr[2] == current_root
|
233
|
+
in_list = true
|
234
|
+
end
|
235
|
+
end
|
236
|
+
unless in_list
|
237
|
+
add_app app[:name]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
end
|
243
|
+
end
|