buildmeister 0.8.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -4,14 +4,20 @@ require 'rubygems'
4
4
  require 'hoe'
5
5
  require './lib/buildmeister.rb'
6
6
 
7
- Hoe.new('buildmeister', Buildmeister::VERSION) do |p|
8
- p.rubyforge_name = 'buildmeister' # if different than lowercase project name
9
- p.developer('Leigh Caplan', 'lcaplan@onehub.com')
10
- end
11
-
12
- task :cultivate do
13
- system "touch Manifest.txt; rake check_manifest | grep -v \"(in \" | patch"
14
- system "rake debug_gem | grep -v \"(in \" > `basename \\`pwd\\``.gemspec"
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |s|
10
+ s.name = "buildmeister"
11
+ s.executables = ["buildmeister", "git_cleanup"]
12
+ s.summary = "Dead simple tools for managing Lighthouse and Git deployment workflow"
13
+ s.email = "lcaplan@onehub.com"
14
+ s.homepage = "http://github.com/onehub/buildmeister"
15
+ s.description = "Dead simple tools for managing Lighthouse and Git deployment workflow"
16
+ s.authors = ["Leigh Caplan"]
17
+ s.files = FileList["[A-Z]*", "{bin,generators,lib,spec}/**/*"]
18
+ end
19
+ rescue LoadError
20
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
15
21
  end
16
22
 
17
23
  # vim: syntax=Ruby
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/bin/buildmeister CHANGED
@@ -3,12 +3,16 @@
3
3
  require File.expand_path(File.dirname(__FILE__) + "/../lib/buildmeister")
4
4
 
5
5
  begin
6
- Buildmeister.new.send ARGV.shift
6
+ command = ARGV.shift
7
+ args = ARGV.dup
8
+ ARGV.clear
9
+
10
+ Buildmeister::Base.new(*args).send command
7
11
  rescue Interrupt => i
8
- Buildmeister.post_notification("Buildmeister Shut Down", "Goodbye!")
12
+ Buildmeister::Notifier.post("Buildmeister Shut Down", "Goodbye!")
9
13
  puts "\rThank you for using Buildmeister!"
10
14
  rescue Exception => e
11
- Buildmeister.post_notification("Buildmeister Error: #{e.class}", e.message)
15
+ Buildmeister::Notifier.post("Buildmeister Error: #{e.class}", e.message)
12
16
  puts "Quitting Buildmeister due to error: #{e.message}"
13
17
  puts e.backtrace
14
18
  end
data/lib/buildmeister.rb CHANGED
@@ -1,303 +1,13 @@
1
1
  require 'rubygems'
2
2
  require 'lighthouse'
3
3
  require 'activesupport'
4
- require 'optparse'
5
4
 
6
- class Buildmeister
7
- attr_accessor :project, :project_name, :bin_groups, :notification_interval
8
-
9
- RETRY_COUNT = 5
10
-
11
- def initialize
12
- @options = {}
13
- OptionParser.new do |opts|
14
- opts.banner = "Usage: buildmeister notify"
5
+ $: << File.dirname(__FILE__)
15
6
 
16
- opts.on('-f', '--from-bin BIN_NAME', 'Move From Bin') do |f|
17
- @options[:move_from] = f
18
- end
19
-
20
- opts.on('-t', '--to-state STATE', 'Move to State') do |t|
21
- @options[:to_state] = t
22
- end
23
-
24
- opts.on('-v', '--verbose', 'Verbose') do |t|
25
- @options[:verbose] = true
26
- end
27
- end.parse!
28
-
29
- @config = Buildmeister.load_config
30
- Lighthouse.account = @config['account']
31
- Lighthouse.token = @config['token']
32
-
33
- self.project_name = @config['project_name']
34
- self.get_project
35
- self.bin_groups = []
36
-
37
- self.notification_interval = @config['notification_interval']
38
-
39
- @config['bin_groups'].each do |bin_group|
40
- self.bin_groups << {
41
- :name => bin_group.keys.first,
42
- :bin_names => bin_group.values.first.map
43
- }
44
- end
45
-
46
- self.bin_groups.each do |bin_group|
47
- bin_group[:bin_names].each do |bin_name|
48
- class << bin_name
49
- def normalize
50
- Buildmeister.normalize_bin_name(self)
51
- end
52
- end
53
-
54
- attr_accessor_init = <<-eos
55
- class << self
56
- attr_accessor :"#{bin_name.normalize}", :"last_#{bin_name.normalize}"
57
- end
58
- eos
59
-
60
- eval attr_accessor_init
61
- end
62
- end
63
-
64
- load_project
65
- end
66
-
67
- def normalize(bin_name)
68
- Buildmeister.normalize_bin_name(bin_name)
69
- end
70
-
71
- def new_hotfix
72
- generate_timed_branch('hotfix')
73
- end
74
-
75
- def new_experimental
76
- generate_timed_branch('experimental')
77
- end
78
-
79
- def generate_timed_branch(prefix)
80
- branches = local_branches
81
- now = Time.now
82
- count = 1
83
-
84
- loop do
85
- new_branch_name = "#{prefix}-#{now.year}-#{now.month.to_s.rjust 2, '0'}-#{now.day.to_s.rjust 2, '0'}-#{count.to_s.rjust 3, '0'}"
86
- unless branches.include? new_branch_name
87
- `git checkout -b #{new_branch_name}`
88
- puts "Created #{new_branch_name}"
89
- return true
90
- end
91
-
92
- count += 1
93
- end
94
- end
95
-
96
- def pull_bin(bin_name = ARGV.shift)
97
- bin_name = normalize(bin_name)
98
- existing_bin_names = bin_names.map { |b| b.normalize }
99
-
100
- raise ArgumentError, "#{bin_name} is not a valid bin! Must be in #{bin_names.join(', ')}" unless existing_bin_names.include?(bin_name)
101
-
102
- `git fetch origin`
103
-
104
- branches = remote_branches
105
- ticket_numbers = send(normalize(bin_name)).tickets.map { |tkt| tkt.id.to_s }
106
-
107
- branches_to_pull = branches.select do |branch_name|
108
- ticket_numbers.map { |tkt_number| branch_name =~ /#{tkt_number}/ }.any?
109
- end
110
-
111
- branches_to_pull.each do |branch|
112
- result = `git pull origin #{branch.gsub("origin/", "")}`
113
- puts result
114
- end
115
- end
116
-
117
- def local_branches
118
- `git branch`.split.reject { |name| name == "*" }
119
- end
120
-
121
- def remote_branches
122
- `git branch -r`.split.reject { |name| name == "*" }
123
- end
124
-
125
- def current_branch
126
- branches = `git branch`.split
127
- i = branches.index "*"
128
- branches[i + 1]
129
- end
130
-
131
- def move_all
132
- bin_name = normalize @options[:move_from]
133
- self.send(bin_name).tickets.each do |ticket|
134
- ticket.state = @options[:to_state]
135
- ticket.save
136
- end
137
-
138
- puts "All tickets from bin #{@options[:move_from]} have been moved to #{@options[:to_state]}"
139
- end
140
-
141
- def bin_group_report(bin_group_name = normalize(ARGV.shift))
142
- bin_group = bin_groups.find { |group| group[:name] == bin_group_name }
143
- bin_names = bin_group[:bin_names].map &:normalize
144
-
145
- ticket_numbers = bin_names.map do |bin_name|
146
- send(normalize(bin_name)).tickets.map &:id
147
- end.flatten
148
-
149
- # Pluck the relevant branch names using git...
150
- relevant_branches =
151
- remote_branches.select do |branch_name|
152
- ticket_numbers.map { |tkt_number| branch_name =~ /#{tkt_number}/ }.any?
153
- end.map { |b| b.gsub('origin/', '') }
154
-
155
- output = ""
156
- output << "#{current_branch}\n"
157
- relevant_branches.each do |branch|
158
- output << "\n#{branch}"
159
- end
160
-
161
- puts output
162
- end
163
-
164
- def resolve_verified
165
- self.verified.tickets.each do |ticket|
166
- ticket.state = 'resolved'
167
- ticket.save
168
- end
169
- end
170
-
171
- def stage_all
172
- self.ready.tickets.each do |ticket|
173
- ticket.state = 'staged'
174
- ticket.save
175
- end
176
- end
177
-
178
- def load_project
179
- bins = self.get_project.bins
180
-
181
- self.bin_groups.each do |bin_group|
182
- bin_group[:bin_names].each do |bin_name|
183
- self.send("#{bin_name.normalize}=", bins.find { |bin| bin.name == bin_name })
184
-
185
- class << self.send("#{bin_name.normalize}")
186
- attr_accessor :display_value
187
- end
188
- end
189
- end
190
- end
191
-
192
- def reload_info
193
- bin_names.each do |bin_name|
194
- send("last_#{bin_name.normalize}=", display_value(bin_name))
195
- end
196
-
197
- self.load_project
198
- end
199
-
200
- def bin_names
201
- bin_groups.map do |bin_group|
202
- bin_group[:bin_names].map do |bin_name|
203
- bin_name
204
- end
205
- end.flatten
206
- end
207
-
208
- def changed?
209
- bin_names.map { |bin_name| display_value(bin_name) } != bin_names.map { |bin_name| send("last_#{bin_name.normalize}") }
210
- end
211
-
212
- def get_project
213
- self.project ||= Lighthouse::Project.find(:all).find {|pr| pr.name == project_name}
214
- end
215
-
216
- def notify
217
- puts "Starting BuildMeister Notify..."
218
-
219
- retry_count = RETRY_COUNT
220
-
221
- loop do
222
- begin
223
- title = "BuildMeister: #{Time.now.strftime("%m/%d %I:%M %p")}"
224
-
225
- body = ''
226
-
227
- bin_groups.each do |bin_group|
228
- body += "#{bin_group[:name].titleize}\n"
229
- body += "---------\n"
230
-
231
- bin_group[:bin_names].each do |bin_name|
232
- body += "#{bin_name}: #{display_value(bin_name)}\n"
233
- end
234
-
235
- body += "\n"
236
- end
237
-
238
- puts "Updated notification at #{Time.now.strftime("%m/%d %I:%M %p")}"
239
-
240
- if changed?
241
- Buildmeister.post_notification(title, body)
242
- end
243
-
244
- sleep notification_interval.minutes.to_i
245
-
246
- reload_info
247
-
248
- # Reset the retry count, since we successfully completed this iteration
249
- retry_count = RETRY_COUNT
250
-
251
- rescue StandardError => e
252
- if retry_count < 1
253
- puts "Retried #{RETRY_COUNT} times... I give up!"
254
- raise e
255
- else
256
- # Exponential falloff...
257
- sleep_time = (50.0 * (1 / (retry_count / 2.0))).to_i
258
-
259
- puts "Caught error: #{e.class.name}: #{e.message}"
260
- puts "#{retry_count} more tries... sleeping #{sleep_time} seconds..."
261
-
262
- sleep sleep_time
263
-
264
- retry_count -= 1
265
-
266
- retry
267
- end
268
- end
269
- end
270
- end
271
-
272
- def display_value(bin_name)
273
- # We're memoizing the display value on the bin objects
274
- # so that when it comes time to reload, the previous value
275
- # is kept.
276
- send(bin_name.normalize).display_value ||=
277
- if @options[:verbose]
278
- send(bin_name.normalize).tickets.map(&:id).join(", ")
279
- else
280
- send(bin_name.normalize).tickets_count
281
- end
282
- end
283
-
284
- def git_cleanup
285
-
286
- end
287
-
288
- # -----------------------------------------
289
- # Class Methods
290
- # -----------------------------------------
291
-
292
- def self.post_notification(title, body)
293
- `growlnotify -H localhost -s -n "Buildmeister" -d "Buildmeister" -t #{title} -m "#{body}"`
294
- end
295
-
296
- def self.normalize_bin_name(bin_name)
297
- bin_name.squeeze(' ').gsub(' ', '_').gsub(/\W/, '').downcase
298
- end
299
-
300
- def self.load_config
301
- YAML.load_file(File.expand_path('~/.buildmeister_config.yml'))
302
- end
303
- end
7
+ require 'buildmeister/string_utils'
8
+ require 'buildmeister/git_utils'
9
+ require 'buildmeister/finder'
10
+ require 'buildmeister/base'
11
+ require 'buildmeister/notifier'
12
+ require 'buildmeister/project'
13
+ require 'buildmeister/bin'
@@ -0,0 +1,146 @@
1
+ require 'optparse'
2
+
3
+ module Buildmeister
4
+ class Base
5
+ include StringUtils
6
+ include GitUtils
7
+
8
+ RETRY_COUNT = 5
9
+
10
+ attr_accessor :projects, :notification_interval
11
+
12
+ def initialize(*args)
13
+ @options = {:mode => :verbose}
14
+
15
+ OptionParser.new do |opts|
16
+ opts.banner = "Usage: buildmeister notify"
17
+
18
+ opts.on('-q', '--quiet', 'Quiet') do
19
+ @options[:mode] = :quiet
20
+ end
21
+
22
+ opts.on('-f', '--from-bin BIN_NAME', 'Move From Bin') do |f|
23
+ @options[:move_from] = f
24
+ end
25
+
26
+ opts.on('-t', '--to-state STATE', 'Move to State') do |t|
27
+ @options[:to_state] = t
28
+ end
29
+
30
+ opts.on('-p', '--project PROJECT', 'Use Project') do |p|
31
+ @options[:project] = p
32
+ end
33
+ end.parse!(args)
34
+
35
+ # Lighthouse setup
36
+ @config = Buildmeister::Base.load_config
37
+
38
+ Lighthouse.account = @config['account']
39
+ Lighthouse.token = @config['token']
40
+
41
+ self.projects = []
42
+
43
+ projects.extend Finder
44
+
45
+ if @options[:project]
46
+ @config['projects'] = @config['projects'].select { |p| p['name'] == @options[:project] }
47
+ raise "#{@options[:project]} did not match any projects in the config file" if @config['projects'].blank?
48
+ end
49
+
50
+ @config['projects'].each do |project|
51
+ self.projects << Buildmeister::Project.new(project, @options)
52
+ end
53
+
54
+ self.notification_interval = @config['notification_interval']
55
+ end
56
+
57
+ def changed?
58
+ projects.any? &:changed?
59
+ end
60
+
61
+ def title
62
+ "Buildmeister: #{Time.now.strftime("%m/%d %I:%M %p")}"
63
+ end
64
+
65
+ def notify
66
+ puts "Starting Buildmeister Notify..."
67
+
68
+ retry_count = RETRY_COUNT
69
+
70
+ loop do
71
+ begin
72
+ body = ''
73
+
74
+ body << projects.map do |project|
75
+ "#{project.display}\n"
76
+ end.join("\n")
77
+
78
+ puts "Updated notification at #{Time.now.strftime("%m/%d %I:%M %p")}"
79
+
80
+ if changed?
81
+ Buildmeister::Notifier.post(title, body)
82
+ end
83
+
84
+ sleep notification_interval.minutes.to_i
85
+
86
+ refresh!
87
+
88
+ # Reset the retry count, since we successfully completed this iteration
89
+ retry_count = RETRY_COUNT
90
+
91
+ rescue StandardError => e
92
+ if retry_count < 1
93
+ puts "Retried #{RETRY_COUNT} times... I give up!"
94
+ raise e
95
+ else
96
+ # Exponential falloff...
97
+ sleep_time = (50.0 * (1 / (retry_count / 2.0))).to_i
98
+
99
+ puts "Caught error: #{e.class.name}: #{e.message}"
100
+ puts "#{retry_count} more tries... sleeping #{sleep_time} seconds..."
101
+
102
+ sleep sleep_time
103
+
104
+ retry_count -= 1
105
+
106
+ retry
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ def move_all
113
+ bin_name = @options[:move_from]
114
+
115
+ if projects.size > 1
116
+ puts "Two projects are loaded (#{projects.map(&:name).join(', ')})"
117
+ puts "Do you really want to move tickets in all projects? [y/n]"
118
+
119
+ choice = gets
120
+
121
+ if choice.downcase.strip == 'n'
122
+ puts "aborting..."
123
+ return
124
+ end
125
+ end
126
+
127
+ projects.each do |project|
128
+ project.bins.named(bin_name).tickets.each do |ticket|
129
+ puts "processing #{project.name}: #{ticket.id}"
130
+ ticket.state = @options[:to_state]
131
+ ticket.save
132
+ end
133
+ end
134
+
135
+ puts "All tickets from bin #{@options[:move_from]} have been moved to #{@options[:to_state]}"
136
+ end
137
+
138
+ def refresh!
139
+ projects.each &:refresh!
140
+ end
141
+
142
+ def self.load_config
143
+ YAML.load_file(File.expand_path('~/.buildmeister_config.yml'))
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,35 @@
1
+ # Wraps Lighthouse::Bin and keeps track of state
2
+
3
+ module Buildmeister
4
+ class Bin
5
+ attr_accessor :bin, :mode, :value, :last_value
6
+
7
+ delegate :name, :tickets, :to => :bin
8
+
9
+ def initialize(lighthouse_bin, mode = :verbose)
10
+ self.bin = lighthouse_bin
11
+ self.mode = mode
12
+
13
+ refresh!
14
+ end
15
+
16
+ def refresh!
17
+ self.last_value = value
18
+
19
+ case mode
20
+ when :verbose
21
+ self.value = bin.tickets.map(&:id).join(', ')
22
+ when :quiet
23
+ self.value = bin.tickets_count
24
+ end
25
+ end
26
+
27
+ def display
28
+ "#{name}: #{value}"
29
+ end
30
+
31
+ def changed?
32
+ value != last_value
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ module Buildmeister
2
+ module Finder
3
+ def named(name)
4
+ find { |e| e.name == name}
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,65 @@
1
+ module Buildmeister
2
+ module GitUtils
3
+ def new_hotfix
4
+ generate_timed_branch('hotfix')
5
+ end
6
+
7
+ def new_experimental
8
+ generate_timed_branch('experimental')
9
+ end
10
+
11
+ def generate_timed_branch(prefix)
12
+ branches = local_branches
13
+ now = Time.now
14
+ count = 1
15
+
16
+ loop do
17
+ new_branch_name = "#{prefix}-#{now.year}-#{now.month.to_s.rjust 2, '0'}-#{now.day.to_s.rjust 2, '0'}-#{count.to_s.rjust 3, '0'}"
18
+ unless branches.include? new_branch_name
19
+ `git checkout -b #{new_branch_name}`
20
+ puts "Created #{new_branch_name}"
21
+ return true
22
+ end
23
+
24
+ count += 1
25
+ end
26
+ end
27
+
28
+ def pull_bin(bin_name = ARGV.shift)
29
+ raise "Multiple projects are loaded. Please use the -p flag to select one project." if projects.size > 1
30
+
31
+ project = projects.first
32
+ bin = project.bins.named(bin_name)
33
+
34
+ raise ArgumentError, "#{bin_name} is not a valid bin! Must be in #{project.bins.map(&:name).join(', ')}" unless bin
35
+
36
+ `git fetch origin`
37
+
38
+ branches = remote_branches
39
+ ticket_numbers = bin.tickets.map { |tkt| tkt.id.to_s }
40
+
41
+ branches_to_pull = branches.select do |branch_name|
42
+ ticket_numbers.map { |tkt_number| branch_name =~ /#{tkt_number}/ }.any?
43
+ end
44
+
45
+ branches_to_pull.each do |branch|
46
+ result = `git pull origin #{branch.gsub("origin/", "")}`
47
+ puts result
48
+ end
49
+ end
50
+
51
+ def local_branches
52
+ `git branch`.split.reject { |name| name == "*" }
53
+ end
54
+
55
+ def remote_branches
56
+ `git branch -r`.split.reject { |name| name == "*" }
57
+ end
58
+
59
+ def current_branch
60
+ branches = `git branch`.split
61
+ i = branches.index "*"
62
+ branches[i + 1]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,7 @@
1
+ module Buildmeister
2
+ class Notifier
3
+ def self.post(title, body)
4
+ `growlnotify -H localhost -s -n "Buildmeister" -d "Buildmeister" -t #{title} -m "#{body}"`
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,45 @@
1
+ module Buildmeister
2
+ class Project
3
+ include StringUtils
4
+
5
+ attr_accessor :project, :name, :bins
6
+
7
+ def initialize(config, options = {})
8
+ self.name = config['name']
9
+ self.bins = []
10
+
11
+ bins.extend Finder
12
+
13
+ self.project = Lighthouse::Project.find(:all).find { |p| p.name == self.name }
14
+
15
+ project_bins = project.bins
16
+
17
+ config['bins'].each do |bin_name|
18
+ bin = project_bins.find { |b| b.name == bin_name }
19
+ raise "No bin named #{bin_name}" unless bin
20
+
21
+ bins << Buildmeister::Bin.new(bin, options[:mode])
22
+ end
23
+ end
24
+
25
+ def display
26
+ out = ''
27
+ out << name + "\n"
28
+ out << "#{divider}\n"
29
+
30
+ bins.each do |bin|
31
+ out << bin.display + "\n"
32
+ end
33
+
34
+ out
35
+ end
36
+
37
+ def refresh!
38
+ bins.each &:refresh!
39
+ end
40
+
41
+ def changed?
42
+ bins.any? &:changed?
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,7 @@
1
+ module Buildmeister
2
+ module StringUtils
3
+ def divider(char = '-', size = 10)
4
+ char * size
5
+ end
6
+ end
7
+ end
data/lib/git_cleanup.rb CHANGED
@@ -7,7 +7,7 @@ require File.expand_path(File.dirname(__FILE__) + "/../lib/buildmeister")
7
7
  class GitCleanup
8
8
 
9
9
  def initialize
10
- @config = Buildmeister.load_config
10
+ @config = Buildmeister::Base.load_config
11
11
  Lighthouse.token = @config['token']
12
12
  Lighthouse.account = @config['account']
13
13
 
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buildmeister::Base do
4
+ before(:each) do
5
+ Buildmeister::Base.stubs(:load_config).returns(load_test_config)
6
+ @project_stub = stub(:name => 'Project', :bins => [stub(), stub()])
7
+ Buildmeister::Project.stubs(:new).returns(@project_stub)
8
+ end
9
+
10
+ describe '#new' do
11
+ it "should create a new instance" do
12
+ b = Buildmeister::Base.new
13
+ b.should be_an_instance_of(Buildmeister::Base)
14
+ end
15
+
16
+ it "should set quiet mode if passed -q" do
17
+ b = Buildmeister::Base.new('-q')
18
+ b.instance_variable_get(:@options)[:mode].should == :quiet
19
+ end
20
+
21
+ it "should set move_from if passed -f" do
22
+ b = Buildmeister::Base.new('-f', 'Cool Bin')
23
+ b.instance_variable_get(:@options)[:move_from].should == 'Cool Bin'
24
+ end
25
+
26
+ it "should set to_state if passed -t" do
27
+ b = Buildmeister::Base.new('-t', 'staged')
28
+ b.instance_variable_get(:@options)[:to_state].should == 'staged'
29
+ end
30
+
31
+ context "passed the -p flag" do
32
+ before(:each) do
33
+ @b = Buildmeister::Base.new('-p', 'Macchiato')
34
+ end
35
+
36
+ it "should set the project" do
37
+ @b.instance_variable_get(:@options)[:project].should == 'Macchiato'
38
+ end
39
+
40
+ it "should only load the specified project" do
41
+ @b.projects.size.should == 1
42
+ end
43
+ end
44
+
45
+
46
+ it "should set the Lighthouse account from config" do
47
+ config = load_test_config
48
+ b = Buildmeister::Base.new
49
+ Lighthouse.account.should == config['account']
50
+ end
51
+
52
+ it "should set the Lighthouse token from config" do
53
+ config = load_test_config
54
+ b = Buildmeister::Base.new
55
+ Lighthouse.token.should == config['token']
56
+ end
57
+
58
+ it "should set up the projects" do
59
+ b = Buildmeister::Base.new
60
+ b.projects.should == [@project_stub, @project_stub]
61
+ end
62
+
63
+ it "should set the notification interval" do
64
+ b = Buildmeister::Base.new
65
+ b.notification_interval.should == 3
66
+ end
67
+ end
68
+
69
+ describe "#changed?" do
70
+ context "with changed bins" do
71
+ before(:each) do
72
+ @project_stub = stub(:changed? => true)
73
+ Buildmeister::Project.stubs(:new).returns(@project_stub)
74
+ end
75
+
76
+ it "should be true" do
77
+ b = Buildmeister::Base.new
78
+ b.changed?.should be_true
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "#projects" do
84
+ before(:each) do
85
+ @project_stub = stub(:name => 'Project', :bins => [stub(), stub()])
86
+ Buildmeister::Project.stubs(:new).returns(@project_stub)
87
+ end
88
+
89
+ it "should search using named" do
90
+ b = Buildmeister::Base.new
91
+ b.projects.named('Project').should == @project_stub
92
+ end
93
+ end
94
+
95
+ describe "title" do
96
+ it "should look like this" do
97
+ Timecop.freeze(Time.parse('1/1/2010 9am')) do
98
+ b = Buildmeister::Base.new
99
+ b.title.should == 'Buildmeister: 01/01 09:00 AM'
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "#divider" do
105
+ before(:each) do
106
+ @b = Buildmeister::Base.new
107
+ end
108
+
109
+ it "looks like this by default" do
110
+ @b.divider.should == "----------"
111
+ end
112
+
113
+ it "accepts character and size arguments" do
114
+ @b.divider("=", 5).should == '====='
115
+ end
116
+ end
117
+
118
+
119
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe Buildmeister::Bin do
4
+ def ticket_stubs
5
+ [stub('ticket_1', :id => 1), stub('ticket_2', :id => 2)]
6
+ end
7
+
8
+ before(:each) do
9
+ Lighthouse::Bin.any_instance.stubs(:tickets).returns(ticket_stubs)
10
+ end
11
+
12
+ describe "#new" do
13
+ it "should create a new instance" do
14
+ b = Buildmeister::Bin.new(Lighthouse::Bin.new)
15
+ end
16
+
17
+ it "should default to verbose mode" do
18
+ b = Buildmeister::Bin.new(Lighthouse::Bin.new)
19
+ b.mode.should == :verbose
20
+ end
21
+
22
+ it "should refresh" do
23
+ Buildmeister::Bin.any_instance.expects(:refresh!).once
24
+ b = Buildmeister::Bin.new(Lighthouse::Bin.new)
25
+ end
26
+ end
27
+
28
+ describe "#refresh!" do
29
+ before(:each) do
30
+ @b = Buildmeister::Bin.new(Lighthouse::Bin.new)
31
+ end
32
+
33
+ context "in verbose mode" do
34
+ it "should call tickets" do
35
+ @b.bin.expects(:tickets).once.returns(ticket_stubs)
36
+ @b.refresh!
37
+ end
38
+
39
+ it "should set value" do
40
+ @b.refresh!
41
+ @b.value.should == '1, 2'
42
+ end
43
+
44
+ context "after the first refresh" do
45
+ before(:each) do
46
+ @b.value = '1, 2'
47
+ end
48
+
49
+ it "should set the last value" do
50
+ @b.refresh!
51
+ @b.last_value.should == '1, 2'
52
+ end
53
+ end
54
+ end
55
+
56
+ context "in quiet mode" do
57
+ before(:each) do
58
+ @b.mode = :quiet
59
+ end
60
+
61
+ it "should call tickets_count" do
62
+ @b.bin.expects(:tickets_count).once.returns(2)
63
+ @b.refresh!
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "#changed?" do
69
+ before(:each) do
70
+ @b = Buildmeister::Bin.new(Lighthouse::Bin.new)
71
+ end
72
+
73
+ context "with identical values" do
74
+ before(:each) do
75
+ @b.value = '1, 2'
76
+ @b.last_value = '1, 2'
77
+ end
78
+
79
+ it "should be false" do
80
+ @b.changed?.should be_false
81
+ end
82
+ end
83
+
84
+ context "with differing values" do
85
+ before(:each) do
86
+ @b.value = '1'
87
+ @b.last_value = '1, 2'
88
+ end
89
+
90
+ it "should be true" do
91
+ @b.changed?.should be_true
92
+ end
93
+ end
94
+ end
95
+
96
+ describe "#display" do
97
+ before(:each) do
98
+ @b = Buildmeister::Bin.new(stub(:name => 'The Bin', :tickets => ticket_stubs))
99
+ end
100
+
101
+ it "displays the ticket" do
102
+ @b.display.should == 'The Bin: 1, 2'
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Buildmeister::Project' do
4
+ def valid_config
5
+ @valid_config ||= load_test_config['projects'].first
6
+ end
7
+
8
+ def project_stub
9
+ stub(:name => valid_config['name'], :bins => valid_config['bins'].map { |b| stub({:name => b}) })
10
+ end
11
+
12
+ before(:each) do
13
+ Lighthouse::Project.stubs(:find).returns([project_stub])
14
+ @p = Buildmeister::Project.new(valid_config)
15
+ end
16
+
17
+ describe "#new" do
18
+ it "should create new given a valid config" do
19
+ @p.should be_an_instance_of(Buildmeister::Project)
20
+ end
21
+
22
+ it "should set the name" do
23
+ @p.name.should == valid_config['name']
24
+ end
25
+
26
+ it "should set the project" do
27
+ @p.project
28
+ end
29
+
30
+ it "should set up the appropriate number of bins" do
31
+ @p.should have(valid_config['bins'].size).bins
32
+ end
33
+
34
+ it "should set up Buildmeister::Bin objects" do
35
+ @p.bins.all? { |b| b.is_a?(Buildmeister::Bin) }.should be_true
36
+ end
37
+
38
+ it "should keep bins in order" do
39
+ @p.bins.map(&:name).should == ['Ready', 'Staged', 'Verified', 'Ready (Experimental)', 'Staged (Experimental)']
40
+ end
41
+ end
42
+
43
+ describe "#changed?" do
44
+ context "with changed bins" do
45
+ before(:each) do
46
+ @p.bins.each { |b| b.stubs(:changed?).returns(true) }
47
+ end
48
+
49
+ it "should be true" do
50
+ @p.changed?.should be_true
51
+ end
52
+ end
53
+
54
+ context "with no changed bins" do
55
+ before(:each) do
56
+ @p.bins.each { |b| b.stubs(:changed?).returns(false) }
57
+ end
58
+
59
+ it "should be false" do
60
+ @p.changed?.should be_false
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#bins" do
66
+ it "should search using named" do
67
+ @p.bins.named('Ready').should be_an_instance_of(Buildmeister::Bin)
68
+ end
69
+ end
70
+
71
+ describe "#display" do
72
+ it "should display the bins" do
73
+ @p.display.should == <<-STRING_CHEESE
74
+ Macchiato
75
+ ----------
76
+ Ready:
77
+ Staged:
78
+ Verified:
79
+ Ready (Experimental):
80
+ Staged (Experimental):
81
+ STRING_CHEESE
82
+ end
83
+ end
84
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,14 @@
1
+ require 'spec/autorun'
2
+ require 'buildmeister'
3
+ require 'fakeweb'
4
+ require 'timecop'
5
+
6
+ FakeWeb.allow_net_connect = false
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.mock_with :mocha
10
+ end
11
+
12
+ def load_test_config
13
+ @test_config ||= YAML.load_file(File.dirname(__FILE__) + '/../config/buildmeister_config.sample.yml')
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: buildmeister
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.6
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leigh Caplan
@@ -9,70 +9,47 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-11 00:00:00 -07:00
13
- default_executable: buildmeister
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: activesupport
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 2.0.0
24
- version:
25
- - !ruby/object:Gem::Dependency
26
- name: texel-lighthouse-api
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 1.0.1
34
- version:
35
- - !ruby/object:Gem::Dependency
36
- name: hoe
37
- type: :development
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: 1.8.3
44
- version:
45
- description: FIX (describe your package)
46
- email:
47
- - lcaplan@onehub.com
12
+ date: 2009-12-10 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Dead simple tools for managing Lighthouse and Git deployment workflow
17
+ email: lcaplan@onehub.com
48
18
  executables:
49
19
  - buildmeister
50
20
  - git_cleanup
51
21
  extensions: []
52
22
 
53
23
  extra_rdoc_files:
54
- - History.txt
55
- - Manifest.txt
56
24
  - README.rdoc
57
25
  files:
58
26
  - History.txt
59
- - Manifest.txt
60
27
  - README.rdoc
61
28
  - Rakefile
29
+ - VERSION
62
30
  - bin/buildmeister
63
31
  - bin/git_cleanup
64
- - config/buildmeister_config.sample.yml
65
32
  - lib/buildmeister.rb
33
+ - lib/buildmeister/base.rb
34
+ - lib/buildmeister/bin.rb
35
+ - lib/buildmeister/finder.rb
36
+ - lib/buildmeister/git_utils.rb
37
+ - lib/buildmeister/notifier.rb
38
+ - lib/buildmeister/project.rb
39
+ - lib/buildmeister/string_utils.rb
66
40
  - lib/git_cleanup.rb
67
- - test/test_buildmeister.rb
41
+ - spec/buildmeister/base_spec.rb
42
+ - spec/buildmeister/bin_spec.rb
43
+ - spec/buildmeister/project_spec.rb
44
+ - spec/spec.opts
45
+ - spec/spec_helper.rb
68
46
  has_rdoc: true
69
- homepage: http://github.com/texel/buildmeister
47
+ homepage: http://github.com/onehub/buildmeister
70
48
  licenses: []
71
49
 
72
50
  post_install_message:
73
51
  rdoc_options:
74
- - --main
75
- - README.txt
52
+ - --charset=UTF-8
76
53
  require_paths:
77
54
  - lib
78
55
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -89,10 +66,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
66
  version:
90
67
  requirements: []
91
68
 
92
- rubyforge_project: buildmeister
69
+ rubyforge_project:
93
70
  rubygems_version: 1.3.5
94
71
  signing_key:
95
- specification_version: 2
96
- summary: FIX (describe your package)
72
+ specification_version: 3
73
+ summary: Dead simple tools for managing Lighthouse and Git deployment workflow
97
74
  test_files:
98
- - test/test_buildmeister.rb
75
+ - spec/buildmeister/base_spec.rb
76
+ - spec/buildmeister/bin_spec.rb
77
+ - spec/buildmeister/project_spec.rb
78
+ - spec/spec_helper.rb
data/Manifest.txt DELETED
@@ -1,10 +0,0 @@
1
- History.txt
2
- Manifest.txt
3
- README.rdoc
4
- Rakefile
5
- bin/buildmeister
6
- bin/git_cleanup
7
- config/buildmeister_config.sample.yml
8
- lib/buildmeister.rb
9
- lib/git_cleanup.rb
10
- test/test_buildmeister.rb
@@ -1,16 +0,0 @@
1
- token: Your Lighthouse token
2
- account: Your Lighthouse account name
3
- project_name: Your Lighthouse project name
4
-
5
- bin_groups:
6
- - staging:
7
- - Ready
8
- - Staged
9
- - Verified
10
-
11
- - master:
12
- - Ready (Experimental)
13
- - Staged (Experimental)
14
- - Verified (Experimental)
15
-
16
- notification_interval: 5
File without changes