deployhq 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+