deployto 0.9.2 → 0.9.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.
data/README.md CHANGED
@@ -2,18 +2,10 @@
2
2
 
3
3
  Deployment tool for Platform-as-a-Service providers, with special support for multi-stage deploys (e.g. for staging environments).
4
4
 
5
- Platforms currently supported:
6
- * Efficient Cloud Platforms
7
-
8
- Platform support planned:
9
- * Heroku
10
- * Cloud Foundry Platforms
11
-
12
5
  ## Deploying a Ruby on Rails app
13
6
 
14
- gem install deploytool
15
- deploy add production portfolio.heroku.com
16
- deploy add staging portfolio.vcap.me
7
+ gem install deployto
8
+ deploy add production young-samurai-4@example.org
17
9
  deploy production
18
10
 
19
11
  ## Config file
@@ -22,6 +14,6 @@ deploy keeps a local config file .deployrc within the top-level sourcecode direc
22
14
 
23
15
  ## Legalese
24
16
 
25
- _Copyright 2011, Efficient Cloud Ltd.
17
+ Copyright 2011, Efficient Cloud Ltd.
26
18
 
27
19
  Released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).
@@ -1,22 +1,29 @@
1
+ require 'deploytool/version'
2
+
1
3
  class DeployTool::Command
2
4
  COMMANDS = ["to", "logs", "import", "export", "config"]
3
5
 
4
6
  def self.print_help
5
- puts "Deploytool Usage Instructions"
7
+ puts "Deploytool Version #{DeployTool::VERSION} Usage Instructions"
6
8
  puts ""
7
9
  puts "Add a target:"
8
- puts " deploy add production app1@demo.efficientcloud.com"
9
- puts " deploy add staging myapp.heroku.com"
10
- puts " deploy add failover api.cloudfoundry.com"
10
+ puts " deploy add production young-samurai-4@example.org"
11
+ puts " deploy add staging green-flower-2@example.org"
11
12
  puts ""
12
13
  puts "Deploy the current directory to the target:"
13
- puts " deploy to production"
14
+ puts " deploy production"
14
15
  end
15
16
 
16
17
  def self.run(command, args)
17
18
  if args.include?("--debug")
18
19
  args.delete("--debug")
19
20
  $logger.level = Logger::DEBUG
21
+ elsif args.include?("-d")
22
+ args.delete("-d")
23
+ $logger.level = Logger::DEBUG
24
+ elsif args.include?("-v")
25
+ args.delete("-v")
26
+ $logger.level = Logger::DEBUG
20
27
  else
21
28
  $logger.level = Logger::INFO
22
29
  end
@@ -75,8 +82,10 @@ class DeployTool::Command
75
82
  target.push(opts)
76
83
  rescue => e
77
84
  puts e
85
+ puts "\nPlease contact %s support: %s" % [EfficientCloud.cloud_name, EfficientCloud.support_email]
78
86
  exit 2
79
87
  end
88
+ DeployTool::Config[args[0]] = target.to_h
80
89
  end
81
90
 
82
91
  DeployTool::Config.save
@@ -51,6 +51,7 @@ class DeployTool::Target
51
51
  $logger.debug "Calling '%s' took longer than 5s, skipping" % [url, res.code, res.body]
52
52
  return nil
53
53
  end
54
+ return nil if res.nil?
54
55
  if res.code != '200'
55
56
  $logger.debug "Calling '%s' returned %s, skipping" % [url, res.code, res.body]
56
57
  return nil
@@ -1,7 +1,15 @@
1
1
  require 'highline'
2
-
3
2
  class DeployTool::Target::EfficientCloud < DeployTool::Target
4
- SUPPORTED_API_VERSION = 2
3
+ SUPPORTED_API_VERSION = 3
4
+
5
+ def self.cloud_name
6
+ @cloud_name || 'Efficient Cloud'
7
+ end
8
+
9
+ def self.support_email
10
+ @support_email || 'team@efficientcloud.com'
11
+ end
12
+
5
13
  def self.parse_target_spec(target_spec)
6
14
  server, app_name = target_spec.split('@').reverse
7
15
  if app_name.nil?
@@ -22,7 +30,12 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
22
30
  end
23
31
 
24
32
  def to_h
25
- {:type => "EfficientCloud", :api_server => @api_client.server, :app_name => @api_client.app_name, :email => @api_client.email, :password => @api_client.password}
33
+ x = {:type => "EfficientCloud", :api_server => @api_client.server, :app_name => @api_client.app_name,}
34
+ if @api_client.auth_method == :refresh_token
35
+ x.merge({:refresh_token => @api_client.refresh_token})
36
+ else
37
+ x
38
+ end
26
39
  end
27
40
 
28
41
  def to_s
@@ -31,7 +44,8 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
31
44
 
32
45
  def initialize(options)
33
46
  @api_server = options['api_server']
34
- @api_client = ApiClient.new(options['api_server'], options['app_name'], options['email'], options['password'])
47
+ auth = options.has_key?('refresh_token') ? {:refresh_token => options['refresh_token']} : {:email => options['email'], :password => options['password']}
48
+ @api_client = ApiClient.new(options['api_server'], options['app_name'], auth)
35
49
  end
36
50
 
37
51
  def self.check_version(api_server)
@@ -47,15 +61,13 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
47
61
  $logger.error "This version of deploytool is outdated.\nThis server requires at least API Version #{info['api_version']}."
48
62
  return false
49
63
  end
64
+ @cloud_name = info['cloud_name']
50
65
  return true
51
66
  end
52
67
 
53
68
  def self.create(target_spec)
54
- $logger.info "Please specify your controlpanel login information"
55
- email = HighLine.new.ask("E-mail: ")
56
- password = HighLine.new.ask("Password: ") {|q| q.echo = "*" }
57
69
  app_name, api_server = parse_target_spec(target_spec)
58
- EfficientCloud.new('api_server' => api_server, 'app_name' => app_name, 'email' => email, 'password' => password)
70
+ EfficientCloud.new('api_server' => api_server, 'app_name' => app_name)
59
71
  end
60
72
 
61
73
  def verify
@@ -69,8 +81,12 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
69
81
  $logger.error "Authentication failed (password wrong?)"
70
82
  elsif e.message.include?("404 ")
71
83
  $logger.error "Application does not exist"
84
+ elsif e.message.start_with?("ERROR ")
85
+ puts e.message
86
+ $logger.info "\nPlease contact %s support and include the above output: %s" % [EfficientCloud.cloud_name, EfficientCloud.support_email]
72
87
  else
73
88
  $logger.error "Remote server said: %s" % [e.message]
89
+ $logger.info "\nPlease contact %s support and include the above output: %s" % [EfficientCloud.cloud_name, EfficientCloud.support_email]
74
90
  end
75
91
  end
76
92
  exit 5
@@ -83,9 +99,22 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
83
99
  $logger.error info[:blocking_deployment]
84
100
  exit 4
85
101
  end
102
+ if info[:warn_deployment]
103
+ $logger.info info[:warn_deployment]
104
+ exit 5 if HighLine.new.ask("Deploy anyway? (y/N)").downcase.strip != 'y'
105
+ end
106
+
86
107
  code_token = @api_client.upload
87
108
  deploy_token = @api_client.deploy(code_token)
88
- @api_client.deploy_status(deploy_token, opts) # Blocks till deploy is done
109
+ begin
110
+ @api_client.deploy_status(deploy_token, opts) # Blocks till deploy is done
111
+ rescue => e
112
+ if e.message.start_with?("ERROR ")
113
+ puts e.message
114
+ else
115
+ puts "some error happened, sorry!"
116
+ end
117
+ end
89
118
  end
90
119
  end
91
120
 
@@ -5,39 +5,135 @@ require 'net/http/post/multipart'
5
5
  require 'fileutils'
6
6
  require 'tempfile'
7
7
  require 'zip'
8
+ require 'oauth2'
9
+ require 'multi_json'
10
+ require 'highline'
11
+
12
+ CLIENT_ID = 'com.efficientcloud.api.deploytool'
13
+ CLIENT_SECRET = '11d6b5cc70e4bc9563a3b8dd50dd34f6'
8
14
 
9
15
  class DeployTool::Target::EfficientCloud
10
16
  class ApiClient
11
- attr_reader :server, :app_name, :email, :password
12
- def initialize(server, app_name, email, password)
17
+ attr_reader :server, :app_name, :email, :password, :refresh_token, :auth_method
18
+ def initialize(server, app_name, auth)
13
19
  @app_name = app_name
14
20
  @server = server
15
- @email = email
16
- @password = password
21
+ if auth.has_key? :refresh_token
22
+ @refresh_token = auth[:refresh_token]
23
+ @auth_method = :refresh_token
24
+ elsif auth.has_key? :email
25
+ @auth_method = :password
26
+ @email = auth[:email]
27
+ @password = auth[:password]
28
+ else
29
+ @auth_method = :password
30
+ end
31
+ end
32
+
33
+ def re_auth
34
+ @auth_method = :password
17
35
  end
18
36
 
19
37
  def call(method, method_name, data = {})
20
38
  url = Addressable::URI.parse("http://#{@server}/api/cli/v1/apps/#{@app_name}/#{method_name}")
21
- data = data.merge(:email => @email, :password => @password)
22
- if method == :post
23
- res = Net::HTTP.start(url.host, url.port) do |http|
24
- http.request Net::HTTP::Post::Multipart.new(url.path, data)
39
+ client = OAuth2::Client.new(CLIENT_ID, CLIENT_SECRET, :site => "http://#{server}/", :token_url => '/oauth2/token', :raise_errors => false) do |builder|
40
+ builder.use Faraday::Request::Multipart
41
+ builder.use Faraday::Request::UrlEncoded
42
+ builder.adapter :net_http
43
+ end
44
+ auth = false
45
+ tries = 0
46
+ while not auth
47
+ token = nil
48
+ handled_error = false
49
+ begin
50
+ if @auth_method == :password
51
+ if tries != 0 && HighLine.new.ask("Would you like to try again? (y/n): ") != 'y'
52
+ return
53
+ end
54
+ if !@email.nil? && !@password.nil?
55
+ # Upgrade from previous configuration file
56
+ print "Logging in..."
57
+ begin
58
+ token = client.password.get_token(@email, @password, :raise_errors => true)
59
+ token = token.refresh!
60
+ @email = nil
61
+ @password = nil
62
+ rescue StandardError => e
63
+ @email = nil
64
+ @password = nil
65
+ tries = 0
66
+ retry
67
+ ensure
68
+ print "\r"
69
+ end
70
+ else
71
+ tries += 1
72
+ $logger.info "Please specify your %s login data" % [DeployTool::Target::EfficientCloud.cloud_name]
73
+ email = HighLine.new.ask("E-mail: ")
74
+ password = HighLine.new.ask("Password: ") {|q| q.echo = "*" }
75
+ print "Authorizing..."
76
+ begin
77
+ token = client.password.get_token(email, password, :raise_errors => true)
78
+ token = token.refresh!
79
+ ensure
80
+ print "\r"
81
+ end
82
+ puts "Authorization succeeded."
83
+ end
84
+ else
85
+ params = {:client_id => client.id,
86
+ :client_secret => client.secret,
87
+ :grant_type => 'refresh_token',
88
+ :refresh_token => @refresh_token
89
+ }
90
+ token = client.get_token(params)
91
+ end
92
+ rescue OAuth2::Error => e
93
+ handled_error = true
94
+ print "Authorization failed"
95
+ token = nil
96
+ details = MultiJson.decode(e.response.body) rescue nil
97
+ if details
98
+ puts ": #{details['error_description']}"
99
+ re_auth if details['error']
100
+ else
101
+ puts "."
102
+ end
103
+ rescue EOFError
104
+ exit 1
105
+ rescue Interrupt
106
+ exit 1
107
+ rescue StandardError => e
108
+ puts "ERROR: #{e.inspect}"
109
+ puts "\nPlease contact %s support: %s" % [EfficientCloud.cloud_name, EfficientCloud.support_email]
110
+ puts ""
111
+ end
112
+ auth = token
113
+ if not token and not handled_error
114
+ puts "Authorization failed."
25
115
  end
26
- else
27
- url.query_values = data
28
- res = Net::HTTP.get_response(url)
29
116
  end
30
- case res
31
- when Net::HTTPSuccess, Net::HTTPRedirection
32
- res.body
33
- else
34
- res.error!
117
+
118
+ @refresh_token = token.refresh_token
119
+ @auth_method = :refresh_token
120
+
121
+ response = token.request(method, url.path, method==:post ? {:body => data} : {:params => data})
122
+ if response.status != 200
123
+ details = MultiJson.decode(response.body) rescue nil
124
+ raise "#{response.status} #{details}"
35
125
  end
126
+ response
127
+ end
128
+
129
+ def to_h
130
+ {:server => @server, :app_name => @app_name, :email => email, :password => @password, :refresh_token => @refresh_token, :auth_method => @auth_method}
36
131
  end
37
132
 
38
133
  def info
39
134
  response = call :get, 'info'
40
- doc = REXML::Document.new response
135
+ return nil if not response
136
+ doc = REXML::Document.new response.body
41
137
  data = {}
42
138
  doc.elements["app"].each_element do |el|
43
139
  data[el.name.gsub('-','_').to_sym] = el.text
@@ -74,25 +170,15 @@ class DeployTool::Target::EfficientCloud
74
170
  end
75
171
 
76
172
  puts "-----> Uploading %s code tarball..." % human_filesize(tempfile.path)
77
- initial_response = call :post, 'upload', {:code => UploadIO.new(tempfile, "application/zip", "ecli-upload.zip")}
78
- doc = REXML::Document.new initial_response
173
+ initial_response = call :post, 'upload', {:code => Faraday::UploadIO.new(tempfile, "application/zip")}
174
+ doc = REXML::Document.new initial_response.body
79
175
  doc.elements["code/code-token"].text
80
- rescue Net::HTTPServerException => e
81
- case e.response
82
- when Net::HTTPNotFound
83
- $logger.error "Application %s couldn't be found." % @app_name
84
- when Net::HTTPUnauthorized
85
- $logger.error "You're not authorized to update %s." % @app_name
86
- else
87
- raise e
88
- end
89
- $logger.info "\nPlease check the controlpanel for update instructions."
90
- exit 2
91
176
  end
92
177
 
93
178
  def deploy(code_token)
94
179
  initial_response = call :post, 'deploy', {:code_token => code_token}
95
- doc = REXML::Document.new initial_response
180
+ return nil if not initial_response
181
+ doc = REXML::Document.new initial_response.body
96
182
  deploy_token = doc.elements["deploy/token"].text
97
183
  deploy_token
98
184
  end
@@ -112,7 +198,7 @@ class DeployTool::Target::EfficientCloud
112
198
  while true
113
199
  sleep 1
114
200
  resp = call :get, 'deploy_status', {:deploy_token => deploy_token}
115
- doc = REXML::Document.new resp
201
+ doc = REXML::Document.new resp.body
116
202
 
117
203
  if doc.elements["deploy/message"].nil?
118
204
  puts resp
@@ -1,3 +1,3 @@
1
1
  module DeployTool
2
- VERSION = "0.9.2"
2
+ VERSION = "0.9.4"
3
3
  end
@@ -15,29 +15,17 @@ describe DeployTool::Target do
15
15
 
16
16
  # TODO: Mock HTTP get method
17
17
  end
18
-
19
- ["api.cloudfoundry.com", "cloudfoundry.com", "awesomeapp.cloudfoundry.com"].each do |target_spec| #, "api.cloud.1and1.com", "cloud.1and1.com", "awesomeapp.cloud.1and1.com"].each do
20
- it "should detect #{target_spec} as a CloudFoundry target" do
21
- DeployTool::Target.find(target_spec).class.should == DeployTool::Target::CloudFoundry
22
- end
23
- end
24
-
25
- ["app10000@api.srv.io", "app10000@srv.io", "app10000@app123.srv.io", "app10000.srv.io"].each do |target_spec|
18
+
19
+ ["app10000@api.hostingstack.com", "app10000@hostingstack.com", "app10000@app123.hostingstack.com", "app10000.hostingstack.com"].each do |target_spec|
26
20
  it "should detect #{target_spec} as an Efficient Cloud target" do
27
21
  DeployTool::Target.find(target_spec).class.should == DeployTool::Target::EfficientCloud
28
22
  end
29
23
  end
30
-
31
- ["heroku.com", "awesomeapp.heroku.com"].each do |target_spec|
32
- it "should detect #{target_spec} as an Heroku target" do
33
- DeployTool::Target.find(target_spec).class.should == DeployTool::Target::Heroku
34
- end
35
- end
36
-
24
+
37
25
  ["gandi.net", "1and1.com"].each do |target_spec|
38
26
  it "should return an error with #{target_spec} as target" do
39
27
  DeployTool::Target.find(target_spec).class.should == NilClass
40
28
  end
41
29
  end
42
30
  end
43
- end
31
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deployto
3
3
  version: !ruby/object:Gem::Version
4
- hash: 63
5
- prerelease: false
4
+ hash: 51
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 2
10
- version: 0.9.2
9
+ - 4
10
+ version: 0.9.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Efficient Cloud Ltd
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-31 00:00:00 +02:00
19
- default_executable:
18
+ date: 2011-09-28 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: inifile
@@ -70,10 +69,12 @@ dependencies:
70
69
  requirements:
71
70
  - - ">="
72
71
  - !ruby/object:Gem::Version
73
- hash: 3
72
+ hash: 11
74
73
  segments:
75
- - 0
76
- version: "0"
74
+ - 1
75
+ - 6
76
+ - 2
77
+ version: 1.6.2
77
78
  type: :runtime
78
79
  version_requirements: *id004
79
80
  - !ruby/object:Gem::Dependency
@@ -91,7 +92,7 @@ dependencies:
91
92
  type: :runtime
92
93
  version_requirements: *id005
93
94
  - !ruby/object:Gem::Dependency
94
- name: heroku
95
+ name: json_pure
95
96
  prerelease: false
96
97
  requirement: &id006 !ruby/object:Gem::Requirement
97
98
  none: false
@@ -105,7 +106,7 @@ dependencies:
105
106
  type: :runtime
106
107
  version_requirements: *id006
107
108
  - !ruby/object:Gem::Dependency
108
- name: json_pure
109
+ name: oauth2
109
110
  prerelease: false
110
111
  requirement: &id007 !ruby/object:Gem::Requirement
111
112
  none: false
@@ -133,15 +134,12 @@ files:
133
134
  - lib/deploytool/command.rb
134
135
  - lib/deploytool/config.rb
135
136
  - lib/deploytool/target.rb
136
- - lib/deploytool/target/cloudfoundry.rb
137
137
  - lib/deploytool/target/efficientcloud.rb
138
138
  - lib/deploytool/target/efficientcloud/api_client.rb
139
- - lib/deploytool/target/heroku.rb
140
139
  - lib/deploytool/version.rb
141
140
  - spec/spec.opts
142
141
  - spec/spec_helper.rb
143
142
  - spec/target_spec.rb
144
- has_rdoc: true
145
143
  homepage: http://platformdirectory.com/
146
144
  licenses: []
147
145
 
@@ -171,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
169
  requirements: []
172
170
 
173
171
  rubyforge_project:
174
- rubygems_version: 1.3.7
172
+ rubygems_version: 1.8.8
175
173
  signing_key:
176
174
  specification_version: 3
177
175
  summary: Multi-platform deployment tool.
@@ -1,31 +0,0 @@
1
- class DeployTool::Target::CloudFoundry < DeployTool::Target
2
- def self.parse_target_spec(target_spec)
3
- app_name, server = target_spec.split('.', 2)
4
- return false if server.nil? or app_name.nil?
5
- # Test through multiple versions of API server URLs
6
- [target_spec, 'api.' + target_spec, 'api.' + server].each do |api_server|
7
- begin
8
- if get_json_resource("http://%s/info" % api_server)['name'] == "vcap"
9
- app_name = nil if server.gsub('api.') == api_server.gsub('api.')
10
- return [app_name, api_server]
11
- end
12
- rescue => e
13
- $logger.debug "Exception: %s\n%s" % [e.message, e.backtrace.join("\n")]
14
- end
15
- end
16
- false
17
- end
18
-
19
- def self.matches?(target_spec)
20
- return true if parse_target_spec(target_spec)
21
- end
22
-
23
- def initialize(options)
24
- # FIXME
25
- end
26
-
27
- def self.create(target_spec)
28
- app_name, api_server = parse_target_spec(target_spec)
29
- CloudFoundry.new('api_server' => api_server, 'app_name' => app_name)
30
- end
31
- end
@@ -1,30 +0,0 @@
1
- class DeployTool::Target::Heroku < DeployTool::Target
2
- def self.matches?(target_spec)
3
- target_spec[/(^|\.)heroku\.com$/]
4
- end
5
-
6
- def to_h
7
- {:type => "Heroku", :app_name => @app_name}
8
- end
9
-
10
- def to_s
11
- "%s.heroku.com (Heroku)" % [@app_name]
12
- end
13
-
14
- def initialize(options)
15
- @app_name = options['app_name']
16
- end
17
-
18
- def self.create(target_name)
19
- app_name = target_name.gsub('.heroku.com', '')
20
- # TODO: Require current directory to be a git repository
21
- # TODO: Ask for app name if app name is nil or www
22
- puts `heroku create #{app_name}`
23
- Heroku.new('app_name' => app_name)
24
- end
25
-
26
- def push(opts)
27
- puts `git push -f git@heroku.com:#{@app_name}.git master`
28
- $?.exitstatus
29
- end
30
- end