deployhq 1.1.0 → 1.3.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 889f38a4af08698a39c5f067aaa861ba7df6ef6b
4
+ data.tar.gz: 77791a4866fb8c7ede8a40f3d843679763e06283
5
+ SHA512:
6
+ metadata.gz: fbb92bf30ef4ec9699c37f2fac34b818a284ca60c8b1dd26b76b874f78402b5af24170f08519247ef30a2547b9b689f2c67e91f8357eff870e92257b2d268fd8
7
+ data.tar.gz: c2901bef7a41b21b7fbb51de8b70790eadd15a71f85ddb112a070c37b14c0b67317c063af0dc8198e5dd6c300104948d880fc81978d96f89a0e9d6ad4b6c518d
data/bin/deployhq ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'deploy/cli'
4
+
5
+ Deploy::CLI.invoke(*ARGV)
data/lib/deploy/cli.rb ADDED
@@ -0,0 +1,194 @@
1
+ require 'optparse'
2
+ require 'highline/import'
3
+ require 'deploy'
4
+
5
+ HighLine.colorize_strings
6
+
7
+ module Deploy
8
+ class CLI
9
+
10
+ ## Constants for formatting output
11
+ TAP_COLOURS = {:info => :yellow, :error => :red, :success => :green}
12
+ PROTOCOL_NAME = {:ssh => "SSH/SFTP", :ftp => "FTP", :s3 => "Amazon S3", :rackspace => "Rackspace CloudFiles"}
13
+
14
+ class Config
15
+ AVAILABLE_CONFIG = [:account, :username, :api_key, :project]
16
+
17
+ def initialize(config_file=nil)
18
+ @config_file_path = config_file || File.join(Dir.pwd, 'Deployfile')
19
+ @config = JSON.parse(File.read(@config_file_path))
20
+ rescue Errno::ENOENT => e
21
+ puts "Couldn't find configuration file at #{@config_file_path}"
22
+ exit 1
23
+ end
24
+
25
+ def method_missing(meth, *args, &block)
26
+ if AVAILABLE_CONFIG.include?(meth.to_sym)
27
+ @config[meth.to_s]
28
+ else
29
+ super
30
+ end
31
+ end
32
+ end
33
+
34
+ class << self
35
+
36
+ def invoke(*args)
37
+ options = {}
38
+ OptionParser.new do |opts|
39
+ opts.banner = "Usage: deployhq [command]"
40
+ opts.on("-c", "--config", 'Configuration file path') do |v|
41
+ options[:config_file] = v
42
+ end
43
+ end.parse!
44
+
45
+ @configuration = Config.new(options[:config_file])
46
+ Deploy.site = @configuration.account
47
+ Deploy.email = @configuration.username
48
+ Deploy.api_key = @configuration.api_key
49
+ @project = Deploy::Project.find(@configuration.project)
50
+
51
+ case args[0]
52
+ when 'deploy'
53
+ deploy
54
+ when 'servers'
55
+ server_list
56
+ else
57
+ puts "Usage: deployhq [command]"
58
+ return
59
+ end
60
+ end
61
+
62
+ def server_list
63
+ @server_groups ||= @project.server_groups
64
+ if @server_groups.count > 0
65
+ @server_groups.each do |group|
66
+ puts "Group: #{group.name}".bold
67
+ puts group.servers.map {|server| format_server(server) }.join("\n\n")
68
+ end
69
+ end
70
+
71
+ @ungrouped_servers ||= @project.servers
72
+ if @ungrouped_servers.count > 0
73
+ puts "\n" if @server_groups.count > 0
74
+ puts "Ungrouped Servers".bold
75
+ puts @ungrouped_servers.map {|server| format_server(server) }.join("\n\n")
76
+ end
77
+ end
78
+
79
+ def deploy
80
+ @ungrouped_servers = @project.servers
81
+ @server_groups = @project.server_groups
82
+
83
+ parent = nil
84
+ while parent.nil?
85
+ parent = choose do |menu|
86
+ menu.prompt = "Please choose a server or group to deploy to:"
87
+
88
+ menu.choices(*(@ungrouped_servers + @server_groups))
89
+ menu.choice("List Server Details") do
90
+ server_list
91
+ nil
92
+ end
93
+ end
94
+ end
95
+
96
+ latest_revision = @project.latest_revision(parent.preferred_branch)
97
+ @deployment = @project.deploy(parent.identifier, parent.last_revision, latest_revision)
98
+
99
+ @server_names = @deployment.servers.each_with_object({}) do |server, hsh|
100
+ hsh[server['id']] = server['name']
101
+ end
102
+ @longest_server_name = @server_names.values.map(&:length).max
103
+
104
+ last_tap = nil
105
+ current_status = 'pending'
106
+ previous_status = ''
107
+ print "Waiting for deployment capacity..."
108
+ while ['running', 'pending'].include?(current_status) do
109
+ sleep 1
110
+
111
+ poll = @deployment.status_poll(:since => last_tap, :status => current_status)
112
+
113
+ # Status only gets returned from the API if it has changed
114
+ current_status = poll.status if poll.status
115
+
116
+ if current_status == 'pending'
117
+ print "."
118
+ elsif current_status == 'running' && previous_status == 'pending'
119
+ puts "\n"
120
+ end
121
+
122
+ if current_status != 'pending'
123
+ poll.taps.each do |tap|
124
+ puts format_tap(tap)
125
+ last_tap = tap.id.to_i
126
+ end
127
+ end
128
+
129
+ previous_status = current_status
130
+ end
131
+ end
132
+
133
+ def deployment
134
+ @deployment = @project.deployments.first
135
+ @server_names = @deployment.servers.each_with_object({}) do |obj, hsh|
136
+ hsh[obj.delete("id")] = obj["name"]
137
+ end
138
+ @longest_server_name = @server_names.values.map(&:length).max
139
+
140
+ @deployment.taps.reverse.each do |tap|
141
+ puts format_tap(tap)
142
+ end
143
+ end
144
+
145
+
146
+ ## Data formatters
147
+
148
+ def format_tap(tap)
149
+ server_name = @server_names[tap.server_id]
150
+
151
+ if server_name
152
+ padding = (@longest_server_name - server_name.length) / 2.0
153
+ server_name = "[#{' ' * padding.ceil} #{server_name} #{' ' * padding.floor}]"
154
+ else
155
+ server_name = ' '
156
+ end
157
+
158
+ text_colour = TAP_COLOURS[tap.tap_type.to_sym] || :white
159
+
160
+ String.new.tap do |s|
161
+ s << "#{server_name} ".color(text_colour, :bold)
162
+ s << tap.message.color(text_colour)
163
+ end
164
+ end
165
+
166
+ def format_server(server)
167
+ server_params = {
168
+ "Name" => server.name,
169
+ "Type" => PROTOCOL_NAME[server.protocol_type.to_sym],
170
+ "Path" => server.server_path,
171
+ "Branch" => server.preferred_branch,
172
+ "Current Revision" => server.last_revision,
173
+ }
174
+ server_params["Hostname"] = [server.hostname, server.port].join(':') if server.hostname
175
+ server_params["Bucket"] = server.bucket_name if server.bucket_name
176
+ server_params["Region"] = server.region if server.region
177
+ server_params["Container"] = server.container_name if server.container_name
178
+
179
+ Array.new.tap do |a|
180
+ a << format_kv_pair(server_params)
181
+ end.join("\n")
182
+ end
183
+
184
+ def format_kv_pair(hash)
185
+ longest_key = hash.keys.map(&:length).max + 2
186
+ hash.each_with_index.map do |(k,v), i|
187
+ str = sprintf("%#{longest_key}s : %s", k,v)
188
+ i == 0 ? str.color(:bold) : str
189
+ end.join("\n")
190
+ end
191
+
192
+ end
193
+ end
194
+ end
@@ -1,19 +1,36 @@
1
1
  module Deploy
2
2
  class Deployment < Base
3
-
3
+
4
4
  class << self
5
5
  def collection_path(params = {})
6
6
  "projects/#{params[:project].permalink}/deployments"
7
7
  end
8
-
8
+
9
9
  def member_path(id, params = {})
10
10
  "projects/#{params[:project].permalink}/deployments/#{id}"
11
11
  end
12
12
  end
13
-
13
+
14
14
  def default_params
15
15
  {:project => self.project}
16
16
  end
17
-
17
+
18
+ def project
19
+ if self.attributes['project'].is_a?(Hash)
20
+ self.attributes['project'] = Project.send(:create_object, self.attributes['project'])
21
+ end
22
+ self.attributes['project']
23
+ end
24
+
25
+ def taps(params={})
26
+ params = {:deployment => self, :project => self.project}.merge(params)
27
+ DeploymentTap.find(:all, params)
28
+ end
29
+
30
+ def status_poll(params = {})
31
+ params = {:deployment => self, :project => self.project}.merge(params)
32
+ DeploymentStatusPoll.poll(params)
33
+ end
34
+
18
35
  end
19
- end
36
+ end
@@ -0,0 +1,34 @@
1
+ module Deploy
2
+ class DeploymentStatusPoll
3
+ attr_accessor :attributes
4
+
5
+ def initialize(parsed_json)
6
+ self.attributes = parsed_json
7
+ end
8
+
9
+ def status
10
+ @status ||= attributes['status']
11
+ end
12
+
13
+ def taps
14
+ return [] unless attributes['taps']
15
+ @taps ||= attributes['taps'].map { |t| DeploymentTap.send(:create_object, t) }
16
+ end
17
+
18
+ class << self
19
+ def poll_url(params)
20
+ base = "projects/#{params[:project].permalink}/deployments/#{params[:deployment].identifier}/logs/poll"
21
+ base += "?status=#{params[:status]}"
22
+ base += "&since=#{params[:since]}" if params[:since]
23
+ base
24
+ end
25
+
26
+ def poll(params = {})
27
+ req = Request.new(poll_url(params)).make
28
+ parsed = JSON.parse(req.output)
29
+
30
+ new(parsed)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ module Deploy
2
+ class DeploymentTap < Base
3
+
4
+ class << self
5
+ def collection_path(params = {})
6
+ base = "projects/#{params[:project].permalink}/deployments/#{params[:deployment].identifier}.js"
7
+ base += "?since=#{params[:since]}" if params[:since]
8
+ base
9
+ end
10
+ end
11
+
12
+ def default_params
13
+ {:deployment => self.deployment, :project => self.deployment.project}
14
+ end
15
+
16
+ end
17
+ end
@@ -1,23 +1,30 @@
1
1
  module Deploy
2
2
  class Project < Base
3
-
3
+
4
4
  ## Return all deployments for this project
5
5
  def deployments
6
6
  Deployment.find(:all, :project => self)
7
7
  end
8
-
8
+
9
9
  ## Return a deployment
10
10
  def deployment(identifier)
11
11
  Deployment.find(identifier, :project => self)
12
12
  end
13
-
13
+
14
+ def latest_revision(branch = '')
15
+ branch ||= 'master'
16
+ req = Request.new(self.class.member_path(self.permalink) + "/repository/latest_revision?branch=#{branch}").make
17
+ parsed = JSON.parse(req.output)
18
+ parsed['ref']
19
+ end
20
+
14
21
  ## Create a deployment in this project (and queue it to run)
15
22
  def deploy(server, start_revision, end_revision)
16
23
  run_deployment(server, start_revision, end_revision) do |d|
17
24
  d.mode = 'queue'
18
25
  end
19
26
  end
20
-
27
+
21
28
  ##
22
29
  def preview(server, start_revision, end_revision)
23
30
  run_deployment(server, start_revision, end_revision) do |d|
@@ -29,9 +36,13 @@ module Deploy
29
36
  def servers
30
37
  Server.find(:all, :project => self)
31
38
  end
32
-
39
+
40
+ def server_groups
41
+ ServerGroup.find(:all, :project => self)
42
+ end
43
+
33
44
  private
34
-
45
+
35
46
  def run_deployment(server, start_revision, end_revision, &block)
36
47
  d = Deployment.new
37
48
  d.project = self
@@ -44,6 +55,6 @@ module Deploy
44
55
  d.save
45
56
  d
46
57
  end
47
-
58
+
48
59
  end
49
- end
60
+ end
@@ -21,7 +21,7 @@ module Deploy
21
21
  ## Hashes will be converted to JSON before being sent to the remote service.
22
22
  def make
23
23
  uri = URI.parse([Deploy.site, @path].join('/'))
24
- http_request = http_class.new(uri.path)
24
+ http_request = http_class.new(uri.request_uri)
25
25
  http_request.basic_auth(Deploy.email, Deploy.api_key)
26
26
  http_request["Accept"] = "application/json"
27
27
  http_request["Content-type"] = "application/json"
data/lib/deploy/server.rb CHANGED
@@ -14,6 +14,18 @@ module Deploy
14
14
  def default_params
15
15
  {:project => self.project}
16
16
  end
17
+
18
+ def to_s
19
+ Array.new.tap do |a|
20
+ a << self.name
21
+ a << "(branch: #{self.preferred_branch})" if self.preferred_branch
22
+ if self.last_revision
23
+ a << "(currently: #{self.last_revision})"
24
+ else
25
+ a << "(currently undeployed)"
26
+ end
27
+ end.join(' ')
28
+ end
17
29
 
18
30
  end
19
31
  end
@@ -0,0 +1,35 @@
1
+ module Deploy
2
+ class ServerGroup < Base
3
+
4
+ class << self
5
+ def collection_path(params = {})
6
+ "projects/#{params[:project].permalink}/server_groups"
7
+ end
8
+
9
+ def member_path(id, params = {})
10
+ "projects/#{params[:project].permalink}/server_groups/#{identifier}"
11
+ end
12
+ end
13
+
14
+ def default_params
15
+ {:project => self.project}
16
+ end
17
+
18
+ def servers
19
+ @servers ||= self.attributes['servers'].map {|server_attr| Deploy::Server.send(:create_object, server_attr) }
20
+ end
21
+
22
+ def to_s
23
+ Array.new.tap do |a|
24
+ a << self.name
25
+ a << "(branch: #{self.preferred_branch})" if self.preferred_branch
26
+ if self.last_revision
27
+ a << "(currently: #{self.last_revision})"
28
+ else
29
+ a << "(currently undeployed)"
30
+ end
31
+ end.join(' ')
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Deploy
2
+ VERSION = "1.3.0"
3
+ end
data/lib/deploy.rb CHANGED
@@ -17,12 +17,14 @@ require 'deploy/base'
17
17
 
18
18
  require 'deploy/project'
19
19
  require 'deploy/deployment'
20
+ require 'deploy/deployment_tap'
21
+ require 'deploy/deployment_status_poll'
20
22
  require 'deploy/server'
23
+ require 'deploy/server_group'
24
+ require 'deploy/version'
21
25
 
22
26
 
23
27
  module Deploy
24
- VERSION = '1.0.0'
25
-
26
28
  class << self
27
29
  ## Domain which you wish to access (e.g. atech.deployhq.com)
28
30
  attr_accessor :site
metadata CHANGED
@@ -1,67 +1,87 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deployhq
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 1.3.0
6
5
  platform: ruby
7
6
  authors:
8
- - Adam Cooke
7
+ - Dan Wentworth
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-09-03 00:00:00.000000000 Z
11
+ date: 2016-04-14 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: json
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: 1.4.6
19
+ version: '1.8'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: 1.4.6
30
- description:
31
- email: adam@atechmedia.com
32
- executables: []
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ description: |2
42
+ API and CLI client for the DeployHQ deployment platform. Provides the
43
+ deployhq executable.
44
+ email: dan@atech.io
45
+ executables:
46
+ - deployhq
33
47
  extensions: []
34
48
  extra_rdoc_files: []
35
49
  files:
50
+ - bin/deployhq
51
+ - lib/deploy.rb
36
52
  - lib/deploy/base.rb
53
+ - lib/deploy/cli.rb
37
54
  - lib/deploy/deployment.rb
55
+ - lib/deploy/deployment_status_poll.rb
56
+ - lib/deploy/deployment_tap.rb
38
57
  - lib/deploy/errors.rb
39
58
  - lib/deploy/project.rb
40
59
  - lib/deploy/request.rb
41
60
  - lib/deploy/server.rb
42
- - lib/deploy.rb
43
- homepage: http://www.deployhq.com
44
- licenses: []
61
+ - lib/deploy/server_group.rb
62
+ - lib/deploy/version.rb
63
+ homepage: https://www.deployhq.com
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
45
67
  post_install_message:
46
68
  rdoc_options: []
47
69
  require_paths:
48
70
  - lib
49
71
  required_ruby_version: !ruby/object:Gem::Requirement
50
- none: false
51
72
  requirements:
52
- - - ! '>='
73
+ - - ">="
53
74
  - !ruby/object:Gem::Version
54
75
  version: '0'
55
76
  required_rubygems_version: !ruby/object:Gem::Requirement
56
- none: false
57
77
  requirements:
58
- - - ! '>='
78
+ - - ">="
59
79
  - !ruby/object:Gem::Version
60
80
  version: '0'
61
81
  requirements: []
62
82
  rubyforge_project:
63
- rubygems_version: 1.8.24
83
+ rubygems_version: 2.4.5.1
64
84
  signing_key:
65
- specification_version: 3
66
- summary: API client for the DeployHQ deployment platform
85
+ specification_version: 4
86
+ summary: API and CLI client for the DeployHQ
67
87
  test_files: []