deployhq 1.0.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.
data/lib/deploy.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ require 'json'
6
+ require 'uri'
7
+ require 'net/http'
8
+ require 'net/https'
9
+ require 'time'
10
+
11
+ ## DeployHQ Ruby API Client
12
+ ## This is a ruby API library for the DeployHQ deployment platform.
13
+
14
+ require 'deploy/errors'
15
+ require 'deploy/request'
16
+ require 'deploy/base'
17
+
18
+ require 'deploy/project'
19
+ require 'deploy/deployment'
20
+ require 'deploy/server'
21
+
22
+
23
+ module Deploy
24
+ VERSION = '1.0.0'
25
+
26
+ class << self
27
+ ## Domain which you wish to access (e.g. atech.deployhq.com)
28
+ attr_accessor :site
29
+ ## E-Mail address you wish to authenticate with
30
+ attr_accessor :email
31
+ ## API key for the user you wish to authenticate with
32
+ attr_accessor :api_key
33
+ end
34
+ end
@@ -0,0 +1,166 @@
1
+ module Deploy
2
+ class Base
3
+
4
+ ## Store all attributes for the model we're working with.
5
+ attr_accessor :id, :attributes, :errors
6
+
7
+ ## Pass any methods via. the attributes hash to see if they exist
8
+ ## before resuming normal method_missing behaviour
9
+ def method_missing(method, *params)
10
+ set = method.to_s.include?('=')
11
+ key = method.to_s.sub('=', '')
12
+ self.attributes = Hash.new unless self.attributes.is_a?(Hash)
13
+ if set
14
+ self.attributes[key] = params.first
15
+ else
16
+ self.attributes[key]
17
+ end
18
+ end
19
+
20
+ class << self
21
+
22
+ ## Find a record or set of records. Passing :all will return all records and passing an integer
23
+ ## will return the individual record for the ID passed.
24
+ def find(type, params = {})
25
+ case type
26
+ when :all then find_all(params)
27
+ else find_single(type, params)
28
+ end
29
+ end
30
+
31
+ ## Find all objects and return an array of objects with the attributes set.
32
+ def find_all(params)
33
+ output = JSON.parse(Request.new(collection_path(params)).make.output)
34
+ if output.is_a?(Hash) && output['records'] && output['pagination']
35
+ output = output['records']
36
+ end
37
+ return [] unless output.is_a?(Array)
38
+ output.map do |o|
39
+ create_object(o, params)
40
+ end
41
+ end
42
+
43
+ ## Find a single object and return an object for it.
44
+ def find_single(id, params = {})
45
+ o = JSON.parse(Request.new(member_path(id, params)).make.output)
46
+ if o.is_a?(Hash)
47
+ create_object(o, params)
48
+ else
49
+ raise Deploy::Errors::NotFound, "Record not found"
50
+ end
51
+ end
52
+
53
+ ## Post to the specified object on the collection path
54
+ def post(path)
55
+ Request.new(path.to_s, :post).make
56
+ end
57
+
58
+ ## Return the collection path for this model. Very lazy pluralizion here
59
+ ## at the moment, nothing in Deploy needs to be pluralized with anything
60
+ ## other than just adding an 's'.
61
+ def collection_path(params = {})
62
+ class_name.downcase + 's'
63
+ end
64
+
65
+ ## Return the member path for the passed ID & attributes
66
+ def member_path(id, params = {})
67
+ [collection_path, id].join('/')
68
+ end
69
+
70
+ ## Return the deploy class name
71
+ def class_name
72
+ self.name.to_s.split('::').last.downcase
73
+ end
74
+
75
+ private
76
+
77
+ ## Create a new object with the specified attributes and getting and ID.
78
+ ## Returns the newly created object
79
+ def create_object(attributes, objects = [])
80
+ o = self.new
81
+ o.attributes = attributes
82
+ o.id = attributes['id']
83
+ for key, object in objects.select{|k,v| v.kind_of?(Deploy::Base)}
84
+ o.attributes[key.to_s] = object
85
+ end
86
+ o
87
+ end
88
+ end
89
+
90
+ ## Run a post on the member path. Returns the ouput from the post, false if a conflict or raises
91
+ ## a Deploy::Error. Optionally, pass a second 'data' parameter to send data to the post action.
92
+ def post(action, data = nil)
93
+ path = self.class.member_path(self.id, default_params) + "/" + action.to_s
94
+ request = Request.new(path, :post)
95
+ request.data = data
96
+ request.make
97
+ end
98
+
99
+ ## Delete this record from the remote service. Returns true or false depending on the success
100
+ ## status of the destruction.
101
+ def destroy
102
+ Request.new(self.class.member_path(self.id, default_params), :delete).make.success?
103
+ end
104
+
105
+ def new_record?
106
+ self.id.nil?
107
+ end
108
+
109
+ def save
110
+ new_record? ? create : update
111
+ end
112
+
113
+ def create
114
+ request = Request.new(self.class.collection_path(default_params), :post)
115
+ request.data = {self.class.class_name.downcase.to_sym => attributes_to_post}
116
+ if request.make && request.success?
117
+ new_record = JSON.parse(request.output)
118
+ self.attributes = new_record
119
+ self.identifier = new_record['identifier']
120
+ true
121
+ else
122
+ populate_errors(request.output)
123
+ false
124
+ end
125
+ end
126
+
127
+ ## Push the updated attributes to the remote. Returns true if the record was saved successfully
128
+ ## other false if not. If not saved successfully, the errors hash will be updated with an array
129
+ ## of all errors with the submission.
130
+ def update
131
+ request = Request.new(self.class.member_path(self.id, default_params), :put)
132
+ request.data = {self.class.class_name.downcase.to_sym => attributes_to_post}
133
+ if request.make && request.success?
134
+ true
135
+ else
136
+ populate_errors(request.output)
137
+ false
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ ## Populate the errors hash from the given raw JSON output
144
+ def populate_errors(json)
145
+ self.errors = Hash.new
146
+ JSON.parse(json).inject(self.errors) do |r, e|
147
+ r[e.first] = e.last
148
+ r
149
+ end
150
+ end
151
+
152
+ ## An array of params which should always be sent with this instances requests
153
+ def default_params
154
+ Hash.new
155
+ end
156
+
157
+ ## Attributes which can be passed for update & creation
158
+ def attributes_to_post
159
+ self.attributes.inject(Hash.new) do |r,(key,value)|
160
+ r[key] = value if value.is_a?(String) || value.is_a?(Integer) || value.is_a?(Fixnum)
161
+ r
162
+ end
163
+ end
164
+
165
+ end
166
+ end
@@ -0,0 +1,19 @@
1
+ module Deploy
2
+ class Deployment < Base
3
+
4
+ class << self
5
+ def collection_path(params = {})
6
+ "projects/#{params[:project].permalink}/deployments"
7
+ end
8
+
9
+ def member_path(id, params = {})
10
+ "projects/#{params[:project].permalink}/deployments/#{id}"
11
+ end
12
+ end
13
+
14
+ def default_params
15
+ {:project => self.project}
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Deploy
2
+
3
+ ## Base level error which all other deploy errors will inherit from. It may also be
4
+ ## invoked for errors which don't directly relate to other errors below.
5
+ class Error < StandardError; end
6
+
7
+ module Errors
8
+
9
+ ## The service is currently unavailable. This may be caused by rate limiting or the API
10
+ ## or the service has been disabled by the system
11
+ class ServiceUnavailable < Error; end
12
+
13
+ ## Access was denied to the remote service
14
+ class AccessDenied < Error; end
15
+
16
+ ## A communication error occured while talking to the Deploy API
17
+ class CommunicationError < Error; end
18
+
19
+ end
20
+ end
@@ -0,0 +1,49 @@
1
+ module Deploy
2
+ class Project < Base
3
+
4
+ ## Return all deployments for this project
5
+ def deployments
6
+ Deployment.find(:all, :project => self)
7
+ end
8
+
9
+ ## Return a deployment
10
+ def deployment(identifier)
11
+ Deployment.find(identifier, :project => self)
12
+ end
13
+
14
+ ## Create a deployment in this project (and queue it to run)
15
+ def deploy(server, start_revision, end_revision)
16
+ run_deployment(server, start_revision, end_revision) do |d|
17
+ d.mode = 'queue'
18
+ end
19
+ end
20
+
21
+ ##
22
+ def preview(server, start_revision, end_revision)
23
+ run_deployment(server, start_revision, end_revision) do |d|
24
+ d.mode = 'preview'
25
+ end
26
+ end
27
+
28
+ ## Return all servers for this project
29
+ def servers
30
+ Server.find(:all, :project => self)
31
+ end
32
+
33
+ private
34
+
35
+ def run_deployment(server, start_revision, end_revision, &block)
36
+ d = Deployment.new
37
+ d.project = self
38
+ d.server_identifier = (server.is_a?(Server) ? server.identifier : server)
39
+ d.start_revision = start_revision
40
+ d.end_revision = end_revision
41
+ d.copy_config_files = '1'
42
+ d.email_notify = '1'
43
+ block.call(d) if block_given?
44
+ d.save
45
+ d
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,68 @@
1
+ module Deploy
2
+ class Request
3
+
4
+ attr_reader :path, :method
5
+ attr_accessor :data
6
+
7
+ def initialize(path, method = :get)
8
+ @path = path
9
+ @method = method
10
+ end
11
+
12
+ def success?
13
+ @success || false
14
+ end
15
+
16
+ def output
17
+ @output || nil
18
+ end
19
+
20
+ ## Make a request to the Deploy API using net/http. Data passed can be a hash or a string
21
+ ## Hashes will be converted to JSON before being sent to the remote service.
22
+ def make
23
+ uri = URI.parse([Deploy.site, @path].join('/'))
24
+ http_request = http_class.new(uri.path)
25
+ http_request.basic_auth(Deploy.email, Deploy.api_key)
26
+ http_request.add_field("Accept", "application/json")
27
+ http_request.add_field("Content-type", "application/json")
28
+
29
+ http = Net::HTTP.new(uri.host, uri.port)
30
+ if uri.scheme == 'https'
31
+ http.use_ssl = true
32
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
33
+ end
34
+
35
+ data = self.data.to_json if self.data.is_a?(Hash) && self.data.respond_to?(:to_json)
36
+ http_result = http.request(http_request, data)
37
+ @output = http_result.body
38
+ @success = case http_result
39
+ when Net::HTTPSuccess
40
+ true
41
+ when Net::HTTPServiceUnavailable
42
+ raise Deploy::Errors::ServiceUnavailable
43
+ when Net::HTTPForbidden, Net::HTTPUnauthorized
44
+ raise Deploy::Errors::AccessDenied, "Access Denied for '#{Deploy.email}'"
45
+ when Net::HTTPNotFound
46
+ raise Deploy::Errors::CommunicationError, "Not Found at #{uri.to_s}"
47
+ when Net::HTTPClientError
48
+ false
49
+ else
50
+ raise Deploy::Errors::CommunicationError, http_result.body
51
+ end
52
+ self
53
+ end
54
+
55
+ private
56
+
57
+ def http_class
58
+ case @method
59
+ when :post then Net::HTTP::Post
60
+ when :put then Net::HTTP::Put
61
+ when :delete then Net::HTTP::Delete
62
+ else
63
+ Net::HTTP::Get
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,19 @@
1
+ module Deploy
2
+ class Server < Base
3
+
4
+ class << self
5
+ def collection_path(params = {})
6
+ "projects/#{params[:project].permalink}/servers"
7
+ end
8
+
9
+ def member_path(id, params = {})
10
+ "projects/#{params[:project].permalink}/servers/#{identifier}"
11
+ end
12
+ end
13
+
14
+ def default_params
15
+ {:project => self.project}
16
+ end
17
+
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deployhq
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Adam Cooke
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-24 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 4
30
+ - 6
31
+ version: 1.4.6
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description:
35
+ email: adam@atechmedia.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ files:
43
+ - lib/deploy/base.rb
44
+ - lib/deploy/deployment.rb
45
+ - lib/deploy/errors.rb
46
+ - lib/deploy/project.rb
47
+ - lib/deploy/request.rb
48
+ - lib/deploy/server.rb
49
+ - lib/deploy.rb
50
+ has_rdoc: true
51
+ homepage: http://www.deployhq.com
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.6
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: API client for the DeployHQ deployment platform
80
+ test_files: []
81
+