spitball 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -8,3 +8,22 @@ deployment.
8
8
  Also comes with `spitball-server`, a small sinatra app that you can run
9
9
  on a dedicated build server. The `spitball` command line client can then
10
10
  pull packages down from said server.
11
+
12
+ ### Usage
13
+
14
+ Usage: spitball [options] GEMFILE ARCHIVE
15
+
16
+ options:
17
+ -h, --host HOST Get the tarball from a remote spitball server
18
+ -p, --port PORT Specify the remote server port. Default 8080
19
+ --without a,b,c Excluded groups in the tarball. Does not apply to remote spitballs
20
+ --version
21
+
22
+ environment variables:
23
+ SPITBALL_CACHE Specifies the cache dir. Defaults to /tmp/spitball
24
+
25
+ ### TODO
26
+
27
+ Lots of things are changing in bundler 1.0. We're stuck on 0.9.5 for
28
+ now, but once we get to 1.0, this tool will probably work with lock
29
+ files instead of gem files, for more predictable builds.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.5
1
+ 0.2.0
@@ -1,19 +1,31 @@
1
1
  require 'fileutils'
2
2
 
3
- module Spitball::FileLock
4
- def acquire_lock(lock_path)
5
- pre_lock_path = "#{lock_path}_pre_#{Process.pid}"
3
+ class Spitball::FileLock
4
+
5
+ attr_reader :path
6
+
7
+ def initialize(path)
8
+ @path = path
9
+ end
10
+
11
+ def acquire_lock
6
12
 
7
13
  File.open(pre_lock_path, 'w') {|f| f.write Process.pid }
8
14
 
9
- system "ln #{pre_lock_path} #{lock_path} > /dev/null 2>&1"
10
- File.read(lock_path).to_i == Process.pid
15
+ system "ln #{pre_lock_path} #{path} > /dev/null 2>&1"
16
+ File.read(path).to_i == Process.pid
11
17
  ensure
12
18
  FileUtils.rm_f pre_lock_path
13
19
  end
14
20
 
15
21
  # seems silly to lock to release lock
16
- def release_lock(lock_path)
17
- FileUtils.rm_f lock_path if acquire_lock lock_path
22
+ def release_lock
23
+ FileUtils.rm_f path if acquire_lock
24
+ end
25
+
26
+ private
27
+
28
+ def pre_lock_path
29
+ "#{path}_pre_#{Process.pid}"
18
30
  end
19
31
  end
@@ -3,6 +3,8 @@ require 'uri'
3
3
 
4
4
  class Spitball::Remote
5
5
 
6
+ WAIT_SECONDS = 30
7
+
6
8
  def initialize(gemfile, host, port)
7
9
  @gemfile = gemfile
8
10
  @host = host
@@ -25,12 +27,14 @@ class Spitball::Remote
25
27
  when '201' # Created
26
28
  Net::HTTP.get(URI.parse(res['Location']))
27
29
  when '202' # Accepted
28
- loop do
30
+ (WAIT_SECONDS / 2).times do
29
31
  sleep 2
30
32
  try = Net::HTTP.get_response(URI.parse(res['Location']))
31
33
  next if try.code != '200'
32
34
  return try.body
33
35
  end
36
+
37
+ raise SpitballServerFailure, "Spitball build timed out. The build failed or it's just taking a while..."
34
38
  else
35
39
  raise SpitballServerFailure, "Expected 2xx response code. Got #{res.code}."
36
40
  end
data/lib/spitball.rb CHANGED
@@ -13,7 +13,6 @@ class Spitball
13
13
  VERSION = '1.0'
14
14
 
15
15
  include Spitball::Digest
16
- include Spitball::FileLock
17
16
 
18
17
  attr_reader :gemfile, :options
19
18
 
@@ -35,11 +34,13 @@ class Spitball
35
34
  Spitball::Repo.make_cache_dir
36
35
 
37
36
  unless cached?
38
- if acquire_lock bundle_path('lock')
37
+ lock = Spitball::FileLock.new(bundle_path('lock'))
38
+
39
+ if lock.acquire_lock
39
40
  begin
40
41
  create_bundle
41
42
  ensure
42
- release_lock bundle_path('lock')
43
+ lock.release_lock
43
44
  end
44
45
  elsif sync
45
46
  sleep 0.1 until cached?
@@ -52,14 +53,10 @@ class Spitball
52
53
 
53
54
  File.open(gemfile_path, 'w') {|f| f.write gemfile }
54
55
 
55
- if system "cd #{bundle_path} && bundle install #{bundle_path} --disable-shared-gems #{without_clause} > /dev/null"
56
- FileUtils.rm_rf File.join(bundle_path, "cache")
57
-
56
+ if system "cd #{bundle_path} && bundle install #{bundle_path} --disable-shared-gems #{without_clause}"
58
57
  system "tar czf #{tarball_path}.#{Process.pid} -C #{bundle_path} ."
59
58
  system "mv #{tarball_path}.#{Process.pid} #{tarball_path}"
60
-
61
59
  else
62
- #FileUtils.rm_rf gemfile_path
63
60
  raise BundleCreationFailure, "Bundle build failure."
64
61
  end
65
62
 
@@ -67,7 +64,7 @@ class Spitball
67
64
  end
68
65
 
69
66
  def without_clause
70
- without = options[:without] || []
67
+ without = Array(options[:without] || [])
71
68
  return '' if without.empty?
72
69
 
73
70
  "--without=#{without.join(',')}"
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  SPEC_DIR = File.dirname(__FILE__)
2
2
 
3
+ SPEC_BIN_PATH = File.expand_path('bin', SPEC_DIR)
4
+
3
5
  SPITBALL_CACHE = ENV['SPITBALL_CACHE'] = File.expand_path('cache', SPEC_DIR)
4
6
 
7
+ ENV['PATH'] = [SPEC_BIN_PATH, ENV['PATH']].join(':') unless ENV['PATH'].include? SPEC_BIN_PATH
8
+
5
9
  $: << File.expand_path("../lib", SPEC_DIR)
6
10
 
7
11
  require 'rubygems'
@@ -14,10 +18,12 @@ Spec::Runner.configure do |config|
14
18
  config.mock_with :rr
15
19
  config.before do
16
20
  purge_test_cache
21
+ purge_bin
17
22
  end
18
23
 
19
24
  config.after :all do
20
25
  purge_test_cache
26
+ purge_bin
21
27
  end
22
28
  end
23
29
 
@@ -27,3 +33,28 @@ end
27
33
  def purge_test_cache
28
34
  FileUtils.rm_rf SPITBALL_CACHE
29
35
  end
36
+
37
+ def make_bundler
38
+ bundle_path = File.expand_path('bundle', SPEC_BIN_PATH)
39
+ FileUtils.mkdir_p SPEC_BIN_PATH
40
+ File.open(bundle_path, 'w') { |f| yield f }
41
+ FileUtils.chmod(0755, bundle_path)
42
+ end
43
+
44
+ def use_success_bundler
45
+ make_bundler do |f|
46
+ f.puts "#!/bin/sh"
47
+ f.puts "mkdir -p $2/gems"
48
+ f.puts "echo WIN > $2/gems/gem"
49
+ end
50
+ end
51
+
52
+ def use_fail_bundler
53
+ make_bundler do |f|
54
+ f.puts "exit 1"
55
+ end
56
+ end
57
+
58
+ def purge_bin
59
+ FileUtils.rm_rf File.expand_path('bin', SPEC_DIR)
60
+ end
@@ -1,35 +1,132 @@
1
1
  require 'spec/spec_helper'
2
2
 
3
3
  describe Spitball do
4
- it "works" do
4
+ before do
5
+ use_success_bundler
6
+
7
+ @gemfile = <<-end_gemfile
8
+ source :rubygems
9
+ gem "activerecord"
10
+ end_gemfile
11
+
12
+ @spitball = Spitball.new(@gemfile)
13
+ end
14
+
15
+ describe "cached?" do
16
+ it "returns true if the tarball has already been cached" do
17
+ @spitball.should_not be_cached
18
+ @spitball.cache!
19
+ @spitball.should be_cached
20
+ end
21
+ end
22
+
23
+ describe "cache!" do
24
+ it "returns if the spitball is cached" do
25
+ mock(@spitball).cached? { true }
26
+ mock.instance_of(Spitball::FileLock).acquire_lock.never
27
+ mock.instance_of(Spitball::FileLock).release_lock.never
28
+ mock(@spitball).create_bundle(anything).never
29
+
30
+ @spitball.cache!
31
+ end
32
+
33
+ it "creates the bundle if it acquires the lock" do
34
+ mock.instance_of(Spitball::FileLock).acquire_lock { true }
35
+ mock(@spitball).create_bundle
36
+ mock.instance_of(Spitball::FileLock).release_lock
37
+
38
+ @spitball.cache!
39
+ end
40
+
41
+ it "does not create the bundle if it does not acquire the lock" do
42
+ mock.instance_of(Spitball::FileLock).release_lock.never
43
+ mock.instance_of(Spitball::FileLock).acquire_lock { false }
44
+ mock(@spitball).create_bundle.never
45
+
46
+ @spitball.cache!(false)
47
+ end
48
+
49
+ it "blocks if it does not acquire the lock and sync is true (default)" do
50
+ cached = false
51
+ done_caching = false
52
+
53
+ mock.instance_of(Spitball::FileLock).acquire_lock { false }
54
+ stub(@spitball).cached? { cached }
55
+
56
+ t = Thread.new do
57
+ @spitball.cache!
58
+ done_caching = true
59
+ end
60
+
61
+ sleep 0.5
62
+ done_caching.should_not == true
63
+
64
+ cached = true
65
+ t.join
66
+ done_caching.should == true
67
+ end
68
+ end
69
+
70
+ describe "create_bundle" do
71
+ it "generates a bundle at the bundle_path" do
72
+ @spitball.create_bundle
73
+
74
+ File.exist?(@spitball.tarball_path).should == true
75
+ end
76
+
77
+ it "raises an exception if bundle creation fails" do
78
+ use_fail_bundler
79
+
80
+ lambda { @spitball.create_bundle }.should raise_error(Spitball::BundleCreationFailure)
81
+ File.exist?(@spitball.tarball_path).should_not == true
82
+ end
83
+ end
84
+
85
+ describe "without_clause" do
86
+ it "returns a --without bundler option if :without is set" do
87
+ Spitball.new('gemfile', :without => "system").without_clause.should == '--without=system'
88
+ end
89
+
90
+ it "returns an empty string if without is not set" do
91
+ Spitball.new('gemfile').without_clause.should == ''
92
+ end
93
+
94
+ it "allows multiple groups" do
95
+ Spitball.new('gemfile', :without => ["system", "test"]).without_clause.should == '--without=system,test'
96
+ end
5
97
  end
6
98
  end
7
99
 
8
100
  describe Spitball::FileLock do
9
- include Spitball::FileLock
10
-
11
101
  describe "acquire_lock" do
12
102
  before do
13
103
  Spitball::Repo.make_cache_dir
14
104
  @lock_path = File.expand_path('test.lock', SPITBALL_CACHE)
105
+ @lock = Spitball::FileLock.new(@lock_path)
15
106
  end
16
107
 
17
108
  it "returns true if the lock is acquired" do
18
- acquire_lock(@lock_path).should == true
109
+ @lock.acquire_lock.should == true
19
110
  end
20
111
 
21
112
  it "returns false if the lock is not acquired" do
22
- fork { acquire_lock(@lock_path); exit! }
113
+ fork { @lock.acquire_lock; exit! }
23
114
  Process.wait
24
115
 
25
- acquire_lock(@lock_path).should == false
116
+ @lock.acquire_lock.should == false
26
117
  end
27
118
  end
28
119
  end
29
120
 
30
121
  describe Spitball::Repo do
122
+ before do
123
+ Spitball::Repo.make_cache_dir
124
+ end
125
+
31
126
  describe "make_cache_dir" do
32
127
  it "creates the correct cache dir" do
128
+ FileUtils.rm_rf(SPITBALL_CACHE)
129
+
33
130
  File.exist?(SPITBALL_CACHE).should_not == true
34
131
  Spitball::Repo.make_cache_dir
35
132
  File.exist?(SPITBALL_CACHE).should == true
@@ -49,4 +146,44 @@ describe Spitball::Repo do
49
146
  Spitball::Repo.path('digest', 'tgz').should =~ %r[bundle_digest\.tgz$]
50
147
  end
51
148
  end
149
+
150
+ describe "exist?" do
151
+ it "returns true if tarball for a digest has been exists" do
152
+ Spitball::Repo.exist?('digest').should_not == true
153
+ File.open(Spitball::Repo.path('digest', 'tgz'), 'w') {|f| f.write 'tarball!' }
154
+ Spitball::Repo.exist?('digest').should == true
155
+ end
156
+ end
157
+
158
+ describe "gemfile" do
159
+ it "returns the contents of the cached gemfile for a digest" do
160
+ gemfile = 'gem :memcached'
161
+ File.open(Spitball::Repo.path('digest', 'gemfile'), 'w') {|f| f.write gemfile }
162
+ Spitball::Repo.gemfile('digest').should == gemfile
163
+ end
164
+ end
165
+
166
+ describe "list_cached" do
167
+ it "returns a list of cached bundles" do
168
+ File.open(Spitball::Repo.path('digest', 'tgz'), 'w') {|f| f.write 'tarball!' }
169
+ File.open(Spitball::Repo.path('digest2', 'tgz'), 'w') {|f| f.write 'tarball2!' }
170
+
171
+ Spitball::Repo.list_cached.should == ['digest', 'digest2']
172
+ end
173
+ end
174
+ end
175
+
176
+ describe Spitball::Digest do
177
+ it "generates a digest based on the spitball's options and gemfile" do
178
+ [Spitball.new('gemfile contents', :without => "system").digest,
179
+ Spitball.new('gemfile contents 2', :without => "system").digest,
180
+ Spitball.new('gemfile', :without => "other_group").digest,
181
+ Spitball.new('gemfile').digest
182
+ ].uniq.length.should == 4
183
+ end
184
+
185
+ it "provides a hash equal to the digest's hash"do
186
+ spitball = Spitball.new('gemfile contents')
187
+ spitball.hash.should == spitball.digest.hash
188
+ end
52
189
  end
data/spitball.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{spitball}
8
- s.version = "0.1.5"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Matt Freels", "Brandon Mitchell"]
12
- s.date = %q{2010-07-21}
12
+ s.date = %q{2010-07-29}
13
13
  s.description = %q{Use bundler to generate gem tarball packages.}
14
14
  s.email = %q{freels@twitter.com}
15
15
  s.executables = ["spitball", "spitball-server"]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spitball
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 5
10
- version: 0.1.5
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Freels
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-07-21 00:00:00 -07:00
19
+ date: 2010-07-29 00:00:00 -07:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency