cerberus 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,16 @@
1
1
  = Cerberus Changelog
2
2
 
3
- == Veresion 0.7.9
3
+ == Version 0.8.0
4
+ Ruby 1.9 bugfixes & new config options
5
+
6
+ * Fixed Ruby 1.9 string encoding bug [git SCM]
7
+ * :require_revision_change - set to true to avoid Cerberus rebuilding a project
8
+ if the SCM revision hasn't changed, even if the prior build was unsuccessful
9
+ (default false - for backwards compatibility)
10
+ * :max_wait_time - set to Integer (number of seconds) for the build command to
11
+ wait for a timeout before cancelling build (default 30 minutes).
12
+
13
+ == Version 0.7.9
4
14
  Bugfixes
5
15
 
6
16
  * Fix issue with tests and mailer with Rails 3 installed
@@ -15,7 +25,7 @@ Added channel password option to IRC publisher
15
25
  Bugfixes, publisher and config options updates, ActionMailer gem version requirements
16
26
 
17
27
  * Projects are now sorted when displayed via Cerberus CLI
18
- * Git builder now includes just the commit message instead of the commit diff
28
+ * Git builder now includes just the commit message instead of the commit diff
19
29
  in the publisher output and log file
20
30
  * Updated output/reporting of :setup_script during Manager#run. Results
21
31
  are included in Publisher's formatted_message
@@ -28,7 +38,7 @@ Bugfixes and updates to RSS publisher
28
38
 
29
39
  * Fix mercurial builder to properly read revision number of merged commits
30
40
  * Updated RSS publisher to output a properly formatted RSS feed with the
31
- ability to keep a certain number of previous builds in the feed. For more
41
+ ability to keep a certain number of previous builds in the feed. For more
32
42
  info on the changes please see the commit message on the following commit:
33
43
  http://github.com/cpjolicoeur/cerberus/commit/1f7176a6a611f30a0d70e0f75ec90724f6302043
34
44
  * Update usage documentation
@@ -125,7 +135,7 @@ Git, Twitter support
125
135
  == Version 0.3.6
126
136
  RSpec, Gmailer support
127
137
 
128
- * Added out of box support of RSpec tests.
138
+ * Added out of box support of RSpec tests.
129
139
  Usage: add to your project configuration following config
130
140
 
131
141
  builder:
@@ -150,7 +160,7 @@ RSpec, Gmailer support
150
160
  :username, :password, :proxy_host, :proxy_port, :proxy_user, :proxy_pass
151
161
 
152
162
  * Remove to_xs helper. Using one from actionsuport
153
-
163
+
154
164
 
155
165
  == Version 0.3.5
156
166
  Bug fixing
@@ -177,7 +187,7 @@ Major changes
177
187
  * Correctly implement Jabber publisher using XMPP4R library
178
188
  * Upgrade Webgen to 0.4.1
179
189
  * Added 'getting better/worse' to build messages' subject
180
- * Added possibility to configure events on what you would like to receive messages.
190
+ * Added possibility to configure events on what you would like to receive messages.
181
191
  By default used 'default' events. It means all except 'successful' state.
182
192
  You could set to this options list of any valid states such as [broken, failed, revival, successful, setup] or predefined sets as
183
193
  [all, default, none].
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # encoding: UTF-8
2
3
 
3
4
  # Add lib dir to load path so that we don't have to be installed in rubygems
4
5
  require 'pathname' # Use Pathname to follow symlinks
@@ -4,5 +4,5 @@ module Cerberus
4
4
 
5
5
  LOCK_WAIT = 30 * 60 # 30 minutes
6
6
 
7
- VERSION = '0.7.9'
7
+ VERSION = '0.8.0'
8
8
  end
@@ -3,29 +3,30 @@ require 'fileutils'
3
3
  require 'cerberus/utils'
4
4
  require 'cerberus/constants'
5
5
  require 'cerberus/config'
6
+ require 'cerberus/status'
6
7
  require 'cerberus/latch'
7
8
  require 'cerberus/component_lazy_loader'
8
9
 
9
10
  module Cerberus
10
11
  class AddCommand
11
12
  EXAMPLE_CONFIG = File.expand_path(File.dirname(__FILE__) + '/config.example.yml')
12
-
13
+
13
14
  def initialize(path, cli_options = {})
14
15
  @path, @cli_options = path, HashWithIndifferentAccess.new(cli_options)
15
16
  end
16
-
17
+
17
18
  def run
18
19
  scm_type = @cli_options[:scm] || Cerberus::SCM.guess_type(@path) || 'svn'
19
20
  scm = Cerberus::SCM.get(scm_type).new(@path, Config.new(nil, @cli_options))
20
21
  say "Can't find any #{scm_type} application under #{@path}" unless scm.url
21
-
22
+
22
23
  application_name = @cli_options[:application_name] || extract_project_name(@path)
23
-
24
+
24
25
  create_example_config
25
-
26
+
26
27
  config_name = "#{HOME}/config/#{application_name}.yml"
27
28
  say "Application #{application_name} already present in Cerberus" if File.exists?(config_name)
28
-
29
+
29
30
  app_config = { 'scm' => {
30
31
  'url' => scm.url,
31
32
  'type' => scm_type },
@@ -35,111 +36,113 @@ module Cerberus
35
36
  dump_yml(config_name, app_config)
36
37
  puts "Application '#{application_name}' has been added to Cerberus successfully" unless @cli_options[:quiet]
37
38
  end
38
-
39
+
39
40
  private
40
41
 
41
42
  def extract_project_name(path)
42
43
  path = File.expand_path(path) if test(?d, path)
43
44
  File.basename(path).strip.gsub( /\.git$/, '' )
44
45
  end
45
-
46
+
46
47
  def create_example_config
47
48
  FileUtils.mkpath(HOME) unless test(?d, HOME)
48
49
  FileUtils.cp(EXAMPLE_CONFIG, CONFIG_FILE) unless test(?f, CONFIG_FILE)
49
50
  end
50
51
  end
51
-
52
+
52
53
  class RemoveCommand
53
- # DRY: this is ugly and needs refactoring. It duplicates functionality from other
54
+ # DRY: this is ugly and needs refactoring. It duplicates functionality from other
54
55
  # classes a lot.
55
-
56
+
56
57
  def initialize(application_name, cli_options = {})
57
58
  unless File.exists?("#{HOME}/config/#{application_name}.yml")
58
59
  say "Project '#{application_name}' does not exist in Cerberus. Type 'cerberus list' to see the list of all active projects."
59
60
  end
60
61
  @app_root = "#{HOME}/work/#{application_name}"
61
-
62
+
62
63
  def_options = {:application_root => @app_root, :application_name => application_name}
63
64
  @config = Config.new(application_name, cli_options.merge(def_options))
64
65
  end
65
-
66
+
66
67
  def run
67
68
  application_name = @config[:application_name]
68
-
69
+
69
70
  config_name = "#{HOME}/config/#{application_name}.yml"
70
-
71
+
71
72
  if not File.exists?(config_name)
72
73
  say "Unknown application #{application_name}"
73
- exit(1)
74
+ exit(1)
74
75
  end
75
76
  FileUtils.rm_rf @config[:application_root]
76
77
  File.unlink config_name
77
78
  puts "Application '#{application_name}' removed." unless @config[:quiet]
78
79
  end
79
80
  end
80
-
81
+
81
82
  class BuildCommand
82
83
  attr_reader :builder, :success, :scm, :status, :setup_script_output
83
-
84
- DEFAULT_CONFIG = {:scm => {:type => 'svn'},
84
+
85
+ DEFAULT_CONFIG = {:scm => {:type => 'svn'},
85
86
  :log => {:enable => true},
86
87
  :at_time => '* *',
88
+ :max_wait_time => LOCK_WAIT,
89
+ :require_revision_change => false
87
90
  }
88
-
91
+
89
92
  def initialize(application_name, cli_options = {})
90
93
  unless File.exists?("#{HOME}/config/#{application_name}.yml")
91
94
  say "Project '#{application_name}' does not exist in Cerberus. Type 'cerberus list' to see the list of all active projects."
92
95
  end
93
-
96
+
94
97
  app_root = "#{HOME}/work/#{application_name}"
95
-
98
+
96
99
  def_options = {:application_root => app_root + '/sources', :application_name => application_name} #pseudo options that stored in config. Could not be set in any config file not through CLI
97
100
  @config = Config.new(application_name, cli_options.merge(def_options))
98
101
  @config.merge!(DEFAULT_CONFIG, false)
99
-
102
+
100
103
  @status = Status.read("#{app_root}/status.log")
101
-
104
+
102
105
  scm_type = @config[:scm, :type]
103
106
  @scm = SCM.get(scm_type).new(@config[:application_root], @config)
104
107
  say "Client for SCM '#{scm_type}' does not installed" unless @scm.installed?
105
-
108
+
106
109
  builder_type = get_configuration_option(@config[:builder], :type, :rake)
107
110
  @builder = Builder.get(builder_type).new(@config)
108
111
  end
109
-
112
+
110
113
  def run
111
114
  begin
112
- Latch.lock("#{HOME}/work/#{@config[:application_name]}/.lock", :lock_ttl => 2 * LOCK_WAIT) do
115
+ Latch.lock("#{HOME}/work/#{@config[:application_name]}/.lock", :lock_ttl => @config[:max_wait_time]) do
113
116
  @scm.update!
114
- if @scm.has_changes? or @config[:force] or !@status.previous_build_successful
117
+ if ( @config[:force] || @scm.has_changes? || ( @status.previous_build_failed? && !@config[:require_revision_change] ) )
115
118
  Dir.chdir File.join(@config[:application_root], @config[:build_dir] || '')
116
119
  @setup_script_output = `#{@config[:setup_script]}` if @config[:setup_script]
117
120
 
118
121
  build_successful = @builder.run
119
122
  @status.keep(build_successful, @scm.current_revision, @builder.brokeness)
120
-
123
+
121
124
  #Save logs to directory
122
125
  if @config[:log, :enable]
123
126
  log_dir = "#{HOME}/work/#{@config[:application_name]}/logs/"
124
127
  FileUtils.mkpath(log_dir)
125
-
128
+
126
129
  time = Time.now.strftime("%Y%m%d%H%M%S")
127
130
  file_name = "#{log_dir}/#{time}-#{@status.current_state.to_s}.log"
128
131
  body = [ @setup_script_output, scm.last_commit_message, builder.output ].join("\n\n")
129
132
  IO.write(file_name, body)
130
133
  end
131
-
134
+
132
135
  #send notifications
133
136
  active_publishers = get_configuration_option(@config[:publisher], :active, 'mail')
134
137
  active_publishers.split(/\W+/).each do |pub|
135
-
138
+
136
139
  publisher_config = @config[:publisher, pub]
137
140
  raise "Publisher have no configuration: #{pub}" unless publisher_config
138
-
141
+
139
142
  events = interpret_state(publisher_config[:on_event] || @config[:publisher, :on_event] || 'default')
140
143
  Publisher.get(pub, publisher_config).publish(@status, self, @config) if events.include?(@status.current_state)
141
144
  end
142
-
145
+
143
146
  #Process hooks
144
147
  hooks = @config[:hook]
145
148
  hooks.each_pair{|name, hook|
@@ -149,13 +152,13 @@ module Cerberus
149
152
  end
150
153
  } if hooks
151
154
  end
152
-
155
+
153
156
  end #lock
154
157
  rescue Exception => e
155
158
  if ENV['CERBERUS_ENV'] == 'TEST'
156
159
  raise e
157
160
  else
158
- File.open("#{HOME}/error.log", File::WRONLY|File::APPEND|File::CREAT) do |f|
161
+ File.open("#{HOME}/error.log", File::WRONLY|File::APPEND|File::CREAT) do |f|
159
162
  f.puts Time.now.strftime("%a, %d %b %Y %H:%M:%S [#{@config[:application_name]}] -- #{e.class}")
160
163
  f.puts e.message unless e.message.empty?
161
164
  f.puts e.backtrace.collect{|line| ' '*5 + line}
@@ -173,7 +176,7 @@ module Cerberus
173
176
  end
174
177
  return false
175
178
  end
176
-
179
+
177
180
  private
178
181
  def get_configuration_option(hash, defining_key = nil, default_option = nil)
179
182
  if hash
@@ -183,24 +186,24 @@ module Cerberus
183
186
  return default_option
184
187
  end
185
188
  end
186
-
189
+
187
190
  class BuildAllCommand
188
191
  def initialize(cli_options = {})
189
192
  @cli_options = cli_options
190
193
  end
191
-
194
+
192
195
  def run
193
196
  threads = []
194
197
  Dir["#{HOME}/config/*.yml"].each do |fn|
195
198
  fn =~ %r{#{HOME}/config/(.*).yml}
196
199
  application_name = $1
197
-
200
+
198
201
  command = Cerberus::BuildCommand.new(application_name, @cli_options)
199
202
  threads << Thread.new { command.run } if command.run_time?(Time.now)
200
203
  end
201
-
204
+
202
205
  @already_waited = false
203
- threads.each do |t|
206
+ threads.each do |t|
204
207
  if @already_waited or not t.join(LOCK_WAIT)
205
208
  t.kill
206
209
  @already_waited = true
@@ -208,37 +211,37 @@ module Cerberus
208
211
  end
209
212
  end
210
213
  end
211
-
214
+
212
215
  class ListCommand
213
- def initialize(cli_options = {})
216
+ def initialize(cli_options = {})
214
217
  end
215
-
218
+
216
219
  def run
217
220
  projects = Dir["#{HOME}/config/*.yml"]
218
221
  if projects.empty?
219
- puts "There are no active projects"
222
+ puts "There are no active projects"
220
223
  else
221
224
  puts "List of active projects:"
222
-
225
+
223
226
  projects.sort.each do |fn|
224
227
  fn =~ %r{#{HOME}/config/(.*).yml}
225
-
228
+
226
229
  puts " * #{$1}"
227
230
  end
228
-
231
+
229
232
  puts "\nType 'cerberus build PROJECT_NAME' to build any of these projects"
230
233
  end
231
234
  end
232
235
  end
233
236
 
234
237
  class StatusCommand
235
- def initialize(cli_options = {})
238
+ def initialize(cli_options = {})
236
239
  end
237
-
240
+
238
241
  def run
239
242
  projects = Dir["#{HOME}/config/*.yml"].sort.map { |fn| fn.gsub(/.*\/(.*).yml$/, '\1') }
240
243
  if projects.empty?
241
- puts "There are not any active projects"
244
+ puts "There are not any active projects"
242
245
  else
243
246
  delim = ' | '
244
247
  cols = [
@@ -273,88 +276,5 @@ module Cerberus
273
276
  end
274
277
 
275
278
  end
276
-
277
- #
278
- # Fields that are contained in status file
279
- #
280
- # successful_build_timestamp
281
- # timestamp
282
- # successful (true mean previous build was successful, otherwise - false)
283
- # revision
284
- # brokeness
285
- # successful_build_revision
286
- #
287
- class Status
288
- attr_reader :previous_build_successful, :previous_brokeness, :current_build_successful, :current_brokeness, :revision, :successful_build_revision
289
-
290
- def initialize(param)
291
- if param.is_a? Hash
292
- @hash = param
293
- @current_build_successful = @hash['state']
294
- @already_kept = true
295
- else
296
- @path = param
297
- value = File.exists?(@path) ? YAML.load(IO.read(@path)) : nil
298
-
299
- @hash =
300
- case value
301
- when String
302
- value = %w(succesful successful setup).include?(value) #fix typo in status values
303
- {'successful' => value}
304
- when nil
305
- {}
306
- else
307
- value
308
- end
309
-
310
- @already_kept = false
311
- end
312
-
313
- @revision = @hash['revision']
314
- @successful_build_revision = @hash['successful_build_revision']
315
- @previous_build_successful = @hash['successful']
316
- @previous_brokeness = @hash['brokeness']
317
-
318
- # Create some convenience methods to access status
319
- @hash.keys.each { |key| self.class.send( :define_method, key ) { @hash[key] } }
320
- end
321
-
322
- def self.read(file_name)
323
- Status.new(file_name)
324
- end
325
-
326
- def keep(build_successful, revision, brokeness)
327
- raise 'Status could be kept only once. Please try to reread status file.' if @already_kept
328
-
329
- @current_brokeness = brokeness
330
- @current_build_successful = build_successful
331
-
332
- hash = {'successful' => @current_build_successful, 'timestamp' => Time.now, 'revision' => revision, 'brokeness' => brokeness}
333
- if build_successful
334
- hash['successful_build_timestamp'] = Time.now
335
- hash['successful_build_revision'] = revision
336
- else
337
- hash['successful_build_timestamp'] = @hash['successful_build_timestamp']
338
- hash['successful_build_revision'] = @hash['successful_build_revision']
339
- end
340
-
341
- File.open(@path, "w+", 0777) { |file| file.write(YAML.dump(hash)) }
342
-
343
- @already_kept = true
344
- end
345
-
346
- def current_state
347
- raise "Invalid project state. Before calculating status please do keeping of it." unless @already_kept
348
-
349
- if @current_build_successful
350
- if @previous_build_successful.nil?
351
- :setup
352
- else
353
- @previous_build_successful ? :successful : :revival
354
- end
355
- else
356
- @previous_build_successful ? :failed : :broken
357
- end
358
- end
359
- end
279
+
360
280
  end
@@ -71,7 +71,9 @@ class Cerberus::SCM::Git < Cerberus::SCM::Base
71
71
  end
72
72
 
73
73
  def extract_commit_info( commit=remote_head )
74
- message = execute("log", "#{ commit } -1 --pretty='format:%an(%ae)|%ai|%H|%s%n%n%b'").split("|")
74
+ message = String.new.respond_to?(:force_encoding) ?
75
+ execute("log", "#{ commit } -1 --pretty='format:%an(%ae)|%ai|%H|%s%n%n%b'").force_encoding('utf-8').split("|") :
76
+ execute("log", "#{ commit } -1 --pretty='format:%an(%ae)|%ai|%H|%s%n%n%b'").split("|")
75
77
  return { :author => message[0], :date => message[1], :revision => message[2], :message => message[3] }
76
78
  end
77
79
 
@@ -0,0 +1,93 @@
1
+ module Cerberus
2
+ # Fields that are contained in status file
3
+ #
4
+ # successful_build_timestamp
5
+ # timestamp
6
+ # successful (true mean previous build was successful, otherwise - false)
7
+ # revision
8
+ # brokeness
9
+ # successful_build_revision
10
+ #
11
+ class Status
12
+ attr_reader :previous_build_successful, :previous_brokeness, :current_build_successful, :current_brokeness, :revision, :successful_build_revision
13
+
14
+ def initialize(param)
15
+ if param.is_a? Hash
16
+ @hash = param
17
+ @current_build_successful = @hash['state']
18
+ @already_kept = true
19
+ else
20
+ @path = param
21
+ value = File.exists?(@path) ? YAML.load(IO.read(@path)) : nil
22
+
23
+ @hash =
24
+ case value
25
+ when String
26
+ value = %w(succesful successful setup).include?(value) #fix typo in status values
27
+ {'successful' => value}
28
+ when nil
29
+ {}
30
+ else
31
+ value
32
+ end
33
+
34
+ @already_kept = false
35
+ end
36
+
37
+ @revision = @hash['revision']
38
+ @successful_build_revision = @hash['successful_build_revision']
39
+ @previous_build_successful = @hash['successful']
40
+ @previous_brokeness = @hash['brokeness']
41
+
42
+ # Create some convenience methods to access status
43
+ @hash.keys.each { |key| self.class.send( :define_method, key ) { @hash[key] } }
44
+ end
45
+
46
+ def self.read(file_name)
47
+ Status.new(file_name)
48
+ end
49
+
50
+ def keep(build_successful, revision, brokeness)
51
+ raise 'Status could be kept only once. Please try to reread status file.' if @already_kept
52
+
53
+ @current_brokeness = brokeness
54
+ @current_build_successful = build_successful
55
+
56
+ hash = {'successful' => @current_build_successful, 'timestamp' => Time.now, 'revision' => revision, 'brokeness' => brokeness}
57
+ if build_successful
58
+ hash['successful_build_timestamp'] = Time.now
59
+ hash['successful_build_revision'] = revision
60
+ else
61
+ hash['successful_build_timestamp'] = @hash['successful_build_timestamp']
62
+ hash['successful_build_revision'] = @hash['successful_build_revision']
63
+ end
64
+
65
+ File.open(@path, "w+", 0777) { |file| file.write(YAML.dump(hash)) }
66
+
67
+ @already_kept = true
68
+ end
69
+
70
+ def previous_build_failed?
71
+ # if @previous_build_successful.nil?
72
+ # return false
73
+ # else
74
+ # ( :setup == current_state ) ? false : !@previous_build_successful
75
+ # end
76
+ @previous_build_successful.nil? ? false : !@previous_build_successful
77
+ end
78
+
79
+ def current_state
80
+ raise "Invalid project state. Before calculating status please do keeping of it." unless @already_kept
81
+
82
+ if @current_build_successful
83
+ if @previous_build_successful.nil?
84
+ :setup
85
+ else
86
+ @previous_build_successful ? :successful : :revival
87
+ end
88
+ else
89
+ @previous_build_successful ? :failed : :broken
90
+ end
91
+ end
92
+ end
93
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cerberus
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
5
- prerelease: false
4
+ hash: 63
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
- - 7
9
- - 9
10
- version: 0.7.9
8
+ - 8
9
+ - 0
10
+ version: 0.8.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Craig P Jolicoeur
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-25 00:00:00 -05:00
18
+ date: 2011-04-02 00:00:00 -04:00
19
19
  default_executable: cerberus
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -106,6 +106,7 @@ files:
106
106
  - lib/cerberus/scm/hg.rb
107
107
  - lib/cerberus/scm/perforce.rb
108
108
  - lib/cerberus/scm/svn.rb
109
+ - lib/cerberus/status.rb
109
110
  - lib/cerberus/utils.rb
110
111
  - lib/vendor/addressable/addressable.gemspec
111
112
  - lib/vendor/addressable/CHANGELOG
@@ -466,7 +467,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
466
467
  requirements: []
467
468
 
468
469
  rubyforge_project: cerberus
469
- rubygems_version: 1.3.7
470
+ rubygems_version: 1.6.2
470
471
  signing_key:
471
472
  specification_version: 3
472
473
  summary: Cerberus is a Continuous Integration tool that could be easily run from Cron.