stickler 2.0.0a → 2.0.1
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/.bnsignore +14 -0
- data/.gitignore +17 -0
- data/HISTORY.asciidoc +20 -0
- data/README.asciidoc +126 -0
- data/Rakefile +22 -3
- data/bin/stickler +50 -0
- data/bin/stickler-passenger-config +112 -0
- data/bin/stickler-server +109 -0
- data/examples/config.ru +6 -3
- data/examples/gemcutter_repo.ru +2 -0
- data/examples/index_repo.ru +2 -0
- data/examples/local_repo.ru +6 -3
- data/examples/mirror_repo.ru +2 -0
- data/examples/not_found.ru +2 -0
- data/lib/stickler.rb +12 -0
- data/lib/stickler/client.rb +47 -0
- data/lib/stickler/client/config.rb +35 -0
- data/lib/stickler/client/config_file.rb +58 -0
- data/lib/stickler/client/mirror.rb +61 -0
- data/lib/stickler/client/push.rb +50 -0
- data/lib/stickler/client/yank.rb +51 -0
- data/lib/stickler/logable.rb +35 -0
- data/lib/stickler/middleware/gemcutter.rb +5 -0
- data/lib/stickler/middleware/helpers.rb +32 -0
- data/lib/stickler/middleware/index.rb +30 -4
- data/lib/stickler/middleware/mirror.rb +8 -3
- data/lib/stickler/middleware/not_found.rb +4 -2
- data/lib/stickler/paths.rb +53 -0
- data/lib/stickler/repository/local.rb +12 -12
- data/lib/stickler/repository/mirror.rb +13 -6
- data/lib/stickler/repository/null.rb +1 -0
- data/lib/stickler/repository/remote.rb +10 -4
- data/lib/stickler/repository/rubygems_authenticator.rb +32 -0
- data/lib/stickler/server.rb +34 -0
- data/lib/stickler/server/public/css/blueprint/LICENSE +22 -0
- data/lib/stickler/server/public/css/blueprint/ie.css +35 -0
- data/lib/stickler/server/public/css/blueprint/screen.css +266 -0
- data/lib/stickler/server/public/css/style.css +19 -0
- data/lib/stickler/server/public/images/apple-touch-icon.png +0 -0
- data/lib/stickler/server/public/images/favicon.ico +0 -0
- data/lib/stickler/server/public/js/modernizr-1.5.min.js +28 -0
- data/lib/stickler/server/views/index.erb +35 -0
- data/lib/stickler/server/views/layout.erb +42 -0
- data/lib/stickler/spec_lite.rb +16 -6
- data/lib/stickler/version.rb +1 -1
- data/man/asciidoc.conf +25 -0
- data/man/stickler-passenger-config.asciidoc +74 -0
- data/man/stickler-server.asciidoc +87 -0
- data/man/stickler.asciidoc +148 -0
- data/spec/middleware/common_gem_server_helpers.rb +4 -2
- data/spec/middleware/index_spec.rb +3 -3
- data/spec/middleware/legacy_gem_server_behavior.rb +0 -2
- data/spec/middleware/local_spec.rb +3 -3
- data/spec/middleware/modern_gem_server_behavior.rb +2 -0
- data/spec/paths_spec.rb +13 -0
- data/spec/spec_lite_spec.rb +14 -0
- data/tasks/man.rake +19 -0
- metadata +183 -56
- data/HISTORY.rdoc +0 -12
- data/README.rdoc +0 -88
- data/lib/stickler/web.rb +0 -19
- data/stickler.gemspec +0 -60
- data/views/index.erb +0 -19
- data/views/layout.erb +0 -39
data/examples/config.ru
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
#-----------------------------------------------------------------------
|
2
2
|
# Example rackup file for an entire stickler stack
|
3
|
+
#
|
4
|
+
# -*- vim: set ft=ruby: -*-
|
3
5
|
#-----------------------------------------------------------------------
|
4
6
|
$:.unshift File.expand_path( File.join( File.dirname(__FILE__), "..", "lib" ) )
|
5
7
|
|
6
|
-
require 'stickler
|
8
|
+
require 'stickler'
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
tmp = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "tmp" ) )
|
11
|
+
|
12
|
+
run Stickler::Server.new( tmp ).app
|
data/examples/gemcutter_repo.ru
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#-----------------------------------------------------------------------
|
2
|
+
# -*- vim: set ft=ruby: -*-
|
3
|
+
#
|
2
4
|
# An Example remote repository that implements all the methods that are
|
3
5
|
# required to satisfy being talked to by a Respository::Remote client.
|
4
6
|
# This means it needs to speak:
|
data/examples/index_repo.ru
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#-----------------------------------------------------------------------
|
2
|
+
#-*- vim: set ft=ruby: -*-
|
3
|
+
#
|
2
4
|
# Example rackup file for serving up a null repository. This really
|
3
5
|
# would never be used in the wild, but it shows the basics of what
|
4
6
|
# is required to setup a stickler webstack
|
data/examples/local_repo.ru
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
#-----------------------------------------------------------------------
|
2
|
+
#-*- vim: set ft=ruby: -*-
|
3
|
+
#
|
2
4
|
# Example rackup file for serving up a single repository. This repository
|
3
5
|
# will respond to index and gem requests.
|
4
6
|
#-----------------------------------------------------------------------
|
5
7
|
$:.unshift File.expand_path( File.join( File.dirname(__FILE__), "..", "lib" ) )
|
6
8
|
|
7
9
|
require 'stickler/middleware/compression'
|
8
|
-
require 'stickler/middleware/
|
10
|
+
require 'stickler/middleware/local'
|
9
11
|
|
10
|
-
gem_dir = File.
|
12
|
+
gem_dir = File.expand_path( File.join( File.dirname( __FILE__ ), *%w[ .. spec data ]))
|
11
13
|
|
14
|
+
puts gem_dir
|
12
15
|
use ::Stickler::Middleware::Compression
|
13
|
-
use ::Stickler::Middleware::
|
16
|
+
use ::Stickler::Middleware::Local, :repo_root => gem_dir
|
14
17
|
run ::Sinatra::Base
|
data/examples/mirror_repo.ru
CHANGED
data/examples/not_found.ru
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#-----------------------------------------------------------------------
|
2
|
+
#-*- vim: set ft=ruby: -*-
|
3
|
+
#
|
2
4
|
# Serve up the not found page for everything
|
3
5
|
#-----------------------------------------------------------------------
|
4
6
|
$:.unshift File.expand_path( File.join( File.dirname(__FILE__), "..", "lib" ) )
|
data/lib/stickler.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'trollop'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'stickler/client/config_file'
|
4
|
+
|
5
|
+
module Stickler
|
6
|
+
class Client
|
7
|
+
|
8
|
+
attr_reader :argv
|
9
|
+
attr_reader :sources
|
10
|
+
|
11
|
+
def self.config
|
12
|
+
::Stickler::Client::ConfigFile.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize( argv = ARGV )
|
16
|
+
@argv = argv
|
17
|
+
end
|
18
|
+
|
19
|
+
def parser
|
20
|
+
me = self # scoping forces this
|
21
|
+
@parser ||= Trollop::Parser.new do
|
22
|
+
banner me.class.banner
|
23
|
+
opt :server, "The gem or stickler server URL", :type => :string, :default => Client.config.server
|
24
|
+
opt :debug, "Output debug information for the server interaction", :default => false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse( argv )
|
29
|
+
opts = Trollop::with_standard_exception_handling( parser ) do
|
30
|
+
raise Trollop::HelpNeeded if argv.empty? # show help screen
|
31
|
+
o = parser.parse( argv )
|
32
|
+
yield parser if block_given?
|
33
|
+
return o
|
34
|
+
end
|
35
|
+
return opts
|
36
|
+
end
|
37
|
+
|
38
|
+
def remote_repo_for( opts )
|
39
|
+
Stickler::Repository::Remote.new( opts[:server], :debug => opts[:debug] )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
require 'stickler/client/push'
|
45
|
+
require 'stickler/client/yank'
|
46
|
+
require 'stickler/client/mirror'
|
47
|
+
require 'stickler/client/config'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Stickler
|
2
|
+
class Client
|
3
|
+
class Config < Stickler::Client
|
4
|
+
def self.banner
|
5
|
+
<<-_
|
6
|
+
Access or update the Stickler client configuration.
|
7
|
+
|
8
|
+
Usage: stickler config [options]
|
9
|
+
|
10
|
+
Options:
|
11
|
+
_
|
12
|
+
end
|
13
|
+
|
14
|
+
def parser
|
15
|
+
unless @parser then
|
16
|
+
@parser = super
|
17
|
+
@parser.opt( :upstream, "The upstream gem server from which to pull", :type => :string, :default => Client.config.upstream )
|
18
|
+
@parser.opt( :add, "Add upstream and/or server to the configuration", :type => :boolean )
|
19
|
+
@parser.opt( :list, "display the current configuration", :type => :boolean )
|
20
|
+
end
|
21
|
+
return @parser
|
22
|
+
end
|
23
|
+
|
24
|
+
def dump_config( opts )
|
25
|
+
puts " server : #{Client.config.server}"
|
26
|
+
puts "upstream : #{Client.config.upstream}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
opts = parse( self.argv )
|
31
|
+
dump_config( opts ) if Client.config.update( opts ) || opts[:list]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'stickler/repository/rubygems_authenticator'
|
3
|
+
|
4
|
+
module Stickler
|
5
|
+
class Client
|
6
|
+
class ConfigFile
|
7
|
+
def initalize
|
8
|
+
@updated = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def config_path
|
12
|
+
File.join(Gem.user_home, '.gem', 'stickler')
|
13
|
+
end
|
14
|
+
|
15
|
+
def configuration
|
16
|
+
Gem.configuration.load_file(config_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def update( opts )
|
20
|
+
self.server = opts[:server] if opts[:server]
|
21
|
+
self.upstream = opts[:upstream] if opts[:upstream]
|
22
|
+
return updated?
|
23
|
+
end
|
24
|
+
|
25
|
+
def updated?
|
26
|
+
return @updated
|
27
|
+
end
|
28
|
+
|
29
|
+
def server
|
30
|
+
configuration[:server]
|
31
|
+
end
|
32
|
+
|
33
|
+
def server=( server )
|
34
|
+
config = configuration.merge( :server => server )
|
35
|
+
save_config( config )
|
36
|
+
end
|
37
|
+
|
38
|
+
def upstream
|
39
|
+
configuration[:upstream] || ::Stickler::Repository::RubygemsAuthenticator.rubygems_uri.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def upstream=( upstream )
|
43
|
+
config = configuration.merge( :upstream => upstream )
|
44
|
+
save_config( config )
|
45
|
+
end
|
46
|
+
|
47
|
+
def save_config( config )
|
48
|
+
dirname = File.dirname(config_path)
|
49
|
+
Dir.mkdir(dirname) unless File.exists?(dirname)
|
50
|
+
|
51
|
+
File.open(config_path, "w") do |f|
|
52
|
+
f.write config.to_yaml
|
53
|
+
end
|
54
|
+
@updated = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Stickler
|
2
|
+
class Client
|
3
|
+
class Mirror < Stickler::Client
|
4
|
+
def self.banner
|
5
|
+
<<-_
|
6
|
+
Pull a specific version of a gem from an upstream gem server
|
7
|
+
and store it in a stickler server.
|
8
|
+
|
9
|
+
Usage: stickler mirror [options] --gem-version x.y.z gem
|
10
|
+
|
11
|
+
Options:
|
12
|
+
_
|
13
|
+
end
|
14
|
+
|
15
|
+
def parser
|
16
|
+
unless @parser then
|
17
|
+
@parser = super
|
18
|
+
@parser.opt( :upstream, "The upstream gem server from which to pull", :type => :string, :default => Client.config.upstream )
|
19
|
+
@parser.opt( :gem_version, "The version of the gem to yank (required)", :type => :string, :required => true )
|
20
|
+
@parser.opt( :platform, "The platform of the gem to yank", :type => :string, :default => ::Gem::Platform::RUBY )
|
21
|
+
end
|
22
|
+
return @parser
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse( argv )
|
26
|
+
gem_name = nil
|
27
|
+
opts = super( argv ) do |p|
|
28
|
+
raise Trollop::CommandlineError, "At least one gem is required to mirror" if p.leftovers.empty?
|
29
|
+
gem_name = p.leftovers.shift
|
30
|
+
end
|
31
|
+
opts[:gem_name] = gem_name
|
32
|
+
return opts
|
33
|
+
end
|
34
|
+
|
35
|
+
def resource_uri( opts )
|
36
|
+
opts[:server] || Client.config.server
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
opts = parse( self.argv )
|
41
|
+
repo = remote_repo_for( opts )
|
42
|
+
spec = Stickler::SpecLite.new( opts[:gem_name], opts[:gem_version], opts[:platform] )
|
43
|
+
upstream_host = Addressable::URI.parse( opts[:upstream] ).host
|
44
|
+
|
45
|
+
$stdout.write "Asking #{repo.uri} to mirror #{spec.full_name} from #{upstream_host} : "
|
46
|
+
$stdout.flush
|
47
|
+
|
48
|
+
uri = [ repo.uri.join( upstream_host ), opts[:gem_name], opts[:gem_version], opts[:platform] ].join("/")
|
49
|
+
resource = repo.http.resource( uri )
|
50
|
+
|
51
|
+
resp = resource.post
|
52
|
+
$stdout.puts "OK -> #{repo.uri.join(resp.headers['Location'])}"
|
53
|
+
rescue Resourceful::UnsuccessfulHttpRequestError => he
|
54
|
+
resp = he.http_response
|
55
|
+
$stdout.puts "ERROR -> #{resp.body}"
|
56
|
+
rescue StandardError => e
|
57
|
+
$stdout.puts "ERROR -> #{e.message}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'trollop'
|
2
|
+
module Stickler
|
3
|
+
class Client
|
4
|
+
class Push < Stickler::Client
|
5
|
+
def self.banner
|
6
|
+
<<-_
|
7
|
+
Push one or more gems to a gemserver.
|
8
|
+
|
9
|
+
Usage: stickler push [options] gemfile(s)
|
10
|
+
|
11
|
+
Options:
|
12
|
+
_
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse( argv )
|
16
|
+
gemfiles = []
|
17
|
+
opts = super do |p|
|
18
|
+
raise Trollop::CommandlineError, "At least one file is required to push" if p.leftovers.empty?
|
19
|
+
p.leftovers.each do |gemfile|
|
20
|
+
raise Trollop::CommandlineError, "#{gemfile} must be readable" unless File.readable?( gemfile )
|
21
|
+
gemfiles << File.expand_path( gemfile )
|
22
|
+
end
|
23
|
+
end
|
24
|
+
opts[:gemfiles] = gemfiles
|
25
|
+
return opts
|
26
|
+
end
|
27
|
+
|
28
|
+
def run
|
29
|
+
opts = parse( self.argv )
|
30
|
+
repo = remote_repo_for( opts )
|
31
|
+
|
32
|
+
width = opts[:gemfiles].collect { |g| g.length }.sort.last
|
33
|
+
|
34
|
+
puts "Pushing gem(s) to #{repo.uri} ..."
|
35
|
+
opts[:gemfiles].each do |gemfile|
|
36
|
+
begin
|
37
|
+
$stdout.write " #{gemfile.ljust( width )} -> "
|
38
|
+
$stdout.flush
|
39
|
+
spec = repo.push( gemfile )
|
40
|
+
ok_msg = "OK"
|
41
|
+
ok_msg += " #{repo.uri_for_gem( spec )}"
|
42
|
+
$stdout.puts ok_msg
|
43
|
+
rescue Stickler::Repository::Error => e
|
44
|
+
$stdout.puts "ERROR: #{e.message}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Stickler
|
2
|
+
class Client
|
3
|
+
class Yank < Stickler::Client
|
4
|
+
def self.banner
|
5
|
+
<<-_
|
6
|
+
Remove a gem from the gemserver's index.
|
7
|
+
It will still be available for direct download.
|
8
|
+
|
9
|
+
Usage: stickler yank [options] --gem-version x.y.z gem
|
10
|
+
|
11
|
+
Options:
|
12
|
+
_
|
13
|
+
end
|
14
|
+
|
15
|
+
def parser
|
16
|
+
unless @parser then
|
17
|
+
@parser = super
|
18
|
+
@parser.opt( :gem_version, "The version of the gem to yank (required)", :type => :string, :required => true )
|
19
|
+
@parser.opt( :platform, "The platform of the gem to yank", :type => :string, :default => ::Gem::Platform::RUBY )
|
20
|
+
end
|
21
|
+
return @parser
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse( argv )
|
25
|
+
gem_name = nil
|
26
|
+
opts = super( argv ) do |p|
|
27
|
+
raise Trollop::CommandlineError, "At least one gem is required to yank" if p.leftovers.empty?
|
28
|
+
gem_name = p.leftovers.shift
|
29
|
+
end
|
30
|
+
opts[:gem_name] = gem_name
|
31
|
+
return opts
|
32
|
+
end
|
33
|
+
|
34
|
+
def run
|
35
|
+
opts = parse( self.argv )
|
36
|
+
repo = remote_repo_for( opts )
|
37
|
+
spec = Stickler::SpecLite.new( opts[:gem_name], opts[:gem_version], opts[:platform] )
|
38
|
+
|
39
|
+
$stdout.write "Yanking gem #{spec.full_name} from #{repo.uri} : "
|
40
|
+
$stdout.flush
|
41
|
+
if spec = repo.yank( spec ) then
|
42
|
+
$stdout.puts "OK"
|
43
|
+
else
|
44
|
+
$stdout.puts "FAILURE"
|
45
|
+
end
|
46
|
+
rescue Stickler::Repository::Error => e
|
47
|
+
$stdout.puts "ERROR: #{e.message}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'logging'
|
2
|
+
module Stickler
|
3
|
+
|
4
|
+
def self.app_name
|
5
|
+
@app_name || "stickler"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.app_name=( name )
|
9
|
+
@app_name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
class Logging
|
13
|
+
def self.init
|
14
|
+
unless @initialized then
|
15
|
+
layout = ::Logging::Layouts::Pattern.new( :pattern => "%5l %c : %m" )
|
16
|
+
appender = ::Logging::Appenders::Syslog.new( Stickler.app_name,
|
17
|
+
:logopt => ::Syslog::Constants::LOG_CONS | ::Syslog::Constants::LOG_PID,
|
18
|
+
:facility => ::Syslog::Constants::LOG_LOCAL0,
|
19
|
+
:layout => layout)
|
20
|
+
::Logging::Appenders['syslog'] = appender
|
21
|
+
logger = ::Logging::Logger[Stickler]
|
22
|
+
logger.add_appenders( appender )
|
23
|
+
@initialized = true
|
24
|
+
end
|
25
|
+
return @initialized
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Logable
|
30
|
+
def logger
|
31
|
+
Stickler::Logging.init
|
32
|
+
::Logging::Logger[self]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -33,8 +33,10 @@ module Stickler::Middleware
|
|
33
33
|
post '/api/v1/gems' do
|
34
34
|
begin
|
35
35
|
spec = @repo.add( request.body )
|
36
|
+
logger.info( "Pushed #{spec.full_name}" )
|
36
37
|
return spec.to_s
|
37
38
|
rescue Stickler::Repository::Error => e
|
39
|
+
logger.error( "Error adding #{spec.full_name} to repo : #{e}" )
|
38
40
|
error( 500, "Error adding gem to repo: #{e}" )
|
39
41
|
end
|
40
42
|
end
|
@@ -43,8 +45,10 @@ module Stickler::Middleware
|
|
43
45
|
delete '/api/v1/gems/yank' do
|
44
46
|
spec = Stickler::SpecLite.new( params[:gem_name], params[:version] )
|
45
47
|
if s = @repo.yank( spec ) then
|
48
|
+
logger.info( "Yanked #{spec.full_name}" )
|
46
49
|
return "Yanked #{s.to_s}"
|
47
50
|
else
|
51
|
+
logger.warn( "Did not Yank #{spec.full_name}" )
|
48
52
|
error( 503, "Did not Yank #{spec.to_s}" )
|
49
53
|
end
|
50
54
|
end
|
@@ -54,6 +58,7 @@ module Stickler::Middleware
|
|
54
58
|
full_name, name, version, platform = *params[:captures]
|
55
59
|
spec = Stickler::SpecLite.new( name, version, platform )
|
56
60
|
@repo.delete( spec )
|
61
|
+
logger.info( "Deleted #{spec.full_name}" )
|
57
62
|
return "deleted gem #{spec.full_name}"
|
58
63
|
end
|
59
64
|
end
|