recipiez 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Recipiez
2
- VERSION = '0.3.7'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -1,6 +1,4 @@
1
1
  $LOAD_PATH << File.dirname(__FILE__) + '/../lib'
2
- require 'basecamp_notifier'
3
- require 'activecollab_notifier'
4
2
 
5
3
  # A collection of reusable deployment tasks.
6
4
  # Hook them into your deploy script with the *after* function.
@@ -30,26 +28,7 @@ namespace :recipiez do
30
28
  run "cp #{release_path}/config/settings.yml.#{rails_env} #{release_path}/config/settings.yml"
31
29
  end
32
30
 
33
- # You need to add the below line to your deploy.rb
34
- # set :basecamp_auth, {:username => 'robot', :domain => 'ifo.projectpath.com',
35
- # :password => 'robot', :project_id => 828898,
36
- # :category_id => 15509}
37
- desc "Update basecamp with information of the deployment."
38
- task :update_basecamp do
39
- basecamp_notifier = BasecampNotifier.new(application, rails_env,
40
- basecamp_auth, current_revision, get_rev_log)
41
- basecamp_notifier.notify
42
- end
43
31
 
44
- # You need to add the below line to your deploy.rb
45
- # set :activecollab_options, {:base_url => "http://projects.bla.com",
46
- # :project_id => 5, :ticket_id => 10,
47
- # :username => 'bla', :password => 'bla'}
48
- desc "Update activecollab with information of the deployment."
49
- task :update_activecollab do
50
- notifier = ActiveCollab::Notifier.new(activecollab_options)
51
- notifier.say(get_rev_log)
52
- end
53
32
 
54
33
  desc "This gets the revision log"
55
34
  task :get_rev_log do
data/recipiez.gemspec CHANGED
@@ -14,10 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.homepage = "http://github.com/pyrat/deployment_recipiez"
15
15
  s.summary = "Capistrano recipies for DB Syncing, Logrotate, Apache, Thin, Basecamp, Activecollab, Monit, NodeJS, Nginx"
16
16
  s.description = "Capistrano recipies for DB Syncing, Logrotate, Apache, Thin, Basecamp, Activecollab, Monit, NodeJS, Nginx"
17
-
18
- s.add_dependency('xml-simple')
19
- s.add_dependency('mechanize')
20
-
17
+
21
18
  s.required_rubygems_version = ">= 1.3.1"
22
19
  s.require_path = 'lib'
23
20
  s.files = `git ls-files`.split("\n")
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recipiez
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 7
10
- version: 0.3.7
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Alastair Brunton
@@ -15,37 +15,10 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-08-31 00:00:00 +02:00
18
+ date: 2012-10-15 00:00:00 +02:00
19
19
  default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
22
- name: xml-simple
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
- version: "0"
33
- type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: mechanize
37
- prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- hash: 3
44
- segments:
45
- - 0
46
- version: "0"
47
- type: :runtime
48
- version_requirements: *id002
20
+ dependencies: []
21
+
49
22
  description: Capistrano recipies for DB Syncing, Logrotate, Apache, Thin, Basecamp, Activecollab, Monit, NodeJS, Nginx
50
23
  email:
51
24
  - info@simplyexcited.co.uk
@@ -59,9 +32,6 @@ files:
59
32
  - .gitignore
60
33
  - MIT-LICENSE
61
34
  - README.md
62
- - lib/activecollab_notifier.rb
63
- - lib/basecamp.rb
64
- - lib/basecamp_notifier.rb
65
35
  - lib/recipiez/capistrano.rb
66
36
  - lib/recipiez/version.rb
67
37
  - recipes/apache.rb
@@ -1,58 +0,0 @@
1
- require 'mechanize'
2
-
3
- module ActiveCollab
4
-
5
- class Notifier
6
-
7
- def initialize(options = {})
8
-
9
- if keys_missing?(options)
10
- raise StandardError, "You have not supplied all the required arguments in the hash. (base_url, project_id, ticket_id, email, password)"
11
- end
12
-
13
- @base_url = options[:base_url]
14
- @project_id = options[:project_id]
15
- @ticket_id = options[:ticket_id]
16
- @mech = Mechanize.new
17
- @email = options[:email]
18
- @password = options[:password]
19
- end
20
-
21
- def say(message)
22
- login
23
- submit_ticket_comment(message)
24
- end
25
-
26
- def keys_missing?(options)
27
- [:base_url, :project_id, :ticket_id, :email, :password].each do |key|
28
- return true unless options.has_key?(key)
29
- end
30
- false
31
- end
32
-
33
- private
34
-
35
- def login
36
-
37
- @mech.get(@base_url + "/login") do |login_page|
38
- form = login_page.forms.first
39
-
40
- form['login[email]'] = @email
41
- form['login[password]'] = @password
42
-
43
- form.click_button
44
- end
45
- end
46
-
47
- def submit_ticket_comment(message)
48
- @mech.get(@base_url + "/projects/#{@project_id}/tickets/#{@ticket_id}") do |page|
49
- page.form_with(:action => /\/projects\/#{@project_id}\/comments\/add/) do |comment_form|
50
- comment_form['comment[body]'] = message
51
- comment_form.click_button
52
- end
53
- end
54
- end
55
-
56
- end
57
-
58
- end
data/lib/basecamp.rb DELETED
@@ -1,486 +0,0 @@
1
- # the following are all standard ruby libraries
2
- require 'net/https'
3
- require 'yaml'
4
- require 'date'
5
- require 'time'
6
-
7
- begin
8
- require 'rubygems'
9
- require 'xmlsimple'
10
- rescue LoadError
11
- abort <<-ERROR
12
- The 'xml-simple' library could not be loaded. If you have RubyGems installed
13
- you can install xml-simple by doing "gem install xml-simple".
14
- ERROR
15
- end
16
-
17
- # An interface to the Basecamp web-services API. Usage is straightforward:
18
- #
19
- # session = Basecamp.new('your.basecamp.com', 'username', 'password')
20
- # puts "projects: #{session.projects.length}"
21
- class Basecamp
22
-
23
- # A wrapper to encapsulate the data returned by Basecamp, for easier access.
24
- class Record #:nodoc:
25
- attr_reader :type
26
-
27
- def initialize(type, hash)
28
- @type = type
29
- @hash = hash
30
- end
31
-
32
- def [](name)
33
- name = dashify(name)
34
- case @hash[name]
35
- when Hash then
36
- @hash[name] = (@hash[name].keys.length == 1 && Array === @hash[name].values.first) ?
37
- @hash[name].values.first.map { |v| Record.new(@hash[name].keys.first, v) } :
38
- Record.new(name, @hash[name])
39
- else @hash[name]
40
- end
41
- end
42
-
43
- def id
44
- @hash["id"]
45
- end
46
-
47
- def attributes
48
- @hash.keys
49
- end
50
-
51
- def respond_to?(sym)
52
- super || @hash.has_key?(dashify(sym))
53
- end
54
-
55
- def method_missing(sym, *args)
56
- if args.empty? && !block_given? && respond_to?(sym)
57
- self[sym]
58
- else
59
- super
60
- end
61
- end
62
-
63
- def to_s
64
- "\#<Record(#{@type}) #{@hash.inspect[1..-2]}>"
65
- end
66
-
67
- def inspect
68
- to_s
69
- end
70
-
71
- private
72
-
73
- def dashify(name)
74
- name.to_s.tr("_", "-")
75
- end
76
- end
77
-
78
- # A wrapper to represent a file that should be uploaded. This is used so that
79
- # the form/multi-part encoder knows when to encode a field as a file, versus
80
- # when to encode it as a simple field.
81
- class FileUpload
82
- attr_reader :filename, :content
83
-
84
- def initialize(filename, content)
85
- @filename = filename
86
- @content = content
87
- end
88
- end
89
-
90
- attr_accessor :use_xml
91
-
92
- # Connects
93
- def initialize(url, user_name, password, use_ssl = false)
94
- @use_xml = false
95
- @user_name, @password = user_name, password
96
- connect!(url, use_ssl)
97
- end
98
-
99
- # Return the list of all accessible projects.
100
- def projects
101
- records "project", "/project/list"
102
- end
103
-
104
- # Returns the list of message categories for the given project
105
- def message_categories(project_id)
106
- records "post-category", "/projects/#{project_id}/post_categories"
107
- end
108
-
109
- # Returns the list of file categories for the given project
110
- def file_categories(project_id)
111
- records "attachment-category", "/projects/#{project_id}/attachment_categories"
112
- end
113
-
114
- # Return information for the company with the given id
115
- def company(id)
116
- record "/contacts/company/#{id}"
117
- end
118
-
119
- # Return an array of the people in the given company. If the project-id is
120
- # given, only people who have access to the given project will be returned.
121
- def people(company_id, project_id=nil)
122
- url = project_id ? "/projects/#{project_id}" : ""
123
- url << "/contacts/people/#{company_id}"
124
- records "person", url
125
- end
126
-
127
- # Return information about the person with the given id
128
- def person(id)
129
- record "/contacts/person/#{id}"
130
- end
131
-
132
- # Return information about the message(s) with the given id(s). The API
133
- # limits you to requesting 25 messages at a time, so if you need to get more
134
- # than that, you'll need to do it in multiple requests.
135
- def message(*ids)
136
- result = records("post", "/msg/get/#{ids.join(",")}")
137
- result.length == 1 ? result.first : result
138
- end
139
-
140
- # Returns a summary of all messages in the given project (and category, if
141
- # specified). The summary is simply the title and category of the message,
142
- # as well as the number of attachments (if any).
143
- def message_list(project_id, category_id=nil)
144
- url = "/projects/#{project_id}/msg"
145
- url << "/cat/#{category_id}" if category_id
146
- url << "/archive"
147
-
148
- records "post", url
149
- end
150
-
151
- # Create a new message in the given project. The +message+ parameter should
152
- # be a hash. The +email_to+ parameter must be an array of person-id's that
153
- # should be notified of the post.
154
- #
155
- # If you want to add attachments to the message, the +attachments+ parameter
156
- # should be an array of hashes, where each has has a :name key (optional),
157
- # and a :file key (required). The :file key must refer to a Basecamp::FileUpload
158
- # instance.
159
- #
160
- # msg = session.post_message(158141,
161
- # { :title => "Requirements",
162
- # :body => "Here are the requirements documents you asked for.",
163
- # :category_id => 2301121 },
164
- # [john.id, martha.id],
165
- # [ { :name => "Primary Requirements",
166
- # :file => Basecamp::FileUpload.new('primary.doc", File.read('primary.doc')) },
167
- # { :file => Basecamp::FileUpload.new('other.doc', File.read('other.doc')) } ])
168
- def post_message(project_id, message, notify=[], attachments=[])
169
- prepare_attachments(attachments)
170
- record "/projects/#{project_id}/msg/create",
171
- :post => message,
172
- :notify => notify,
173
- :attachments => attachments
174
- end
175
-
176
- # Edit the message with the given id. The +message+ parameter should
177
- # be a hash. The +email_to+ parameter must be an array of person-id's that
178
- # should be notified of the post.
179
- #
180
- # The +attachments+ parameter, if used, should be the same as described for
181
- # #post_message.
182
- def update_message(id, message, notify=[], attachments=[])
183
- prepare_attachments(attachments)
184
- record "/msg/update/#{id}",
185
- :post => message,
186
- :notify => notify,
187
- :attachments => attachments
188
- end
189
-
190
- # Deletes the message with the given id, and returns it.
191
- def delete_message(id)
192
- record "/msg/delete/#{id}"
193
- end
194
-
195
- # Return a list of the comments for the specified message.
196
- def comments(post_id)
197
- records "comment", "/msg/comments/#{post_id}"
198
- end
199
-
200
- # Retrieve a specific comment
201
- def comment(id)
202
- record "/msg/comment/#{id}"
203
- end
204
-
205
- # Add a new comment to a message. +comment+ must be a hash describing the
206
- # comment. You can add attachments to the comment, too, by giving them in
207
- # an array. See the #post_message method for a description of how to do that.
208
- def create_comment(post_id, comment, attachments=[])
209
- prepare_attachments(attachments)
210
- record "/msg/create_comment", :comment => comment.merge(:post_id => post_id),
211
- :attachments => attachments
212
- end
213
-
214
- # Update the given comment. Attachments follow the same format as #post_message.
215
- def update_comment(id, comment, attachments=[])
216
- prepare_attachments(attachments)
217
- record "/msg/update_comment", :comment_id => id,
218
- :comment => comment, :attachments => attachments
219
- end
220
-
221
- # Deletes (and returns) the given comment.
222
- def delete_comment(id)
223
- record "/msg/delete_comment/#{id}"
224
- end
225
-
226
- # =========================================================================
227
- # TODO LISTS AND ITEMS
228
- # =========================================================================
229
-
230
- # Marks the given item completed.
231
- def complete_item(id)
232
- record "/todos/complete_item/#{id}"
233
- end
234
-
235
- # Marks the given item uncompleted.
236
- def uncomplete_item(id)
237
- record "/todos/uncomplete_item/#{id}"
238
- end
239
-
240
- # Creates a new to-do item.
241
- def create_item(list_id, content, responsible_party=nil, notify=true)
242
- record "/todos/create_item/#{list_id}",
243
- :content => content, :responsible_party => responsible_party,
244
- :notify => notify
245
- end
246
-
247
- # Creates a new list using the given hash of list metadata.
248
- def create_list(project_id, list)
249
- record "/projects/#{project_id}/todos/create_list", list
250
- end
251
-
252
- # Deletes the given item from it's parent list.
253
- def delete_item(id)
254
- record "/todos/delete_item/#{id}"
255
- end
256
-
257
- # Deletes the given list and all of its items.
258
- def delete_list(id)
259
- record "/todos/delete_list/#{id}"
260
- end
261
-
262
- # Retrieves the specified list, and all of its items.
263
- def get_list(id)
264
- record "/todos/list/#{id}"
265
- end
266
-
267
- # Return all lists for a project. If complete is true, only completed lists
268
- # are returned. If complete is false, only uncompleted lists are returned.
269
- def lists(project_id, complete=nil)
270
- records "todo-list", "/projects/#{project_id}/todos/lists", :complete => complete
271
- end
272
-
273
- # Repositions an item to be at the given position in its list
274
- def move_item(id, to)
275
- record "/todos/move_item/#{id}", :to => to
276
- end
277
-
278
- # Repositions a list to be at the given position in its project
279
- def move_list(id, to)
280
- record "/todos/move_list/#{id}", :to => to
281
- end
282
-
283
- # Updates the given item
284
- def update_item(id, content, responsible_party=nil, notify=true)
285
- record "/todos/update_item/#{id}",
286
- :item => { :content => content }, :responsible_party => responsible_party,
287
- :notify => notify
288
- end
289
-
290
- # Updates the given list's metadata
291
- def update_list(id, list)
292
- record "/todos/update_list/#{id}", :list => list
293
- end
294
-
295
- # =========================================================================
296
- # MILESTONES
297
- # =========================================================================
298
-
299
- # Complete the milestone with the given id
300
- def complete_milestone(id)
301
- record "/milestones/complete/#{id}"
302
- end
303
-
304
- # Create a new milestone for the given project. +data+ must be hash of the
305
- # values to set, including +title+, +deadline+, +responsible_party+, and
306
- # +notify+.
307
- def create_milestone(project_id, data)
308
- create_milestones(project_id, [data]).first
309
- end
310
-
311
- # As #create_milestone, but can create multiple milestones in a single
312
- # request. The +milestones+ parameter must be an array of milestone values as
313
- # descrbed in #create_milestone.
314
- def create_milestones(project_id, milestones)
315
- records "milestone", "/projects/#{project_id}/milestones/create", :milestone => milestones
316
- end
317
-
318
- # Destroys the milestone with the given id.
319
- def delete_milestone(id)
320
- record "/milestones/delete/#{id}"
321
- end
322
-
323
- # Returns a list of all milestones for the given project, optionally filtered
324
- # by whether they are completed, late, or upcoming.
325
- def milestones(project_id, find="all")
326
- records "milestone", "/projects/#{project_id}/milestones/list", :find => find
327
- end
328
-
329
- # Uncomplete the milestone with the given id
330
- def uncomplete_milestone(id)
331
- record "/milestones/uncomplete/#{id}"
332
- end
333
-
334
- # Updates an existing milestone.
335
- def update_milestone(id, data, move=false, move_off_weekends=false)
336
- record "/milestones/update/#{id}", :milestone => data,
337
- :move_upcoming_milestones => move,
338
- :move_upcoming_milestones_off_weekends => move_off_weekends
339
- end
340
-
341
- # Make a raw web-service request to Basecamp. This will return a Hash of
342
- # Arrays of the response, and may seem a little odd to the uninitiated.
343
- def request(path, parameters = {}, second_try = false)
344
- response = post(path, convert_body(parameters), "Content-Type" => content_type)
345
-
346
- if response.code.to_i / 100 == 2
347
- result = XmlSimple.xml_in(response.body, 'keeproot' => true,
348
- 'contentkey' => '__content__', 'forcecontent' => true)
349
- typecast_value(result)
350
- elsif response.code == "302" && !second_try
351
- connect!(@url, !@use_ssl)
352
- request(path, parameters, true)
353
- else
354
- raise "#{response.message} (#{response.code})"
355
- end
356
- end
357
-
358
- # A convenience method for wrapping the result of a query in a Record
359
- # object. This assumes that the result is a singleton, not a collection.
360
- def record(path, parameters={})
361
- result = request(path, parameters)
362
- (result && !result.empty?) ? Record.new(result.keys.first, result.values.first) : nil
363
- end
364
-
365
- # A convenience method for wrapping the result of a query in Record
366
- # objects. This assumes that the result is a collection--any singleton
367
- # result will be wrapped in an array.
368
- def records(node, path, parameters={})
369
- result = request(path, parameters).values.first or return []
370
- result = result[node] or return []
371
- result = [result] unless Array === result
372
- result.map { |row| Record.new(node, row) }
373
- end
374
-
375
- private
376
-
377
- def connect!(url, use_ssl)
378
- @use_ssl = use_ssl
379
- @url = url
380
- @connection = Net::HTTP.new(url, use_ssl ? 443 : 80)
381
- @connection.use_ssl = @use_ssl
382
- @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE if @use_ssl
383
- end
384
-
385
- def convert_body(body)
386
- body = use_xml ? body.to_xml : body.to_yaml
387
- end
388
-
389
- def content_type
390
- use_xml ? "application/xml" : "application/x-yaml"
391
- end
392
-
393
- def post(path, body, header={})
394
- request = Net::HTTP::Post.new(path, header.merge('Accept' => 'application/xml'))
395
- request.basic_auth(@user_name, @password)
396
- @connection.request(request, body)
397
- end
398
-
399
- def store_file(contents)
400
- response = post("/upload", contents, 'Content-Type' => 'application/octet-stream',
401
- 'Accept' => 'application/xml')
402
-
403
- if response.code == "200"
404
- result = XmlSimple.xml_in(response.body, 'keeproot' => true, 'forcearray' => false)
405
- return result["upload"]["id"]
406
- else
407
- raise "Could not store file: #{response.message} (#{response.code})"
408
- end
409
- end
410
-
411
- def typecast_value(value)
412
- case value
413
- when Hash
414
- if value.has_key?("__content__")
415
- content = translate_entities(value["__content__"]).strip
416
- case value["type"]
417
- when "integer" then content.to_i
418
- when "boolean" then content == "true"
419
- when "datetime" then Time.parse(content)
420
- when "date" then Date.parse(content)
421
- else content
422
- end
423
- # a special case to work-around a bug in XmlSimple. When you have an empty
424
- # tag that has an attribute, XmlSimple will not add the __content__ key
425
- # to the returned hash. Thus, we check for the presense of the 'type'
426
- # attribute to look for empty, typed tags, and simply return nil for
427
- # their value.
428
- elsif value.keys == %w(type)
429
- nil
430
- elsif value["nil"] == "true"
431
- nil
432
- # another special case, introduced by the latest rails, where an array
433
- # type now exists. This is parsed by XmlSimple as a two-key hash, where
434
- # one key is 'type' and the other is the actual array value.
435
- elsif value.keys.length == 2 && value["type"] == "array"
436
- value.delete("type")
437
- typecast_value(value)
438
- else
439
- value.empty? ? nil : value.inject({}) do |h,(k,v)|
440
- h[k] = typecast_value(v)
441
- h
442
- end
443
- end
444
- when Array
445
- value.map! { |i| typecast_value(i) }
446
- case value.length
447
- when 0 then nil
448
- when 1 then value.first
449
- else value
450
- end
451
- else
452
- raise "can't typecast #{value.inspect}"
453
- end
454
- end
455
-
456
- def translate_entities(value)
457
- value.gsub(/&lt;/, "<").
458
- gsub(/&gt;/, ">").
459
- gsub(/&quot;/, '"').
460
- gsub(/&apos;/, "'").
461
- gsub(/&amp;/, "&")
462
- end
463
-
464
- def prepare_attachments(list)
465
- (list || []).each do |data|
466
- upload = data[:file]
467
- id = store_file(upload.content)
468
- data[:file] = { :file => id,
469
- :content_type => "application/octet-stream",
470
- :original_filename => upload.filename }
471
- end
472
- end
473
- end
474
-
475
- # A minor hack to let Xml-Simple serialize symbolic keys in hashes
476
- class Symbol
477
- def [](*args)
478
- to_s[*args]
479
- end
480
- end
481
-
482
- class Hash
483
- def to_xml
484
- XmlSimple.xml_out({:request => self}, 'keeproot' => true, 'noattr' => true)
485
- end
486
- end
@@ -1,34 +0,0 @@
1
- # A class to update basecamp after a successful deployment
2
- require 'basecamp'
3
- class BasecampNotifier
4
-
5
- def initialize(application, rails_env, options = {}, current_rev = 0, rev_log = '')
6
-
7
- if keys_missing?(options)
8
- raise StandardError, 'You have not supplied all arguments in the hash (:username, :password, :domain, :category_id, :project_id)'
9
- end
10
-
11
- @project_id = options[:project_id]
12
- @category_id = options[:category_id]
13
- @current_revision = current_rev
14
- @application = application
15
- @rails_env = rails_env
16
- @revision_log = rev_log
17
- @basecamp = Basecamp.new(options[:domain], options[:username], options[:password], true)
18
- end
19
-
20
- def keys_missing?(options)
21
- [:domain, :username, :password, :category_id, :project_id].each do |key|
22
- return true unless options.has_key?(key)
23
- end
24
- false
25
- end
26
-
27
- def notify
28
- message = {:title => "#{@application} rev. #{@current_revision} deployed to #{@rails_env}",
29
- :body => "#{@application} has been successfully deployed to #{@rails_env} <br /> #{@revision_log}",
30
- :category_id => @category_id}
31
- @basecamp.post_message(@project_id, message)
32
- end
33
-
34
- end