r5_geminabox 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright 2012 Tom Lea, Jack Foy, and Rob Nichols
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,133 @@
1
+ # Gem in a Box – Really simple rubygem hosting
2
+ [![Build Status](https://secure.travis-ci.org/geminabox/geminabox.png)](http://travis-ci.org/geminabox/geminabox)
3
+ [![Gem Version](https://badge.fury.io/rb/geminabox.png)](http://badge.fury.io/rb/geminabox)
4
+
5
+ Geminabox lets you host your own gems, and push new gems to it just like with rubygems.org.
6
+ The bundler dependencies API is supported out of the box.
7
+ Authentication is left up to either the web server, or the Rack stack.
8
+ For basic auth, try [Rack::Auth](http://rack.rubyforge.org/doc/Rack/Auth/Basic.html).
9
+
10
+ ![screen shot](http://pics.tomlea.co.uk/bbbba6/geminabox.png)
11
+
12
+ ## System Requirements
13
+
14
+ Tested on Mac OS X 10.8.2
15
+ Ruby 1.9.3-392
16
+
17
+ Tests fail on Ruby 2.0.0-p0
18
+
19
+ ## Server Setup
20
+
21
+ gem install geminabox
22
+
23
+ Create a config.ru as follows:
24
+
25
+ require "rubygems"
26
+ require "geminabox"
27
+
28
+ Geminabox.data = "/var/geminabox-data" # ... or wherever
29
+ run Geminabox::Server
30
+
31
+ Start your gem server with 'rackup' to run WEBrick or hook up the config.ru as you normally would ([passenger][passenger], [thin][thin], [unicorn][unicorn], whatever floats your boat).
32
+
33
+ ## Legacy RubyGems index
34
+
35
+ RubyGems supports generating indexes for the so called legacy versions (< 1.2), and since it is very rare to use such versions nowadays, it can be disabled, thus improving indexing times for large repositories. If it's safe for your application, you can disable support for these legacy versions by adding the following configuration to your config.ru file:
36
+
37
+ Geminabox.build_legacy = false
38
+
39
+ ## RubyGems Proxy
40
+
41
+ Geminabox can be configured to pull gems, it does not currently have, from rubygems.org. To enable this mode you can either:
42
+
43
+ Set RUBYGEM_PROXY to true in the environment:
44
+
45
+ RUBYGEMS_PROXY=true rackup
46
+
47
+ Or in config.ru (before the run command), set:
48
+
49
+ Geminabox.rubygems_proxy = true
50
+
51
+ If you want Geminabox to carry on providing gems when rubygems.org is unavailable, add this to config.ru:
52
+
53
+ Geminabox.allow_remote_failure = true
54
+
55
+ ## HTTP adapter
56
+
57
+ Geminabox uses the HTTPClient gem to manage its connections to remote resources.
58
+ The relationship is managed via Geminabox::HttpClientAdapter.
59
+
60
+ If you would like to use an alternative HTTP gem, create your own adapter
61
+ and specify it in config.ru:
62
+
63
+ Geminabox.http_adapter = YourHttpAdapter.new
64
+
65
+ It is recommend (but not essential) that your adapter inherits from HttpAdapter.
66
+ The adapter will need to replace HttpAdapter's methods with those specific to
67
+ the alternative HTTP gem. It should also be able to handle HTTP proxy
68
+ settings.
69
+
70
+ Defining your own adapter also allows you to configure Geminabox to use the
71
+ local systems SSL certificates.
72
+
73
+ TemplateFaradayAdapter is provided as an example of an alternative HTTPAdapter.
74
+
75
+ ## Client Usage
76
+
77
+ Since version 0.10, Geminabox supports the standard gemcutter push API:
78
+
79
+ gem push pkg/my-awesome-gem-1.0.gem --host HOST
80
+
81
+ You can also use the gem plugin:
82
+
83
+ gem install geminabox
84
+
85
+ gem inabox pkg/my-awesome-gem-1.0.gem
86
+
87
+ Configure Gem in a box (interactive prompt to specify where to upload to):
88
+
89
+ gem inabox -c
90
+
91
+ Change the host to upload to:
92
+
93
+ gem inabox -g HOST
94
+
95
+ Simples!
96
+
97
+ ## Command Line Help
98
+
99
+ Usage: gem inabox GEM [options]
100
+
101
+ Options:
102
+ -c, --configure Configure GemInABox
103
+ -g, --host HOST Host to upload to.
104
+ -o, --overwrite Overwrite Gem.
105
+
106
+
107
+ Common Options:
108
+ -h, --help Get help on this command
109
+ -V, --[no-]verbose Set the verbose level of output
110
+ -q, --quiet Silence commands
111
+ --config-file FILE Use this config file instead of default
112
+ --backtrace Show stack backtrace on errors
113
+ --debug Turn on Ruby debugging
114
+
115
+
116
+ Arguments:
117
+ GEM built gem to push up
118
+
119
+ Summary:
120
+ Push a gem up to your GemInABox
121
+
122
+ Description:
123
+ Push a gem up to your GemInABox
124
+
125
+ ## Licence
126
+
127
+ Fork it, mod it, choose it, use it, make it better. All under the MIT License.
128
+
129
+ [WTFBPPL]: http://tomlea.co.uk/WTFBPPL.txt
130
+ [sinatra]: http://www.sinatrarb.com/
131
+ [passenger]: http://www.modrails.com/
132
+ [thin]: http://code.macournoyer.com/thin/
133
+ [unicorn]: http://unicorn.bogomips.org/
@@ -0,0 +1,79 @@
1
+ require 'rubygems'
2
+ require 'digest/md5'
3
+ require 'builder'
4
+ require 'sinatra/base'
5
+ require 'rubygems/user_interaction'
6
+ require 'rubygems/indexer'
7
+ require 'rubygems/package'
8
+ require 'rss/atom'
9
+ require 'tempfile'
10
+ require 'json'
11
+
12
+ module Geminabox
13
+
14
+ require_relative 'geminabox/version'
15
+ require_relative 'geminabox/proxy'
16
+ require_relative 'geminabox/http_adapter'
17
+
18
+ def self.geminabox_path(file)
19
+ File.join File.dirname(__FILE__), 'geminabox', file
20
+ end
21
+
22
+ autoload :Hostess, geminabox_path('hostess')
23
+ autoload :GemStore, geminabox_path('gem_store')
24
+ autoload :GemStoreError, geminabox_path('gem_store_error')
25
+ autoload :RubygemsDependency, geminabox_path('rubygems_dependency')
26
+ autoload :GemListMerge, geminabox_path('gem_list_merge')
27
+ autoload :GemVersion, geminabox_path('gem_version')
28
+ autoload :GemVersionCollection, geminabox_path('gem_version_collection')
29
+ autoload :Server, geminabox_path('server')
30
+ autoload :DiskCache, geminabox_path('disk_cache')
31
+ autoload :IncomingGem, geminabox_path('incoming_gem')
32
+
33
+ class << self
34
+
35
+ attr_accessor(
36
+ :data,
37
+ :public_folder,
38
+ :build_legacy,
39
+ :incremental_updates,
40
+ :views,
41
+ :allow_replace,
42
+ :gem_permissions,
43
+ :allow_delete,
44
+ :rubygems_proxy,
45
+ :http_adapter,
46
+ :allow_remote_failure
47
+ )
48
+
49
+ def set_defaults(defaults)
50
+ defaults.each do |method, default|
51
+ variable = "@#{method}"
52
+ instance_variable_set(variable, default) unless instance_variable_get(variable)
53
+ end
54
+ end
55
+
56
+ def settings
57
+ Server.settings
58
+ end
59
+
60
+ def call(env)
61
+ Server.call env
62
+ end
63
+ end
64
+
65
+ set_defaults(
66
+ data: File.join(File.dirname(__FILE__), *%w[.. data]),
67
+ public_folder: File.join(File.dirname(__FILE__), *%w[.. public]),
68
+ build_legacy: false,
69
+ incremental_updates: true,
70
+ views: File.join(File.dirname(__FILE__), *%w[.. views]),
71
+ allow_replace: false,
72
+ gem_permissions: 0644,
73
+ rubygems_proxy: (ENV['RUBYGEMS_PROXY'] == 'true'),
74
+ allow_delete: true,
75
+ http_adapter: HttpClientAdapter.new,
76
+ allow_remote_failure: false
77
+ )
78
+
79
+ end
@@ -0,0 +1,73 @@
1
+ require "fileutils"
2
+ module Geminabox
3
+ class Geminabox::DiskCache
4
+ attr_reader :root_path
5
+
6
+ def initialize(root_path)
7
+ @root_path = root_path
8
+ ensure_dir_exists!
9
+ end
10
+
11
+ def flush_key(key)
12
+ path = path(key_hash(key))
13
+ FileUtils.rm_f(path)
14
+ end
15
+
16
+ def flush
17
+ FileUtils.rm_rf(root_path)
18
+ ensure_dir_exists!
19
+ end
20
+
21
+ def cache(key)
22
+ key_hash = key_hash(key)
23
+ read(key_hash) || write(key_hash, yield)
24
+ end
25
+
26
+ def marshal_cache(key)
27
+ key_hash = key_hash(key)
28
+ marshal_read(key_hash) || marshal_write(key_hash, yield)
29
+ end
30
+
31
+ protected
32
+
33
+ def ensure_dir_exists!
34
+ FileUtils.mkdir_p(root_path)
35
+ end
36
+
37
+ def key_hash(key)
38
+ Digest::MD5.hexdigest(key)
39
+ end
40
+
41
+ def path(key_hash)
42
+ File.join(root_path, key_hash)
43
+ end
44
+
45
+ def read(key_hash)
46
+ read_int(key_hash) { |path| File.read(path) }
47
+ end
48
+
49
+ def marshal_read(key_hash)
50
+ read_int(key_hash) { |path| Marshal.load(File.open(path)) }
51
+ end
52
+
53
+ def read_int(key_hash)
54
+ path = path(key_hash)
55
+ yield(path) if File.exists?(path)
56
+ end
57
+
58
+ def write(key_hash, value)
59
+ write_int(key_hash) { |f| f << value }
60
+ value
61
+ end
62
+
63
+ def marshal_write(key_hash, value)
64
+ write_int(key_hash) { |f| Marshal.dump(value, f) }
65
+ value
66
+ end
67
+
68
+ def write_int(key_hash)
69
+ File.open(path(key_hash), 'wb') { |f| yield(f) }
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,51 @@
1
+ module Geminabox
2
+ class GemListMerge
3
+ attr_accessor :list
4
+
5
+ def self.from(*lists)
6
+ lists.map{|list| new(list)}.inject(:merge)
7
+ end
8
+
9
+ def initialize(list)
10
+ @list = list
11
+ end
12
+
13
+ def merge(other)
14
+ combine_hashes(other).values.flatten.sort do |x, y|
15
+ x.values[ignore_dependencies] <=> y.values[ignore_dependencies]
16
+ end
17
+ end
18
+
19
+ def hash
20
+ list.each do |item|
21
+ ensure_symbols_as_keys(item)
22
+ name = item[:name].to_sym
23
+ collection[name] ||= []
24
+ collection[name] << item unless collection[name].include?(item)
25
+ end
26
+ collection
27
+ end
28
+
29
+ def collection
30
+ @collection ||= {}
31
+ end
32
+
33
+ def combine_hashes(other)
34
+ hash.merge(other.hash) do |key, value, other_value|
35
+ (value + other_value).uniq{|v| v.values[ignore_dependencies]}
36
+ end
37
+ end
38
+
39
+ def ignore_dependencies
40
+ 0..-2
41
+ end
42
+
43
+ def ensure_symbols_as_keys(item)
44
+ item.keys.each do |key|
45
+ next if key.kind_of? Symbol
46
+ item[key.to_sym] = item.delete(key)
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,99 @@
1
+ module Geminabox
2
+
3
+ class GemStore
4
+ attr_accessor :gem, :overwrite
5
+
6
+ def self.create(gem, overwrite = false)
7
+ gem_store = new(gem, overwrite)
8
+ gem_store.save
9
+ end
10
+
11
+ def initialize(gem, overwrite = false)
12
+ @gem = gem
13
+ @overwrite = overwrite && overwrite == 'true'
14
+ end
15
+
16
+ def save
17
+ ensure_gem_valid
18
+ prepare_data_folders
19
+ check_replacement_status
20
+ write_and_index
21
+ end
22
+
23
+ def prepare_data_folders
24
+ ensure_existing_data_folder_compatible
25
+ begin
26
+ FileUtils.mkdir_p(File.join(Geminabox.data, "gems"))
27
+ rescue
28
+ raise GemStoreError.new(
29
+ 500,
30
+ "Could not create #{File.expand_path(Geminabox.data)}."
31
+ )
32
+ end
33
+ end
34
+
35
+ def check_replacement_status
36
+ if !overwrite and Geminabox::Server.disallow_replace? and File.exist?(gem.dest_filename)
37
+ if existing_file_digest != gem.hexdigest
38
+ raise GemStoreError.new(409, "Updating an existing gem is not permitted.\nYou should either delete the existing version, or change your version number.")
39
+ else
40
+ raise GemStoreError.new(200, "Ignoring upload, you uploaded the same thing previously.\nPlease use -o to ovewrite.")
41
+ end
42
+ end
43
+ end
44
+
45
+ def ensure_gem_valid
46
+ raise GemStoreError.new(400, "Cannot process gem") unless gem.valid?
47
+ end
48
+
49
+ private
50
+ def ensure_existing_data_folder_compatible
51
+ if File.exists? Geminabox.data
52
+ ensure_data_folder_is_directory
53
+ ensure_data_folder_is_writable
54
+ end
55
+ end
56
+
57
+ def ensure_data_folder_is_directory
58
+ raise GemStoreError.new(
59
+ 500,
60
+ "Please ensure #{File.expand_path(Geminabox.data)} is a directory."
61
+ ) unless File.directory? Geminabox.data
62
+ end
63
+
64
+ def ensure_data_folder_is_writable
65
+ raise GemStoreError.new(
66
+ 500,
67
+ "Please ensure #{File.expand_path(Geminabox.data)} is writable by the geminabox web server."
68
+ ) unless File.writable? Geminabox.data
69
+ end
70
+
71
+ def existing_file_digest
72
+ Digest::SHA1.file(gem.dest_filename).hexdigest
73
+ end
74
+
75
+ def write_and_index
76
+ tmpfile = gem.gem_data
77
+ atomic_write(gem.dest_filename) do |f|
78
+ while blk = tmpfile.read(65536)
79
+ f << blk
80
+ end
81
+ end
82
+ Geminabox::Server.reindex
83
+ end
84
+
85
+ # based on http://as.rubyonrails.org/classes/File.html
86
+ def atomic_write(file_name)
87
+ temp_dir = File.join(Geminabox.data, "_temp")
88
+ FileUtils.mkdir_p(temp_dir)
89
+ temp_file = Tempfile.new("." + File.basename(file_name), temp_dir)
90
+ temp_file.binmode
91
+ yield temp_file
92
+ temp_file.close
93
+ File.rename(temp_file.path, file_name)
94
+ File.chmod(Geminabox::Server.gem_permissions, file_name)
95
+ end
96
+
97
+ end
98
+
99
+ end