Permalink
Browse files

Merge branch 'master' of git://github.com/integrity/bob

* 'master' of git://github.com/integrity/bob: (49 commits)
  Made tests pass for regular OS X ruby
  Support passing :head as a valid commit_id to Bob.build
  Push version to 0.1.1 now that we have the Buildable mixin
  Fix the mess with cloning and directories that either exist or don't
  Strip whitespace
  Ruby1.9 compat
  Fix that test depends on Time.now
  Fix git tests and test setup
  Weird errors...
  Uh, oops? order of include *is* relevant
  Add a buildable mixin
  Get rid of useless Git#reset method
  Move Bob configuration for tests in setup
  Update manifest
  Renamed to bob-the-builder as bob was already taken on rubyforge
  Reorganize tests
  Code style nazi
  Not using OpenStruct
  DRY-up test case accessors
  Get rid BobGitTest#commit_id
  ...

Conflicts:
	Rakefile
	lib/bob/background_engines/threaded.rb
  • Loading branch information...
2 parents 18bb5c5 + 1805769 commit a70d111e05f48e213692e3f4cc0a2bf576b1d1d9 @raggi committed May 14, 2009
View
@@ -1,52 +1,24 @@
= Bob the Builder
-Given a Buildable object with the following public API:
-
-* <tt>buildable.kind</tt>
-
- Should return a Symbol with whatever kind of repository the buildable's code is
- in (:git, :svn, etc).
-
-* <tt>buildable.uri</tt>
-
- Returns a string like "git://github.com/integrity/bob.git", pointing to the code
- repository.
-
-* <tt>buildable.branch</tt>
-
- What branch of the repository should we build?
-
-* <tt>buildable.build_script</tt>
-
- Returns a string containing the build script to be run when "building".
-
-* <tt>buildable.start_building(commit_id)</tt>
-
- `commit_id` is a String that contains whatever is appropriate for the repo type,
- so it would be a SHA1 hash for git repos, or a numeric id for svn, etc. This is a
- callback so the buildable can determine how long it takes to build. It doesn't
- need to return anything.
-
-* <tt>buildable.finish_building(commit_id, build_status, build_output)</tt>
-
- Callback for when the build finishes. It doesn't need to return anything. It will
- receive a string with the commit identifier, a boolean for the build exit status
- (true for successful builds, false fore failed ones) and a string with the build
- output (both STDOUT and STDERR).
+Given a Buildable object with a determined API (described in it's documentation),
+Bob will, when called like:
- A successful build is one where the build script returns a zero status code.
+ Bob.build(buildable, commit_id) # or Bob.build(buildable, [commit_id, commit_id, ...])
-Bob will, when called like:
+or from your buildable (if you are using the Bob::Buildable mixin provided) as:
- Bob.build(buildable, commit_id)
+ buildable.build(commit_id) # or buildable.build([commit_id, commit_id, ...])
1. Checkout the buildable on the specified commit
-2. Call <tt>buildable.start_building</tt>
-3. Run the script provided in <tt>build_script</tt> in the buildable.
-4. When the build process finishes, it will call <tt>finish_building</tt> with
+2. Call <tt>Buildable#start_building</tt>
+3. Run the script provided in <tt>Buildable#build_script</tt> in the buildable.
+4. When the build process finishes, it will call <tt>Buildable#finish_building</tt> with
the commit_id, the build status (true if the script returns a status code
of 0, false otherwise), and a string with the build output (both STDOUT and STDERR).
+If you pass an array of commits, the steps 1-4 will be repeated for each commit provided,
+in order.
+
== Do I need this?
Probably not. Check out integrity[http://integrityapp.com] for a full fledged
View
@@ -11,24 +11,34 @@ rescue LoadError
end
begin
- require "metric_fu"
+ require "metric_fu" if RUBY_VERSION < "1.9"
rescue LoadError
end
begin
require "mg"
- MG.new("bob.gemspec")
+ MG.new("bob-the-builder.gemspec")
rescue LoadError
end
desc "Default: run all tests"
task :default => :test
+SCMs = %w[git svn]
+
desc "Run unit tests"
-Rake::TestTask.new(:test) do |t|
- t.test_files = FileList["test/*_test.rb"]
+task :test => SCMs.map { |scm| "test:#{scm}" } do
+ ruby "test/bob_test.rb"
+ ruby "test/background_engine/threaded_test.rb"
end
+SCMs.each { |scm|
+ desc "Run unit tests with #{scm}"
+ task "test:#{scm}" do
+ ruby "test/scm/#{scm}_test.rb"
+ end
+}
+
(defined?(RDoc::Task) ? RDoc::Task : Rake::RDocTask).new do |rd|
rd.main = "README.rdoc"
rd.title = "Documentation for Bob the Builder"
@@ -1,7 +1,7 @@
Gem::Specification.new do |s|
- s.name = "bob"
- s.version = "0.1"
- s.date = "2009-04-12"
+ s.name = "bob-the-builder"
+ s.version = "0.1.1"
+ s.date = "2009-05-08"
s.description = "Bob the Builder will build your code. Simple."
s.summary = "Bob builds!"
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
s.email = "info@integrityapp.com"
s.require_paths = ["lib"]
- s.rubyforge_project = "integrity"
+ s.rubyforge_project = "bob-the-builder"
s.has_rdoc = true
s.rubygems_version = "1.3.1"
@@ -29,18 +29,26 @@ Gem::Specification.new do |s|
LICENSE
README.rdoc
Rakefile
-bob.gemspec
+bob-the-builder.gemspec
lib/bob.rb
lib/bob/background_engines.rb
lib/bob/background_engines/foreground.rb
+lib/bob/background_engines/threaded.rb
lib/bob/builder.rb
+lib/bob/buildable.rb
lib/bob/scm.rb
lib/bob/scm/abstract.rb
lib/bob/scm/git.rb
+lib/bob/scm/svn.rb
lib/core_ext/object.rb
+test/background_engine/threaded_test.rb
test/bob_test.rb
test/helper.rb
+test/helper/abstract_scm_helper.rb
+test/helper/buildable_stub.rb
test/helper/git_helper.rb
-test/helper/stub_buildable.rb
+test/helper/svn_helper.rb
+test/scm/git_test.rb
+test/scm/svn_test.rb
]
end
View
@@ -4,34 +4,49 @@
require "time"
require "addressable/uri"
+require "bob/buildable"
require "bob/builder"
require "bob/scm"
-require "bob/scm/git"
require "bob/background_engines"
+require "core_ext/object"
module Bob
# Builds the specified <tt>buildable</tt>. This object must understand
# the API described in the README.
+ #
+ # The second argument will take an array of commit_ids, which should be
+ # strings with the relevant identifier (a SHA1 hash for git repositories,
+ # a numerical revision for svn repositories, etc).
+ #
+ # You can pass :head as a commit identifier to build the latest commit
+ # in the repo. Examples:
+ #
+ # Bob.build(buildable, :head) # just build the head
+ # Bob.build(buildable, ["4", "3", "2"]) # build revision 4, 3, and 2
+ # # (in that order)
+ # Bob.build(buildable, [:head, "a30fb12"]) # build the HEAD and a30fb12
+ # # commits in this repo.
def self.build(buildable, commit_ids)
Array(commit_ids).each do |commit_id|
Builder.new(buildable, commit_id).build
end
end
- # Directory where the code for the different buildables will be checked out. Make sure
- # the user running Bob is allowed to write to this directory.
+ # Directory where the code for the different buildables will be checked out.
+ # Make sure the user running Bob is allowed to write to this directory.
def self.directory
- @checkout_directory || "/tmp"
+ @directory || "/tmp"
end
- # What will you use to build in background. Must respond to <tt>call</tt> and take a block
- # which will be run "in background". The default is to run in foreground.
+ # What will you use to build in background. Must respond to <tt>call</tt> and
+ # take a block which will be run "in background". The default is to run in
+ # foreground.
def self.engine
@engine || BackgroundEngines::Foreground
end
- # What to log with (must implement ruby's Logger interface). Logs to STDOUT by
- # default.
+ # What to log with (must implement ruby's Logger interface). Logs to STDOUT
+ # by default.
def self.logger
@logger || Logger.new(STDOUT)
end
@@ -1,4 +1,5 @@
-require 'thread'
+require "thread"
+
module Bob
module BackgroundEngines
# A thread pool based build engine. This engine simply adds jobs to an
@@ -38,22 +39,27 @@ def initialize(v = 0)
@m = Mutex.new
@v = v
end
+
# Add the given value to self, default 1.
def inc(v = 1)
sync { @v += v }
end
+
# Subtract the given value to self, default 1.
def dec(v = 1)
sync { @v -= v }
end
+
# Simply shows the value inspect for convenience.
def inspect
@v.inspect
end
+
# Extract the value.
def to_i
@v
end
+
private
# Wrap the given block in a mutex.
def sync(&b)
@@ -69,8 +75,11 @@ def sync(&b)
# Set the size of the thread pool. Asynchronously run down threads
# that are no longer required, and synchronously spawn new required
# threads.
+ attr_reader :size, :jobs
+
def size=(other)
@size = other
+
if @workers.size > @size
(@workers.size - @size).times do
@workers.shift[:run] = false
@@ -85,22 +94,24 @@ def size=(other)
# Default pool size is 2 threads.
def initialize(size = nil)
size ||= 2
- @jobs = Queue.new
- @njobs = Incrementor.new
+ @jobs = Queue.new
+ @njobs = Incrementor.new
@workers = Array.new(size) { spawn }
end
# Adds a job to the queue, the job can be any number of objects
# responding to call, and/or a block.
def add(*jobs, &blk)
jobs = jobs + Array(blk)
+
jobs.each do |job|
@jobs << job
@njobs.inc
end
end
- alias push add
- alias :<< add
+
+ alias_method :push, :add
+ alias_method :<<, :add
# A peak at the number of jobs in the queue. N.B. May differ, but
# should be more accurate than +jobs.size+.
@@ -109,12 +120,14 @@ def njobs
end
private
+
# Create a new thread and return it. The thread will run until the
# thread-local value +:run+ is changed to false or nil.
def spawn
Thread.new do
c = Thread.current
c[:run] = true
+
while c[:run]
@jobs.pop.call
@njobs.dec
@@ -124,4 +137,4 @@ def spawn
end
end
end
-end
+end
View
@@ -0,0 +1,70 @@
+module Bob
+ # Mixin to add to your classes.
+ module Buildable
+ # Builds the list of commits you pass. You must provide an
+ # enumerable with commit identifiers appropriate to the
+ # repository the code is in (SHAs if git, rev numbers if svn,
+ # etc), or a single string with a commit id.
+ def build(commits)
+ Bob.build(self, commits)
+ end
+
+ # What kind of repository this buildable represents. Must
+ # return a Symbol (:git, :svn, etc.)
+ #
+ # <b>You must implement this in the classes where you mixin this module</b>
+ def kind
+ raise NotImplementedError
+ end
+
+ # Full URI to the repository to clone/checkout.
+ #
+ # <b>You must implement this in the classes where you mixin this module</b>
+ def uri
+ raise NotImplementedError
+ end
+
+ # Branch of the code you want to watch in order to build.
+ #
+ # <b>You must implement this in the classes where you mixin this module</b>
+ def branch
+ raise NotImplementedError
+ end
+
+ # Script that will be run in the buildable's checked out code,
+ # if it returns a status code of 0 it will be considered a
+ # successfull build. Else it will be considered a failed build.
+ #
+ # <b>You must implement this in the classes where you mixin this module</b>
+ def build_script
+ raise NotImplementedError
+ end
+
+ # Callback sent when a build starts. The first argument is a
+ # string with whatever identifier is appropriate for a repository
+ # of this kind. The second is a hash with information about the
+ # commit.
+ #
+ # <tt>:author</tt>:: A string with the name/email of the committer
+ # <tt>:message</tt>:: The commit message
+ # <tt>:committed_at</tt>:: A Time object with the timestamp of the
+ # commit
+ #
+ # <b>You must implement this in the classes where you mixin this module</b>
+ def start_building(commit_id, commit_info)
+ raise NotImplementedError
+ end
+
+ # Callback sent after a build finishes. The first argument is a
+ # string with whatever identifier is appropriate for a respository
+ # of this kind. The second is a boolean which is true if the build
+ # was successful or false if it failed. And the last one is a string
+ # with the full output returned by the build process (STDOUT and
+ # STDERR interleaved)
+ #
+ # <b>You must implement this in the classes where you mixin this module</b>
+ def finish_building(commit_id, build_status, build_output)
+ raise NotImplementedError
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit a70d111

Please sign in to comment.