bob 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/LICENSE +22 -0
- data/README.rdoc +58 -0
- data/Rakefile +43 -0
- data/bob.gemspec +46 -0
- data/lib/bob.rb +41 -0
- data/lib/bob/background_engines.rb +9 -0
- data/lib/bob/background_engines/foreground.rb +6 -0
- data/lib/bob/builder.rb +53 -0
- data/lib/bob/scm.rb +23 -0
- data/lib/bob/scm/abstract.rb +49 -0
- data/lib/bob/scm/git.rb +57 -0
- data/lib/core_ext/object.rb +7 -0
- data/test/bob_test.rb +22 -0
- data/test/helper.rb +32 -0
- data/test/helper/buildable_stub.rb +55 -0
- data/test/helper/git_helper.rb +48 -0
- metadata +119 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2008-2009 Nicolas Sanguinetti, entp.com
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
= Bob the Builder
|
2
|
+
|
3
|
+
Given a Buildable object with the following public API:
|
4
|
+
|
5
|
+
* <tt>buildable.kind</tt>
|
6
|
+
|
7
|
+
Should return a Symbol with whatever kind of repository the buildable's code is
|
8
|
+
in (:git, :svn, etc).
|
9
|
+
|
10
|
+
* <tt>buildable.uri</tt>
|
11
|
+
|
12
|
+
Returns a string like "git://github.com/integrity/bob.git", pointing to the code
|
13
|
+
repository.
|
14
|
+
|
15
|
+
* <tt>buildable.branch</tt>
|
16
|
+
|
17
|
+
What branch of the repository should we build?
|
18
|
+
|
19
|
+
* <tt>buildable.build_script</tt>
|
20
|
+
|
21
|
+
Returns a string containing the build script to be run when "building".
|
22
|
+
|
23
|
+
* <tt>buildable.start_building(commit_id)</tt>
|
24
|
+
|
25
|
+
`commit_id` is a String that contains whatever is appropriate for the repo type,
|
26
|
+
so it would be a SHA1 hash for git repos, or a numeric id for svn, etc. This is a
|
27
|
+
callback so the buildable can determine how long it takes to build. It doesn't
|
28
|
+
need to return anything.
|
29
|
+
|
30
|
+
* <tt>buildable.finish_building(commit_id, build_status, build_output)</tt>
|
31
|
+
|
32
|
+
Callback for when the build finishes. It doesn't need to return anything. It will
|
33
|
+
receive a string with the commit identifier, a boolean for the build exit status
|
34
|
+
(true for successful builds, false fore failed ones) and a string with the build
|
35
|
+
output (both STDOUT and STDERR).
|
36
|
+
|
37
|
+
A successful build is one where the build script returns a zero status code.
|
38
|
+
|
39
|
+
Bob will, when called like:
|
40
|
+
|
41
|
+
Bob.build(buildable, commit_id)
|
42
|
+
|
43
|
+
1. Checkout the buildable on the specified commit
|
44
|
+
2. Call <tt>buildable.start_building</tt>
|
45
|
+
3. Run the script provided in <tt>build_script</tt> in the buildable.
|
46
|
+
4. When the build process finishes, it will call <tt>finish_building</tt> with
|
47
|
+
the commit_id, the build status (true if the script returns a status code
|
48
|
+
of 0, false otherwise), and a string with the build output (both STDOUT and STDERR).
|
49
|
+
|
50
|
+
== Do I need this?
|
51
|
+
|
52
|
+
Probably not. Check out integrity[http://integrityapp.com] for a full fledged
|
53
|
+
automated CI server, which is what most people need.
|
54
|
+
|
55
|
+
== Credits
|
56
|
+
|
57
|
+
Authors:: Nicolas Sanguinetti (foca[http://github.com/foca]) and Simon Rozet (sr[http://github.com/sr])
|
58
|
+
License:: MIT (Check LICENSE for details)
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "hanna/rdoctask"
|
5
|
+
rescue LoadError
|
6
|
+
require "rake/rdoctask"
|
7
|
+
end
|
8
|
+
|
9
|
+
begin
|
10
|
+
require "metric_fu"
|
11
|
+
rescue LoadError
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
require "mg"
|
16
|
+
MG.new("bob.gemspec")
|
17
|
+
rescue LoadError
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Default: run all tests"
|
21
|
+
task :default => :test
|
22
|
+
|
23
|
+
SCMs = %w[git svn]
|
24
|
+
|
25
|
+
desc "Run unit tests"
|
26
|
+
task :test => SCMs.map { |scm| "test:#{scm}" } do
|
27
|
+
ruby "test/bob_test.rb"
|
28
|
+
ruby "test/background_engine/threaded_test.rb"
|
29
|
+
end
|
30
|
+
|
31
|
+
SCMs.each { |scm|
|
32
|
+
desc "Run unit tests with #{scm}"
|
33
|
+
task "test:#{scm}" do
|
34
|
+
ruby "test/scm/#{scm}_test.rb"
|
35
|
+
end
|
36
|
+
}
|
37
|
+
|
38
|
+
Rake::RDocTask.new do |rd|
|
39
|
+
rd.main = "README"
|
40
|
+
rd.title = "Documentation for Bob the Builder"
|
41
|
+
rd.rdoc_files.include("README.rdoc", "LICENSE", "lib/**/*.rb")
|
42
|
+
rd.rdoc_dir = "doc"
|
43
|
+
end
|
data/bob.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "bob"
|
3
|
+
s.version = "0.1"
|
4
|
+
s.date = "2009-04-12"
|
5
|
+
|
6
|
+
s.description = "Bob the Builder will build your code. Simple."
|
7
|
+
s.summary = "Bob builds!"
|
8
|
+
s.homepage = "http://integrityapp.com"
|
9
|
+
|
10
|
+
s.authors = ["Nicolás Sanguinetti", "Simon Rozet"]
|
11
|
+
s.email = "info@integrityapp.com"
|
12
|
+
|
13
|
+
s.require_paths = ["lib"]
|
14
|
+
s.rubyforge_project = "integrity"
|
15
|
+
s.has_rdoc = true
|
16
|
+
s.rubygems_version = "1.3.1"
|
17
|
+
|
18
|
+
s.add_dependency "addressable"
|
19
|
+
|
20
|
+
if s.respond_to?(:add_development_dependency)
|
21
|
+
s.add_development_dependency "sr-mg"
|
22
|
+
s.add_development_dependency "contest"
|
23
|
+
s.add_development_dependency "redgreen"
|
24
|
+
s.add_development_dependency "ruby-debug"
|
25
|
+
end
|
26
|
+
|
27
|
+
s.files = %w[
|
28
|
+
.gitignore
|
29
|
+
LICENSE
|
30
|
+
README.rdoc
|
31
|
+
Rakefile
|
32
|
+
bob.gemspec
|
33
|
+
lib/bob.rb
|
34
|
+
lib/bob/background_engines.rb
|
35
|
+
lib/bob/background_engines/foreground.rb
|
36
|
+
lib/bob/builder.rb
|
37
|
+
lib/bob/scm.rb
|
38
|
+
lib/bob/scm/abstract.rb
|
39
|
+
lib/bob/scm/git.rb
|
40
|
+
lib/core_ext/object.rb
|
41
|
+
test/bob_test.rb
|
42
|
+
test/helper.rb
|
43
|
+
test/helper/git_helper.rb
|
44
|
+
test/helper/buildable_stub.rb
|
45
|
+
]
|
46
|
+
end
|
data/lib/bob.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "yaml"
|
3
|
+
require "logger"
|
4
|
+
require "time"
|
5
|
+
require "addressable/uri"
|
6
|
+
|
7
|
+
require "bob/builder"
|
8
|
+
require "bob/scm"
|
9
|
+
require "bob/background_engines"
|
10
|
+
|
11
|
+
module Bob
|
12
|
+
# Builds the specified <tt>buildable</tt>. This object must understand
|
13
|
+
# the API described in the README.
|
14
|
+
def self.build(buildable, commit_ids)
|
15
|
+
Array(commit_ids).each do |commit_id|
|
16
|
+
Builder.new(buildable, commit_id).build
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Directory where the code for the different buildables will be checked out. Make sure
|
21
|
+
# the user running Bob is allowed to write to this directory.
|
22
|
+
def self.directory
|
23
|
+
@directory || "/tmp"
|
24
|
+
end
|
25
|
+
|
26
|
+
# What will you use to build in background. Must respond to <tt>call</tt> and take a block
|
27
|
+
# which will be run "in background". The default is to run in foreground.
|
28
|
+
def self.engine
|
29
|
+
@engine || BackgroundEngines::Foreground
|
30
|
+
end
|
31
|
+
|
32
|
+
# What to log with (must implement ruby's Logger interface). Logs to STDOUT by
|
33
|
+
# default.
|
34
|
+
def self.logger
|
35
|
+
@logger || Logger.new(STDOUT)
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
attr_writer :directory, :engine, :logger
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Bob
|
2
|
+
# Different engines to run code in background. An engine is any object
|
3
|
+
# that responds to #call and takes a Proc object, which should be executed
|
4
|
+
# "in the background". The different engines are:
|
5
|
+
module BackgroundEngines
|
6
|
+
autoload :Foreground, "bob/background_engines/foreground"
|
7
|
+
autoload :Threaded, "bob/background_engines/threaded"
|
8
|
+
end
|
9
|
+
end
|
data/lib/bob/builder.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Bob
|
2
|
+
# A Builder will take care of building a buildable (wow, you didn't see that coming,
|
3
|
+
# right?).
|
4
|
+
class Builder
|
5
|
+
attr_reader :buildable, :commit_id
|
6
|
+
|
7
|
+
def initialize(buildable, commit_id)
|
8
|
+
@buildable = buildable
|
9
|
+
@commit_id = commit_id
|
10
|
+
end
|
11
|
+
|
12
|
+
# This is where the magic happens:
|
13
|
+
#
|
14
|
+
# 1. Check out the repo to the appropriate commit.
|
15
|
+
# 2. Notify the buildable that the build is starting.
|
16
|
+
# 3. Run the build script on it in the background.
|
17
|
+
# 4. Reports the build back to the buildable.
|
18
|
+
def build
|
19
|
+
Bob.logger.info "Building #{commit_id} of the #{buildable.kind} repo at #{buildable.uri}"
|
20
|
+
in_background do
|
21
|
+
scm.with_commit(commit_id) do
|
22
|
+
buildable.start_building(commit_id, scm.info(commit_id))
|
23
|
+
build_status, build_output = run_build_script
|
24
|
+
buildable.finish_building(commit_id, build_status, build_output)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def run_build_script
|
32
|
+
build_output = nil
|
33
|
+
|
34
|
+
Bob.logger.debug "Running the build script for #{buildable.uri}"
|
35
|
+
IO.popen(build_script, "r") { |output| build_output = output.read }
|
36
|
+
Bob.logger.debug("Ran build script `#{build_script}` and got:\n#{build_output}")
|
37
|
+
|
38
|
+
[$?.success?, build_output]
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_script
|
42
|
+
"(cd #{scm.working_dir} && #{buildable.build_script} 2>&1)"
|
43
|
+
end
|
44
|
+
|
45
|
+
def scm
|
46
|
+
@scm ||= SCM.new(buildable.kind, buildable.uri, buildable.branch)
|
47
|
+
end
|
48
|
+
|
49
|
+
def in_background(&block)
|
50
|
+
Bob.engine.call(block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/bob/scm.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "bob/scm/abstract"
|
2
|
+
|
3
|
+
module Bob
|
4
|
+
module SCM
|
5
|
+
autoload :Git, "bob/scm/git"
|
6
|
+
autoload :Svn, "bob/scm/svn"
|
7
|
+
|
8
|
+
class CantRunCommand < RuntimeError; end
|
9
|
+
|
10
|
+
# Factory to return appropriate SCM instances (according to repository kind)
|
11
|
+
def self.new(kind, uri, branch)
|
12
|
+
class_for(kind).new(uri, branch)
|
13
|
+
end
|
14
|
+
|
15
|
+
# A copy of Inflector.camelize, from ActiveSupport. It will convert
|
16
|
+
# string to UpperCamelCase.
|
17
|
+
def self.class_for(kind)
|
18
|
+
class_name = kind.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
19
|
+
const_get(class_name)
|
20
|
+
end
|
21
|
+
private_class_method :class_for
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Bob
|
2
|
+
module SCM
|
3
|
+
class Abstract
|
4
|
+
attr_reader :uri, :branch
|
5
|
+
|
6
|
+
def initialize(uri, branch)
|
7
|
+
@uri = Addressable::URI.parse(uri)
|
8
|
+
@branch = branch
|
9
|
+
end
|
10
|
+
|
11
|
+
# Checkout the code into <tt>working_dir</tt> at the specified revision and
|
12
|
+
# call the passed block
|
13
|
+
def with_commit(commit_id)
|
14
|
+
update_code
|
15
|
+
checkout(commit_id)
|
16
|
+
yield
|
17
|
+
end
|
18
|
+
|
19
|
+
# Directory where the code will be checked out. Make sure the user running Bob is
|
20
|
+
# allowed to write to this directory (or you'll get a <tt>Errno::EACCESS</tt>)
|
21
|
+
def working_dir
|
22
|
+
@working_dir ||= "#{Bob.directory}/#{path_from_uri}".tap do |path|
|
23
|
+
FileUtils.mkdir_p path
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get some information about the specified commit. Returns a hash with:
|
28
|
+
#
|
29
|
+
# [<tt>:author</tt>] Commit author's name and email
|
30
|
+
# [<tt>:message</tt>] Commit message
|
31
|
+
# [<tt>:committed_at</tt>] Commit date (as a <tt>Time</tt> object)
|
32
|
+
def info(commit_id)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def run(command)
|
39
|
+
command = "(cd #{working_dir} && #{command} &>/dev/null)"
|
40
|
+
Bob.logger.debug command
|
41
|
+
system(command) || raise(CantRunCommand, "Couldn't run SCM command `#{command}`")
|
42
|
+
end
|
43
|
+
|
44
|
+
def path_from_uri
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/bob/scm/git.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Bob
|
2
|
+
module SCM
|
3
|
+
class Git < Abstract
|
4
|
+
def info(commit_id)
|
5
|
+
format = %Q(---%n:author: %an <%ae>%n:message: >-%n %s%n:committed_at: %ci%n)
|
6
|
+
YAML.load(`cd #{working_dir} && git show -s --pretty=format:"#{format}" #{commit_id}`).tap do |info|
|
7
|
+
info[:committed_at] = Time.parse(info[:committed_at])
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def path_from_uri
|
14
|
+
path = uri.path.
|
15
|
+
gsub(/\~[a-z0-9]*\//i, ""). # remove ~foobar/
|
16
|
+
gsub(/\s+|\.|\//, "-"). # periods, spaces, slashes -> hyphens
|
17
|
+
gsub(/^-+|-+$/, "") # remove trailing hyphens
|
18
|
+
path += "-#{branch}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def update_code
|
24
|
+
cloned? ? fetch : clone
|
25
|
+
end
|
26
|
+
|
27
|
+
def cloned?
|
28
|
+
File.directory?("#{working_dir}/.git")
|
29
|
+
end
|
30
|
+
|
31
|
+
def clone
|
32
|
+
git "clone #{uri} #{working_dir}"
|
33
|
+
rescue CantRunCommand
|
34
|
+
FileUtils.rm_r working_dir
|
35
|
+
retry
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch
|
39
|
+
git "fetch origin"
|
40
|
+
end
|
41
|
+
|
42
|
+
def checkout(commit_id)
|
43
|
+
# First checkout the branch just in case the commit_id turns out to be HEAD or other non-sha identifier
|
44
|
+
git "checkout origin/#{branch}"
|
45
|
+
git "reset --hard #{commit_id}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def reset(commit_id)
|
49
|
+
git "reset --hard #{commit_id}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def git(command)
|
53
|
+
run "git #{command}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/test/bob_test.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/helper"
|
2
|
+
|
3
|
+
class BobTest < Test::Unit::TestCase
|
4
|
+
test "directory" do
|
5
|
+
Bob.directory = "/foo/bar"
|
6
|
+
assert_equal "/foo/bar", Bob.directory
|
7
|
+
end
|
8
|
+
|
9
|
+
test "logger" do
|
10
|
+
logger = Logger.new("/tmp/bob.log")
|
11
|
+
Bob.logger = logger
|
12
|
+
|
13
|
+
assert_same logger, Bob.logger
|
14
|
+
end
|
15
|
+
|
16
|
+
test "engine" do
|
17
|
+
engine = Object.new
|
18
|
+
Bob.engine = engine
|
19
|
+
|
20
|
+
assert_same engine, Bob.engine
|
21
|
+
end
|
22
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "contest"
|
3
|
+
require "hpricot"
|
4
|
+
|
5
|
+
begin
|
6
|
+
require "redgreen"
|
7
|
+
require "ruby-debug"
|
8
|
+
rescue LoadError
|
9
|
+
end
|
10
|
+
|
11
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"),
|
12
|
+
File.expand_path(File.dirname(__FILE__) + "/../test/helper"))
|
13
|
+
|
14
|
+
require "bob"
|
15
|
+
require "git_helper"
|
16
|
+
require "svn_helper"
|
17
|
+
require "buildable_stub"
|
18
|
+
|
19
|
+
Bob.logger = Logger.new("/dev/null")
|
20
|
+
Bob.engine = Bob::BackgroundEngines::Foreground
|
21
|
+
Bob.directory = File.expand_path(File.dirname(__FILE__) + "/../tmp")
|
22
|
+
|
23
|
+
class Test::Unit::TestCase
|
24
|
+
include Bob
|
25
|
+
include TestHelper
|
26
|
+
|
27
|
+
attr_reader :repo, :buildable
|
28
|
+
|
29
|
+
def setup
|
30
|
+
FileUtils.rm_rf(Bob.directory)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module TestHelper
|
2
|
+
module BuildableStub
|
3
|
+
attr_reader :repo, :builds, :metadata
|
4
|
+
|
5
|
+
def initialize(repo)
|
6
|
+
@repo = repo
|
7
|
+
@builds = {}
|
8
|
+
@metadata = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_script
|
12
|
+
"./test"
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_building(commit_id, commit_info)
|
16
|
+
@metadata[commit_id] = commit_info
|
17
|
+
end
|
18
|
+
|
19
|
+
def finish_building(commit_id, status, output)
|
20
|
+
@builds[commit_id] = [status ? :successful : :failed, output]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class GitBuildableStub
|
25
|
+
include BuildableStub
|
26
|
+
|
27
|
+
def kind
|
28
|
+
:git
|
29
|
+
end
|
30
|
+
|
31
|
+
def uri
|
32
|
+
repo.path
|
33
|
+
end
|
34
|
+
|
35
|
+
def branch
|
36
|
+
"master"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class SvnBuildableStub
|
41
|
+
include BuildableStub
|
42
|
+
|
43
|
+
def kind
|
44
|
+
:svn
|
45
|
+
end
|
46
|
+
|
47
|
+
def uri
|
48
|
+
"file://#{SvnRepo.server_root}/#{repo.name}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def branch
|
52
|
+
""
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/abstract_scm_helper"
|
2
|
+
|
3
|
+
module TestHelper
|
4
|
+
class GitRepo < AbstractSCMRepo
|
5
|
+
def create
|
6
|
+
FileUtils.mkdir_p @path
|
7
|
+
|
8
|
+
Dir.chdir(@path) do
|
9
|
+
system 'git init &>/dev/null'
|
10
|
+
system 'git config user.name "John Doe"'
|
11
|
+
system 'git config user.email "johndoe@example.org"'
|
12
|
+
system 'echo "just a test repo" >> README'
|
13
|
+
add 'README &>/dev/null'
|
14
|
+
commit "First commit"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def commits
|
19
|
+
Dir.chdir(@path) do
|
20
|
+
commits = `git log --pretty=oneline`.collect { |l| l.split(" ").first }
|
21
|
+
commits.inject([]) do |commits, sha1|
|
22
|
+
format = "---%n:message: >-%n %s%n:timestamp: %ci%n" +
|
23
|
+
":identifier: %H%n:author: %n :name: %an%n :email: %ae%n"
|
24
|
+
commits << YAML.load(`git show -s --pretty=format:"#{format}" #{sha1}`)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def head
|
30
|
+
Dir.chdir(@path) do
|
31
|
+
`git log --pretty=format:%H | head -1`.chomp
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def short_head
|
36
|
+
head[0..6]
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
def add(file)
|
41
|
+
system "git add #{file}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def commit(message)
|
45
|
+
system %Q{git commit -m "#{message}" &>/dev/null}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bob
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Nicol\xC3\xA1s Sanguinetti"
|
8
|
+
- Simon Rozet
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-04-12 00:00:00 -03:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: addressable
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: sr-mg
|
28
|
+
type: :development
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: contest
|
38
|
+
type: :development
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: redgreen
|
48
|
+
type: :development
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: ruby-debug
|
58
|
+
type: :development
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
description: Bob the Builder will build your code. Simple.
|
67
|
+
email: info@integrityapp.com
|
68
|
+
executables: []
|
69
|
+
|
70
|
+
extensions: []
|
71
|
+
|
72
|
+
extra_rdoc_files: []
|
73
|
+
|
74
|
+
files:
|
75
|
+
- .gitignore
|
76
|
+
- LICENSE
|
77
|
+
- README.rdoc
|
78
|
+
- Rakefile
|
79
|
+
- bob.gemspec
|
80
|
+
- lib/bob.rb
|
81
|
+
- lib/bob/background_engines.rb
|
82
|
+
- lib/bob/background_engines/foreground.rb
|
83
|
+
- lib/bob/builder.rb
|
84
|
+
- lib/bob/scm.rb
|
85
|
+
- lib/bob/scm/abstract.rb
|
86
|
+
- lib/bob/scm/git.rb
|
87
|
+
- lib/core_ext/object.rb
|
88
|
+
- test/bob_test.rb
|
89
|
+
- test/helper.rb
|
90
|
+
- test/helper/git_helper.rb
|
91
|
+
- test/helper/buildable_stub.rb
|
92
|
+
has_rdoc: true
|
93
|
+
homepage: http://integrityapp.com
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: "0"
|
104
|
+
version:
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: "0"
|
110
|
+
version:
|
111
|
+
requirements: []
|
112
|
+
|
113
|
+
rubyforge_project: integrity
|
114
|
+
rubygems_version: 1.3.1
|
115
|
+
signing_key:
|
116
|
+
specification_version: 2
|
117
|
+
summary: Bob builds!
|
118
|
+
test_files: []
|
119
|
+
|