stickler 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/CONTRIBUTING.md +5 -4
- data/HISTORY.md +16 -9
- data/LICENSE +1 -1
- data/Manifest.txt +34 -20
- data/README.md +128 -0
- data/Rakefile +10 -9
- data/bin/stickler +9 -6
- data/bin/stickler-passenger-config +8 -8
- data/bin/stickler-server +12 -12
- data/examples/as_middleware.ru +14 -0
- data/examples/auth_repo.ru +1 -1
- data/examples/gemcutter_repo.ru +1 -1
- data/examples/local_repo.ru +1 -1
- data/lib/stickler.rb +3 -1
- data/lib/stickler/client.rb +2 -1
- data/lib/stickler/client/delete.rb +1 -1
- data/lib/stickler/client/latest-version.rb +40 -0
- data/lib/stickler/client/mirror.rb +47 -15
- data/lib/stickler/client/push.rb +1 -1
- data/lib/stickler/client/unyank.rb +1 -1
- data/lib/stickler/client/yank.rb +1 -1
- data/lib/stickler/gem_container.rb +40 -0
- data/lib/stickler/gemfile_lock_parser.rb +47 -0
- data/lib/stickler/middleware.rb +1 -0
- data/lib/stickler/middleware/server.rb +37 -0
- data/lib/stickler/repository/api.rb +16 -0
- data/lib/stickler/repository/index.rb +0 -3
- data/lib/stickler/repository/local.rb +6 -8
- data/lib/stickler/repository/remote.rb +29 -7
- data/lib/stickler/server.rb +2 -6
- data/man/stickler-passenger-config.1 +2 -22
- data/man/stickler-server.1 +9 -99
- data/man/stickler.1 +15 -173
- data/man/stickler.1.ronn +6 -0
- data/tasks/default.rake +16 -18
- data/tasks/man.rake +7 -0
- data/tasks/this.rb +5 -5
- data/test/data/Gemfile.lock.example +56 -0
- data/{spec → test}/data/gemcutter/gems/foo-1.0.0.gem +0 -0
- data/{spec → test}/data/gems/bar-1.0.0.gem +0 -0
- data/{spec → test}/data/gems/baz-3.1.4-java.gem +0 -0
- data/{spec → test}/data/gems/baz-3.1.4.gem +0 -0
- data/{spec → test}/data/gems/foo-1.0.0.gem +0 -0
- data/{spec → test}/data/gems/foo-2.0.0a.gem +0 -0
- data/test/data/specifications/bar-1.0.0.gemspec +31 -0
- data/test/data/specifications/baz-3.1.4-java.gemspec +32 -0
- data/test/data/specifications/baz-3.1.4.gemspec +31 -0
- data/test/data/specifications/foo-1.0.0.gemspec +31 -0
- data/test/data/specifications/foo-2.0.0a.gemspec +32 -0
- data/test/index_test_helpers.rb +75 -0
- data/test/middleware/test_local.rb +75 -0
- data/test/middleware/test_not_found.rb +26 -0
- data/test/repository/test_api.rb +49 -0
- data/test/repository/test_api_behavior.rb +208 -0
- data/test/repository/test_index.rb +48 -0
- data/test/repository/test_local.rb +59 -0
- data/test/repository/test_null.rb +15 -0
- data/test/repository/test_remote.rb +26 -0
- data/test/repository/test_remote_authenticated.rb +39 -0
- data/test/stickler_test_server.rb +35 -0
- data/test/test_gemfile_lock_parser.rb +28 -0
- data/test/test_paths.rb +22 -0
- data/test/test_spec_lite.rb +90 -0
- data/test/test_stickler.rb +49 -0
- metadata +58 -85
- data/README.rdoc +0 -156
- data/spec/index_spec_helpers.rb +0 -73
- data/spec/middleware/local_spec.rb +0 -72
- data/spec/middleware/not_found_spec.rb +0 -25
- data/spec/paths_spec.rb +0 -11
- data/spec/repository/api_behavior.rb +0 -192
- data/spec/repository/api_spec.rb +0 -37
- data/spec/repository/index_spec.rb +0 -46
- data/spec/repository/local_spec.rb +0 -49
- data/spec/repository/null_spec.rb +0 -14
- data/spec/repository/remote_spec.rb +0 -86
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -24
- data/spec/spec_lite_spec.rb +0 -96
@@ -0,0 +1,14 @@
|
|
1
|
+
#-----------------------------------------------------------------------
|
2
|
+
#-*- vim: set ft=ruby: -*-
|
3
|
+
#
|
4
|
+
# Example rackup file for using stickler as middleware in a larger application
|
5
|
+
#-----------------------------------------------------------------------
|
6
|
+
$:.unshift File.expand_path( File.join( File.dirname(__FILE__), "..", "lib" ) )
|
7
|
+
|
8
|
+
require 'stickler'
|
9
|
+
|
10
|
+
root = File.expand_path( File.join( File.dirname( __FILE__ ), *%w[ .. test data ]))
|
11
|
+
|
12
|
+
puts root
|
13
|
+
use ::Stickler::Middleware::Server, :stickler_root => root
|
14
|
+
run ::Sinatra::Base
|
data/examples/auth_repo.ru
CHANGED
@@ -7,7 +7,7 @@ $:.unshift File.expand_path( File.join( File.dirname(__FILE__), "..", "lib" ) )
|
|
7
7
|
|
8
8
|
require 'stickler'
|
9
9
|
|
10
|
-
tmp = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "
|
10
|
+
tmp = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "test", "tmp" ) )
|
11
11
|
|
12
12
|
use Rack::Auth::Basic, 'Secure Stickler' do |u,p|
|
13
13
|
(u == "stickler") and (p == "secret")
|
data/examples/gemcutter_repo.ru
CHANGED
@@ -12,7 +12,7 @@ $:.unshift File.expand_path( File.join( File.dirname(__FILE__), "..", "lib" ) )
|
|
12
12
|
|
13
13
|
require 'stickler'
|
14
14
|
|
15
|
-
gem_dir = File.expand_path( "../
|
15
|
+
gem_dir = File.expand_path( "../test/tmp", File.dirname( __FILE__ ) )
|
16
16
|
|
17
17
|
use ::Stickler::Middleware::Compression
|
18
18
|
use ::Stickler::Middleware::Gemcutter, :repo_root => gem_dir
|
data/examples/local_repo.ru
CHANGED
@@ -9,7 +9,7 @@ $:.unshift File.expand_path( File.join( File.dirname(__FILE__), "..", "lib" ) )
|
|
9
9
|
require 'stickler/middleware/compression'
|
10
10
|
require 'stickler/middleware/local'
|
11
11
|
|
12
|
-
gem_dir = File.expand_path( File.join( File.dirname( __FILE__ ), *%w[ ..
|
12
|
+
gem_dir = File.expand_path( File.join( File.dirname( __FILE__ ), *%w[ .. test data ]))
|
13
13
|
|
14
14
|
puts gem_dir
|
15
15
|
use ::Stickler::Middleware::Compression
|
data/lib/stickler.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Stickler
|
2
2
|
# The Current Version of the library
|
3
|
-
VERSION = "2.
|
3
|
+
VERSION = "2.4.0"
|
4
4
|
end
|
5
5
|
require 'sinatra/base'
|
6
6
|
|
@@ -8,6 +8,8 @@ require 'stickler/logable'
|
|
8
8
|
require 'stickler/error'
|
9
9
|
require 'stickler/paths'
|
10
10
|
require 'stickler/spec_lite'
|
11
|
+
require 'stickler/gem_container'
|
12
|
+
require 'stickler/gemfile_lock_parser'
|
11
13
|
|
12
14
|
require 'stickler/repository'
|
13
15
|
require 'stickler/middleware'
|
data/lib/stickler/client.rb
CHANGED
@@ -30,7 +30,7 @@ module Stickler
|
|
30
30
|
def parse( argv )
|
31
31
|
opts = Trollop::with_standard_exception_handling( parser ) do
|
32
32
|
o = parser.parse( argv )
|
33
|
-
yield parser if block_given?
|
33
|
+
yield( parser, o ) if block_given?
|
34
34
|
return o
|
35
35
|
end
|
36
36
|
return opts
|
@@ -49,3 +49,4 @@ require 'stickler/client/mirror'
|
|
49
49
|
require 'stickler/client/push'
|
50
50
|
require 'stickler/client/unyank'
|
51
51
|
require 'stickler/client/yank'
|
52
|
+
require 'stickler/client/latest-version'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Stickler
|
2
|
+
class Client
|
3
|
+
class LatestVersion < Stickler::Client
|
4
|
+
def self.banner
|
5
|
+
<<-_
|
6
|
+
Prints the latest version of a gem
|
7
|
+
|
8
|
+
Usage: stickler latest-version gem-name
|
9
|
+
|
10
|
+
Options:
|
11
|
+
_
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse( argv )
|
15
|
+
gem_name = nil
|
16
|
+
opts = super( argv ) do |p,o|
|
17
|
+
raise Trollop::CommandlineError, "At lest one gem-name is required" if p.leftovers.empty?
|
18
|
+
gem_name = p.leftovers.shift
|
19
|
+
end
|
20
|
+
opts[:gem_name] = gem_name
|
21
|
+
return opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
opts = parse( self.argv )
|
26
|
+
repo = remote_repo_for( opts )
|
27
|
+
match = repo.latest_specs_list.find do |name, version, platform|
|
28
|
+
name == opts[:gem_name]
|
29
|
+
end
|
30
|
+
if match then
|
31
|
+
$stdout.puts match[1]
|
32
|
+
else
|
33
|
+
$stdout.puts "Gem #{opts[:gem_name]} not found in remote repository"
|
34
|
+
end
|
35
|
+
rescue Stickler::Repository::Error => e
|
36
|
+
$stdout.puts "ERROR: #{e.message}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -5,9 +5,11 @@ module Stickler
|
|
5
5
|
def self.banner
|
6
6
|
<<-_
|
7
7
|
Pull a specific version of a gem from an upstream gem server
|
8
|
-
and store it in a stickler server.
|
8
|
+
and store it in a stickler server. Either a specific version
|
9
|
+
must be specificied, or a Gemfile.lock must be used.
|
9
10
|
|
10
11
|
Usage: stickler mirror [options] --gem-version x.y.z gem
|
12
|
+
stickler mirror [options] Gemfile.lock
|
11
13
|
|
12
14
|
Options:
|
13
15
|
_
|
@@ -17,19 +19,26 @@ _
|
|
17
19
|
unless @parser then
|
18
20
|
@parser = super
|
19
21
|
@parser.opt( :upstream, "The upstream gem server from which to pull", :type => :string, :default => Client.config.upstream )
|
20
|
-
@parser.opt( :gem_version, "The version of the gem to
|
21
|
-
@parser.opt( :platform, "The platform of the gem to
|
22
|
+
@parser.opt( :gem_version, "The version of the gem to mirror", :type => :string)
|
23
|
+
@parser.opt( :platform, "The platform of the gem to mirror", :type => :string, :default => ::Gem::Platform::RUBY )
|
22
24
|
end
|
23
25
|
return @parser
|
24
26
|
end
|
25
27
|
|
26
28
|
def parse( argv )
|
27
|
-
gem_name
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
gem_name = nil
|
30
|
+
gemfile_lock = nil
|
31
|
+
opts = super( argv ) do |p, o|
|
32
|
+
raise Trollop::CommandlineError, "A Gemfile.lock or a gem name is required to mirror" if p.leftovers.empty?
|
33
|
+
if o[:gem_version] then
|
34
|
+
gem_name = p.leftovers.shift
|
35
|
+
else
|
36
|
+
gemfile_lock = p.leftovers.shift
|
37
|
+
raise Trollop::CommandlineError, "#{lock} must be readable" unless File.readable?( gemfile_lock )
|
38
|
+
end
|
31
39
|
end
|
32
|
-
opts[:gem_name]
|
40
|
+
opts[:gem_name] = gem_name
|
41
|
+
opts[:gemfile_lock] = gemfile_lock
|
33
42
|
return opts
|
34
43
|
end
|
35
44
|
|
@@ -41,24 +50,47 @@ _
|
|
41
50
|
Stickler::Repository::RemoteMirror.new( opts[:server], :debug => opts[:debug] )
|
42
51
|
end
|
43
52
|
|
44
|
-
def
|
45
|
-
opts
|
46
|
-
|
47
|
-
|
48
|
-
|
53
|
+
def spec_list( opts )
|
54
|
+
if opts[:gem_name] then
|
55
|
+
return [Stickler::SpecLite.new( opts[:gem_name], opts[:gem_version], opts[:platform] )]
|
56
|
+
end
|
57
|
+
|
58
|
+
if opts[:gemfile_lock] then
|
59
|
+
parser = Stickler::GemfileLockParser.new( opts[:gemfile_lock] )
|
60
|
+
return parser.gem_dependencies
|
61
|
+
end
|
62
|
+
raise Sticker::Error, "No gem name, or gemfile lock... no idea what to do"
|
63
|
+
end
|
49
64
|
|
65
|
+
def mirror_one_spec( repo, spec, upstream_host )
|
50
66
|
$stdout.write "Asking #{repo.uri} to mirror #{spec.full_name} from #{upstream_host} : "
|
51
67
|
$stdout.flush
|
52
68
|
|
53
69
|
resp = repo.mirror( spec, upstream_host )
|
54
|
-
|
55
70
|
$stdout.puts "OK -> #{repo.uri.join(resp.headers['Location'])}"
|
71
|
+
|
72
|
+
rescue Stickler::Repository::Error => e
|
73
|
+
$stdout.puts "ERROR: #{e.message}"
|
74
|
+
rescue StandardError => e
|
75
|
+
$stdout.puts e.backtrace.join("\n")
|
76
|
+
$stdout.puts "ERROR -> #{e.message}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def run
|
80
|
+
opts = parse( self.argv )
|
81
|
+
repo = remote_repo_for( opts )
|
82
|
+
specs = spec_list( opts )
|
83
|
+
upstream_host = Addressable::URI.parse( opts[:upstream] ).host
|
84
|
+
|
85
|
+
specs.each do |spec|
|
86
|
+
mirror_one_spec( repo, spec, upstream_host )
|
87
|
+
end
|
56
88
|
rescue Stickler::Repository::Error => e
|
57
89
|
$stdout.puts "ERROR: #{e.message}"
|
58
90
|
rescue StandardError => e
|
59
91
|
puts e.backtrace.join("\n")
|
60
92
|
$stdout.puts "ERROR -> #{e.message}"
|
61
|
-
|
93
|
+
end
|
62
94
|
end
|
63
95
|
end
|
64
96
|
end
|
data/lib/stickler/client/push.rb
CHANGED
@@ -14,7 +14,7 @@ _
|
|
14
14
|
|
15
15
|
def parse( argv )
|
16
16
|
gemfiles = []
|
17
|
-
opts = super do |p|
|
17
|
+
opts = super do |p,o|
|
18
18
|
raise Trollop::CommandlineError, "At least one file is required to push" if p.leftovers.empty?
|
19
19
|
p.leftovers.each do |gemfile|
|
20
20
|
raise Trollop::CommandlineError, "#{gemfile} must be readable" unless File.readable?( gemfile )
|
data/lib/stickler/client/yank.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Stickler
|
2
|
+
#
|
3
|
+
# Wrap the class that opens the gem file and gives access to all the gem file
|
4
|
+
# internals. The class that implements this in rubygems itself changed, so we
|
5
|
+
# need to be backwards compatible with folks that are using older versions of
|
6
|
+
# rubygems.
|
7
|
+
#
|
8
|
+
class GemContainer
|
9
|
+
attr_reader :path
|
10
|
+
def initialize( gem_file_path )
|
11
|
+
@path = gem_file_path
|
12
|
+
@container = load_container( path )
|
13
|
+
end
|
14
|
+
|
15
|
+
def spec
|
16
|
+
@container.spec
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Rubygems transitions to using Gem::Package, so if we have that use it,
|
22
|
+
# otherwise fall back to the older method of using Gem::Format
|
23
|
+
begin
|
24
|
+
require 'rubygems/package'
|
25
|
+
def load_container( path )
|
26
|
+
Gem::Package.new( path )
|
27
|
+
end
|
28
|
+
rescue LoadError
|
29
|
+
puts "Unable to load 'rubygems/package' falling back to Gem::Format"
|
30
|
+
begin
|
31
|
+
require 'rubygems/format'
|
32
|
+
def load_container( path )
|
33
|
+
Gem::Format.from_file_by_path( path )
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
abort "FAilure to load rubygems/format"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Stickler
|
2
|
+
class GemfileLockParser
|
3
|
+
attr_reader :gem_dependencies
|
4
|
+
|
5
|
+
|
6
|
+
def initialize( path )
|
7
|
+
p = Pathname.new( path )
|
8
|
+
raise Stickler::Error, "#{path} does not exist" unless p.exist?
|
9
|
+
raise Stickler::Error, "#{path} is not readable" unless p.readable?
|
10
|
+
parse( p.read )
|
11
|
+
end
|
12
|
+
|
13
|
+
def depends_on?( name )
|
14
|
+
gem_dependencies.any?{ |spec| spec.name == name }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def parse( text )
|
20
|
+
parts = partition( text )
|
21
|
+
@gem_dependencies = parse_dependencies( parts['GEM'] )
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_dependencies( lines )
|
25
|
+
drop_until_specs( lines )
|
26
|
+
deps = []
|
27
|
+
lines.each do |line|
|
28
|
+
md = line.match( /\A\s{4}(\S+)\s+\(([\w\.]+)\)\Z/ )
|
29
|
+
next if md.nil?
|
30
|
+
deps << Stickler::SpecLite.new( md.captures[0], md.captures[1] )
|
31
|
+
end
|
32
|
+
return deps
|
33
|
+
end
|
34
|
+
|
35
|
+
def drop_until_specs( lines )
|
36
|
+
lines.drop_while{ |l| %w[ remote specs ].include?( l.strip.split(":").first ) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def partition( text )
|
40
|
+
text.split("\n\n").each_with_object({}) { | p, h |
|
41
|
+
next if p.empty?
|
42
|
+
parts = p.split("\n").map(&:rstrip)
|
43
|
+
h[parts.first] = parts[1..-1]
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/stickler/middleware.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
module Stickler::Middleware
|
3
|
+
# Server is the entire stickler stack as a single piece of middleware that
|
4
|
+
# may be used by other libraries that would like to include Stickler in their
|
5
|
+
# application.
|
6
|
+
class Server
|
7
|
+
attr_reader :stickler_root
|
8
|
+
|
9
|
+
def initialize( app, opts = {} )
|
10
|
+
@app = app
|
11
|
+
@stickler_root = Pathname.new( opts.fetch( :stickler_root ) ).realpath
|
12
|
+
@run = server_app
|
13
|
+
validate
|
14
|
+
end
|
15
|
+
|
16
|
+
def call( env )
|
17
|
+
@run.call( env )
|
18
|
+
end
|
19
|
+
|
20
|
+
def server_app
|
21
|
+
root = self.stickler_root
|
22
|
+
Rack::Builder.app( @app )do
|
23
|
+
use Stickler::Middleware::Compression
|
24
|
+
use Stickler::Middleware::Gemcutter, :serve_indexes => false, :repo_root => root.join( "gemcutter" )
|
25
|
+
use Stickler::Middleware::Mirror, :serve_indexes => false, :repo_root => root.join( "mirror" )
|
26
|
+
use Stickler::Middleware::Index, :serve_indexes => true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def validate
|
33
|
+
raise ::Stickler::Error, "Stickler root directory '#{stickler_root}' must already exist" unless stickler_root.directory?
|
34
|
+
raise ::Stickler::Error, "Stickler root directory '#{stickler_root}' must be writable" unless stickler_root.writable?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -109,6 +109,22 @@ module Stickler::Repository
|
|
109
109
|
raise NotImplementedError, not_implemented_msg( :yank )
|
110
110
|
end
|
111
111
|
|
112
|
+
#
|
113
|
+
# :call-seq:
|
114
|
+
# repo.unyank( spec ) -> true or nil
|
115
|
+
#
|
116
|
+
# "unyank" in the sense of undoing "yank"
|
117
|
+
#
|
118
|
+
# This means, put the gem matching +spec+ back into the index so that it
|
119
|
+
# will be found during searching.
|
120
|
+
#
|
121
|
+
# If the gem is sucessfully put back into the index then true is returned.
|
122
|
+
# Otherwise nil is returned
|
123
|
+
#
|
124
|
+
def unyank( spec )
|
125
|
+
raise NotImplementedError, not_implemented_msg( :unyank )
|
126
|
+
end
|
127
|
+
|
112
128
|
#
|
113
129
|
# :call-seq:
|
114
130
|
# repo.get( spec ) -> bytes
|
@@ -2,7 +2,6 @@ require 'stickler/repository/index'
|
|
2
2
|
require 'addressable/uri'
|
3
3
|
require 'tempfile'
|
4
4
|
require 'forwardable'
|
5
|
-
require 'rubygems/format'
|
6
5
|
|
7
6
|
module Stickler::Repository
|
8
7
|
#
|
@@ -180,13 +179,14 @@ module Stickler::Repository
|
|
180
179
|
#
|
181
180
|
def add( io )
|
182
181
|
# spooling to a temp file because Gem::Format.from_io() closes the io
|
183
|
-
# stream it is sent. Why it does this, I do not know.
|
182
|
+
# stream it is sent. Why it does this, I do not know. This may be
|
183
|
+
# removed once we are no longer using older rubygems
|
184
184
|
tempfile = Tempfile.new( "uploaded-gem.", temp_dir )
|
185
185
|
tempfile.write( io.read )
|
186
186
|
tempfile.rewind
|
187
187
|
|
188
|
-
|
189
|
-
spec = Stickler::SpecLite.new(
|
188
|
+
container = Stickler::GemContainer.new( tempfile.path )
|
189
|
+
spec = Stickler::SpecLite.new( container.spec.name, container.spec.version, container.spec.platform )
|
190
190
|
specs = search_for( spec )
|
191
191
|
|
192
192
|
raise Error, "gem #{spec.full_name} already exists" unless specs.empty?
|
@@ -201,8 +201,6 @@ module Stickler::Repository
|
|
201
201
|
# See Api#push
|
202
202
|
#
|
203
203
|
def push( path )
|
204
|
-
# is this line needed? Never used.
|
205
|
-
# spec = specification_from_gem_file( path )
|
206
204
|
result = nil
|
207
205
|
File.open( path ) do |io|
|
208
206
|
result = add( io )
|
@@ -302,8 +300,8 @@ module Stickler::Repository
|
|
302
300
|
end
|
303
301
|
|
304
302
|
def specification_from_gem_file( path )
|
305
|
-
|
306
|
-
return
|
303
|
+
container = Stickler::GemContainer.new( path )
|
304
|
+
return container.spec
|
307
305
|
end
|
308
306
|
|
309
307
|
def speclite_from_specification( spec )
|