stickler 2.3.0 → 2.4.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.
Files changed (80) hide show
  1. checksums.yaml +15 -0
  2. data/CONTRIBUTING.md +5 -4
  3. data/HISTORY.md +16 -9
  4. data/LICENSE +1 -1
  5. data/Manifest.txt +34 -20
  6. data/README.md +128 -0
  7. data/Rakefile +10 -9
  8. data/bin/stickler +9 -6
  9. data/bin/stickler-passenger-config +8 -8
  10. data/bin/stickler-server +12 -12
  11. data/examples/as_middleware.ru +14 -0
  12. data/examples/auth_repo.ru +1 -1
  13. data/examples/gemcutter_repo.ru +1 -1
  14. data/examples/local_repo.ru +1 -1
  15. data/lib/stickler.rb +3 -1
  16. data/lib/stickler/client.rb +2 -1
  17. data/lib/stickler/client/delete.rb +1 -1
  18. data/lib/stickler/client/latest-version.rb +40 -0
  19. data/lib/stickler/client/mirror.rb +47 -15
  20. data/lib/stickler/client/push.rb +1 -1
  21. data/lib/stickler/client/unyank.rb +1 -1
  22. data/lib/stickler/client/yank.rb +1 -1
  23. data/lib/stickler/gem_container.rb +40 -0
  24. data/lib/stickler/gemfile_lock_parser.rb +47 -0
  25. data/lib/stickler/middleware.rb +1 -0
  26. data/lib/stickler/middleware/server.rb +37 -0
  27. data/lib/stickler/repository/api.rb +16 -0
  28. data/lib/stickler/repository/index.rb +0 -3
  29. data/lib/stickler/repository/local.rb +6 -8
  30. data/lib/stickler/repository/remote.rb +29 -7
  31. data/lib/stickler/server.rb +2 -6
  32. data/man/stickler-passenger-config.1 +2 -22
  33. data/man/stickler-server.1 +9 -99
  34. data/man/stickler.1 +15 -173
  35. data/man/stickler.1.ronn +6 -0
  36. data/tasks/default.rake +16 -18
  37. data/tasks/man.rake +7 -0
  38. data/tasks/this.rb +5 -5
  39. data/test/data/Gemfile.lock.example +56 -0
  40. data/{spec → test}/data/gemcutter/gems/foo-1.0.0.gem +0 -0
  41. data/{spec → test}/data/gems/bar-1.0.0.gem +0 -0
  42. data/{spec → test}/data/gems/baz-3.1.4-java.gem +0 -0
  43. data/{spec → test}/data/gems/baz-3.1.4.gem +0 -0
  44. data/{spec → test}/data/gems/foo-1.0.0.gem +0 -0
  45. data/{spec → test}/data/gems/foo-2.0.0a.gem +0 -0
  46. data/test/data/specifications/bar-1.0.0.gemspec +31 -0
  47. data/test/data/specifications/baz-3.1.4-java.gemspec +32 -0
  48. data/test/data/specifications/baz-3.1.4.gemspec +31 -0
  49. data/test/data/specifications/foo-1.0.0.gemspec +31 -0
  50. data/test/data/specifications/foo-2.0.0a.gemspec +32 -0
  51. data/test/index_test_helpers.rb +75 -0
  52. data/test/middleware/test_local.rb +75 -0
  53. data/test/middleware/test_not_found.rb +26 -0
  54. data/test/repository/test_api.rb +49 -0
  55. data/test/repository/test_api_behavior.rb +208 -0
  56. data/test/repository/test_index.rb +48 -0
  57. data/test/repository/test_local.rb +59 -0
  58. data/test/repository/test_null.rb +15 -0
  59. data/test/repository/test_remote.rb +26 -0
  60. data/test/repository/test_remote_authenticated.rb +39 -0
  61. data/test/stickler_test_server.rb +35 -0
  62. data/test/test_gemfile_lock_parser.rb +28 -0
  63. data/test/test_paths.rb +22 -0
  64. data/test/test_spec_lite.rb +90 -0
  65. data/test/test_stickler.rb +49 -0
  66. metadata +58 -85
  67. data/README.rdoc +0 -156
  68. data/spec/index_spec_helpers.rb +0 -73
  69. data/spec/middleware/local_spec.rb +0 -72
  70. data/spec/middleware/not_found_spec.rb +0 -25
  71. data/spec/paths_spec.rb +0 -11
  72. data/spec/repository/api_behavior.rb +0 -192
  73. data/spec/repository/api_spec.rb +0 -37
  74. data/spec/repository/index_spec.rb +0 -46
  75. data/spec/repository/local_spec.rb +0 -49
  76. data/spec/repository/null_spec.rb +0 -14
  77. data/spec/repository/remote_spec.rb +0 -86
  78. data/spec/spec.opts +0 -2
  79. data/spec/spec_helper.rb +0 -24
  80. data/spec/spec_lite_spec.rb +0 -96
@@ -0,0 +1,75 @@
1
+ require 'test_stickler'
2
+
3
+ module Stickler
4
+ class MiddlewareLocalTest < Test
5
+ include IndexTestHelpers
6
+ include Rack::Test::Methods
7
+
8
+ def app
9
+ repo_root = @idx_test_datadir
10
+ ::Rack::Builder.new do
11
+ use ::Stickler::Middleware::Compression
12
+ use ::Stickler::Middleware::Local, :repo_root => repo_root
13
+ run ::Sinatra::Base
14
+ end
15
+ end
16
+ end
17
+
18
+ class MiddlewareLocalModernTest < MiddlewareLocalTest
19
+ def setup
20
+ mirror_spec_gemdir
21
+ make_modern_index
22
+ end
23
+
24
+ def teardown
25
+ destroy_scratch_dir
26
+ end
27
+
28
+ def test_returns_the_same_bytes_as_gem_indexer
29
+ [
30
+ "/specs.#{Gem.marshal_version}",
31
+ "/specs.#{Gem.marshal_version}.gz",
32
+ "/latest_specs.#{Gem.marshal_version}",
33
+ "/latest_specs.#{Gem.marshal_version}.gz",
34
+ "/prerelease_specs.#{Gem.marshal_version}",
35
+ "/prerelease_specs.#{Gem.marshal_version}.gz",
36
+ "/quick/Marshal.#{Gem.marshal_version}/foo-1.0.0.gemspec.rz",
37
+ "/quick/Marshal.#{Gem.marshal_version}/bar-1.0.0.gemspec.rz",
38
+ "/quick/Marshal.#{Gem.marshal_version}/foo-2.0.0a.gemspec.rz",
39
+ "/quick/Marshal.#{Gem.marshal_version}/baz-3.1.4-java.gemspec.rz",
40
+ "/quick/Marshal.#{Gem.marshal_version}/baz-3.1.4.gemspec.rz",
41
+ ].each do |path|
42
+ response = get( path )
43
+ need, got = validate_contents( response.body,
44
+ IO.read( File.join(@scratch_datadir, path) ),
45
+ response.content_type )
46
+ assert_equal need, got, "Match failure for #{path}"
47
+ end
48
+ end
49
+ end
50
+
51
+ class MiddlewareLocalLegacyTest < MiddlewareLocalTest
52
+ def setup
53
+ mirror_spec_gemdir
54
+ make_legacy_index
55
+ end
56
+
57
+ def teardown
58
+ destroy_scratch_dir
59
+ end
60
+
61
+ def test_returns_the_same_bytes_as_gem_indexer
62
+ [
63
+ "/Marshal.#{Gem.marshal_version}",
64
+ "/Marshal.#{Gem.marshal_version}.Z",
65
+ ].each do |path|
66
+ skip "Not testing legacy indexes at the moment"
67
+ response = get( path )
68
+ need, got = validate_contents( response.body,
69
+ IO.read( File.join(@scratch_datadir, path) ),
70
+ response.content_type )
71
+ assert_equal need, got, "Match failure for #{path}"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_stickler'
2
+
3
+ module Stickler
4
+ class MiddlewareNotFound < Test
5
+ include Rack::Test::Methods
6
+ def app
7
+ ::Stickler::Middleware::NotFound.new
8
+ end
9
+
10
+ def setup
11
+ get "/"
12
+ end
13
+
14
+ def test_responds_with_404_to_everything
15
+ assert_equal 404, last_response.status
16
+ end
17
+
18
+ def test_resturns_html_page
19
+ assert_equal 'text/html', last_response.content_type
20
+ end
21
+
22
+ def test_body_says_look_elsewhere
23
+ assert_match( /Not Found/, last_response.body)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,49 @@
1
+ require 'test_stickler'
2
+ require 'digest/sha1'
3
+
4
+ module Stickler
5
+ module RepositoryApiTests
6
+ def test_implements_all_the_api_methods
7
+ Stickler::Repository::Api.api_methods.each do |method|
8
+ assert_respond_to repo, method
9
+ end
10
+ end
11
+ end
12
+
13
+ # Stub class for testing
14
+ module Repository
15
+ class Stub
16
+ include Stickler::Repository::Api
17
+ end
18
+ end
19
+
20
+ class RepositoryApiModuleTest < Test
21
+ include RepositoryApiTests
22
+
23
+ def repo
24
+ @repo ||= Stickler::Repository::Stub.new
25
+ end
26
+
27
+ def setup
28
+ @spec = Stickler::SpecLite.new( "foo", "1.0.0" )
29
+ end
30
+
31
+ def test_raising_error_on_unimplemented_methods
32
+ %w[ uri gems_uri ].each do |method|
33
+ assert_raises( NotImplementedError ) { repo.send( method ) }
34
+ end
35
+ end
36
+
37
+ def test_raising_error_on_unimplemented_methods_taking_a_spec
38
+ %w[ uri_for_gem search_for delete yank get open ].each do |method|
39
+ assert_raises( NotImplementedError ) { repo.send( method, @spec ) }
40
+ end
41
+ end
42
+
43
+ def test_raising_error_on_implemented_methods_taking_an_object
44
+ %w[ push ].each do |method|
45
+ assert_raises( NotImplementedError ) { repo.send( method, Object.new ) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,208 @@
1
+ require 'test_stickler'
2
+ require 'digest/sha1'
3
+ require 'repository/test_api'
4
+
5
+ module Stickler
6
+ module RepositoryApiBehaviorTests
7
+ include RepositoryApiTests
8
+ def setup
9
+ super
10
+ @foo_gem_local_path = File.join( gems_dir, "foo-1.0.0.gem" )
11
+ @foo_spec = Stickler::SpecLite.new( 'foo', '1.0.0' )
12
+ assert File.readable?( @foo_gem_local_path ), "#{@foo_gem_local_path} is missing"
13
+ @foo_digest = Digest::SHA1.hexdigest( IO.read( @foo_gem_local_path ) )
14
+ @missing_spec = Stickler::SpecLite.new( "does_not_exist", "0.1.0" )
15
+ end
16
+
17
+ #####
18
+ # URI
19
+ #####
20
+
21
+ def test_uri
22
+ result = repo.uri
23
+ assert_includes [ ::URI, ::Addressable::URI ], result.class
24
+ end
25
+
26
+ def test_gems_uri
27
+ result = repo.gems_uri
28
+ assert_includes [ ::URI, ::Addressable::URI ], result.class
29
+ end
30
+
31
+ def test_uri_for_gem
32
+ repo.push( @foo_gem_local_path )
33
+ result = repo.uri_for_gem( @foo_spec )
34
+ assert_includes [ ::URI, ::Addressable::URI ], result.class
35
+ assert_nil repo.uri_for_gem( @missing_spec )
36
+ end
37
+
38
+ #######
39
+ # Push
40
+ #######
41
+
42
+ def test_pushes_a_gem_file
43
+ repo.push( @foo_gem_local_path )
44
+ result = repo.search_for( Stickler::SpecLite.new( "foo", "1.0.0" ) )
45
+ assert_instance_of Array, result
46
+ assert_equal 1, result.size
47
+ end
48
+
49
+ def test_raising_error_when_pushing_already_existing_gem
50
+ repo.push( @foo_gem_local_path )
51
+ assert_raises_kind_of( Stickler::Repository::Error ) do
52
+ repo.push( @foo_gem_local_path )
53
+ end
54
+ end
55
+
56
+ ########
57
+ # Delete
58
+ ########
59
+
60
+ def test_delete_existing_gem
61
+ assert_empty repo.search_for( @foo_spec )
62
+ repo.push( @foo_gem_local_path )
63
+
64
+ assert_equal 1, repo.search_for( @foo_spec ).size
65
+
66
+ assert repo.delete( @foo_spec )
67
+ assert_empty repo.search_for( @foo_spec )
68
+ end
69
+
70
+ def test_delete_non_existing_gem
71
+ assert_empty repo.search_for( @foo_spec )
72
+ refute repo.delete( @foo_spec )
73
+ end
74
+
75
+ ######
76
+ # yank
77
+ ######
78
+
79
+ def test_yank_returns_the_uri_of_the_gem
80
+ assert_empty repo.search_for( @foo_spec )
81
+ repo.push( @foo_gem_local_path )
82
+ @response_uri = repo.yank( @foo_spec )
83
+ assert_includes [ ::URI, ::Addressable::URI ], @response_uri.class
84
+ end
85
+
86
+ def test_yank_returns_nil_for_non_existent_gem
87
+ assert_nil repo.yank( @missing_spec )
88
+ end
89
+
90
+ def test_yanked_gem_does_not_appear_in_search
91
+ assert_empty repo.search_for( @foo_spec )
92
+ repo.push( @foo_gem_local_path )
93
+ @response_uri = repo.yank( @foo_spec )
94
+ assert_empty repo.search_for( @foo_spec )
95
+ end
96
+
97
+ def test_yanked_gem_does_still_have_a_uri
98
+ assert_empty repo.search_for( @foo_spec )
99
+ repo.push( @foo_gem_local_path )
100
+ @response_uri = repo.yank( @foo_spec )
101
+ assert_equal @response_uri, repo.uri_for_gem( @foo_spec )
102
+ end
103
+
104
+ def test_yanked_gem_may_still_be_retrieved
105
+ assert_empty repo.search_for( @foo_spec )
106
+ repo.push( @foo_gem_local_path )
107
+ data = repo.get( @foo_spec )
108
+ sha1 = Digest::SHA1.hexdigest( data )
109
+ assert_equal @foo_digest, sha1
110
+ end
111
+
112
+ ########
113
+ # unyank
114
+ ########
115
+
116
+ def test_unyank_returns_nil_for_non_existent_gem
117
+ assert_empty repo.search_for( @foo_spec )
118
+ repo.push( @foo_gem_local_path )
119
+ non_existing_gem = @missing_spec
120
+ assert_nil repo.unyank( non_existing_gem )
121
+ end
122
+
123
+
124
+ # Do we even care about this?
125
+ def test_unyank_returns_nil_for_not_yet_yanked_gem
126
+ assert_empty repo.search_for( @foo_spec )
127
+ repo.push( @foo_gem_local_path )
128
+ assert_equal 1, repo.search_for( @foo_spec ).size
129
+ assert_nil repo.unyank( @foo_spec )
130
+ end
131
+
132
+ def test_unyank_returns_true_on_success
133
+ assert_empty repo.search_for( @foo_spec )
134
+ repo.push( @foo_gem_local_path )
135
+ repo.yank( @foo_spec )
136
+ assert repo.unyank( @foo_spec ), "Expected true"
137
+ end
138
+
139
+ def test_unyank_returns_gem_to_searchability
140
+ assert_empty repo.search_for( @foo_spec )
141
+ repo.push( @foo_gem_local_path )
142
+ repo.yank( @foo_spec )
143
+ repo.unyank( @foo_spec )
144
+ assert_equal 1, repo.search_for( @foo_spec ).size
145
+ end
146
+
147
+ ############
148
+ # Search For
149
+ ############
150
+
151
+ def test_search_for_returns_found_items
152
+ repo.push( @foo_gem_local_path )
153
+ refute_empty repo.search_for( @foo_spec )
154
+ end
155
+
156
+ def test_search_for_returns_empty_array_when_nothing_found
157
+ assert_empty repo.search_for( @missing_spec )
158
+ end
159
+
160
+
161
+ #####
162
+ # GET
163
+ #####
164
+
165
+ def test_get_returns_the_bytes_of_a_gem_that_exists
166
+ repo.push( @foo_gem_local_path )
167
+ data = repo.get( @foo_spec )
168
+ sha1 = Digest::SHA1.hexdigest( data )
169
+ assert_equal @foo_digest, sha1
170
+ end
171
+
172
+ def test_get_returns_nil_for_non_existent_gem
173
+ assert_nil repo.get( @missing_spec )
174
+ end
175
+
176
+ ######
177
+ # open
178
+ ######
179
+
180
+ def test_open_reads_a_gem_via_the_returned_output_stream
181
+ repo.push( @foo_gem_local_path )
182
+ io = repo.open( @foo_spec )
183
+ sha1 = Digest::SHA1.hexdigest( io.read )
184
+ assert_equal @foo_digest, sha1
185
+ end
186
+
187
+ def test_open_may_be_called_with_a_block
188
+ repo.push( @foo_gem_local_path )
189
+ sha1 = Digest::SHA1.new
190
+ repo.open( @foo_spec ) do |io|
191
+ sha1 << io.read
192
+ end
193
+ assert_equal @foo_digest, sha1.hexdigest
194
+ end
195
+
196
+ def test_open_returns_nil_for_non_existent_gem
197
+ assert_nil repo.open( @missing_spec )
198
+ end
199
+
200
+ def test_open_does_not_call_block_for_non_existent_gem
201
+ called = false
202
+ repo.open( @missing_spec ) do |io|
203
+ called = true
204
+ end
205
+ refute called
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,48 @@
1
+ require 'test_stickler'
2
+
3
+ module Stickler
4
+ class TestIndexRepository < Test
5
+ def setup
6
+ @index_me = File.join( test_dir, "tmp" )
7
+ FileUtils.mkdir_p( @index_me )
8
+
9
+ @specifications = Dir.glob( File.join( specifications_dir, "*.gemspec" ) )
10
+ @specifications.each do |s|
11
+ FileUtils.cp( s, @index_me )
12
+ end
13
+ @index = ::Stickler::Repository::Index.new( @index_me )
14
+ end
15
+
16
+ def teardown
17
+ FileUtils.rm_rf( @index_me )
18
+ end
19
+
20
+ def test_all_gemspec_files_in_a_directory_are_indexed
21
+ assert_equal @specifications.size, @index.specs.size
22
+ end
23
+
24
+ def test_notice_changes_in_the_index
25
+ assert_equal @specifications.size, @index.specs.size
26
+ FileUtils.rm( File.join( @index_me, "foo-1.0.0.gemspec" ) )
27
+ assert_equal @specifications.size-1, @index.specs.size
28
+ end
29
+
30
+ def test_latest_specs
31
+ assert_equal( @specifications.size - 1, @index.latest_specs.size )
32
+ expected = %w[ bar-1.0.0 baz-3.1.4 baz-3.1.4-java foo-1.0.0 ]
33
+ assert_equal expected, @index.latest_specs.collect { |s| s.full_name }.sort
34
+ end
35
+
36
+ def test_pre_release_specs
37
+ assert_equal 1, @index.prerelease_specs.size
38
+ assert_equal "foo-2.0.0a",@index.prerelease_specs.first.full_name
39
+ end
40
+
41
+ def test_released_specs
42
+ assert_equal 4, @index.released_specs.size
43
+ expected = %w[ bar-1.0.0 baz-3.1.4 baz-3.1.4-java foo-1.0.0 ]
44
+ assert_equal expected, @index.released_specs.collect { |s| s.full_name }.sort
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,59 @@
1
+ require 'test_stickler'
2
+ require 'repository/test_api_behavior'
3
+
4
+ module Stickler
5
+ class LocalRepositoryTest < Test
6
+ include RepositoryApiBehaviorTests
7
+
8
+ def repos_dir
9
+ File.join( test_dir, "repos" )
10
+ end
11
+
12
+ def repo_dir
13
+ File.join( repos_dir, "1" )
14
+ end
15
+
16
+ def repo
17
+ @repo ||= ::Stickler::Repository::Local.new( repo_dir )
18
+ end
19
+
20
+ def teardown
21
+ super
22
+ FileUtils.rm_rf( repos_dir )
23
+ end
24
+
25
+ def test_creates_gems_directory
26
+ repo.gems_dir
27
+ new_dir = File.join( repo_dir , 'gems') + File::SEPARATOR
28
+ assert File.directory?( new_dir ), "#{new_dir} must be a directory"
29
+ assert_equal new_dir, repo.gems_dir
30
+ end
31
+
32
+ def test_creates_specifications_directory
33
+ repo.specifications_dir
34
+ new_dir = File.join( repo_dir , 'specifications' ) + File::SEPARATOR
35
+ assert File.directory?( new_dir ), "#{new_dir} must be a directory"
36
+ assert_equal new_dir, repo.specifications_dir
37
+ end
38
+
39
+ def test_lists_all_specs_in_the_repo
40
+ Dir.glob( File.join( gems_dir, "*.gem" ) ).each do |gem|
41
+ repo.push( gem )
42
+ end
43
+ assert_equal 5, repo.specs.size
44
+ end
45
+
46
+ def test_two_instances_with_the_same_repo_dir_are_the_same_object
47
+ repo2 = ::Stickler::Repository::Local.new( File.join( repos_dir, '1') )
48
+ assert_equal repo.object_id, repo2.object_id
49
+ assert_equal 1, ::Stickler::Repository::Local.repos.size
50
+ end
51
+
52
+ def test_tracks_all_repository_instances
53
+ ::Stickler::Repository::Local.new( File.join( repos_dir, "2" ) )
54
+ ::Stickler::Repository::Local.new( File.join( repos_dir, "3" ) )
55
+ assert_equal 2, ::Stickler::Repository::Local.repos.size
56
+ end
57
+ end
58
+ end
59
+