spitball 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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