stickler 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +339 -0
- data/HISTORY +4 -0
- data/LICENSE +54 -0
- data/README +86 -0
- data/bin/stickler +58 -0
- data/data/stickler.yml +14 -0
- data/gemspec.rb +62 -0
- data/lib/stickler.rb +19 -0
- data/lib/stickler/cli.rb +302 -0
- data/lib/stickler/configuration.rb +74 -0
- data/lib/stickler/console.rb +72 -0
- data/lib/stickler/paths.rb +62 -0
- data/lib/stickler/repository.rb +502 -0
- data/lib/stickler/source.rb +76 -0
- data/lib/stickler/source_group.rb +365 -0
- data/lib/stickler/spec_lite.rb +48 -0
- data/lib/stickler/version.rb +36 -0
- data/spec/configuration_spec.rb +68 -0
- data/spec/paths_spec.rb +25 -0
- data/spec/repository_spec.rb +55 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/version_spec.rb +17 -0
- data/tasks/announce.rake +39 -0
- data/tasks/config.rb +107 -0
- data/tasks/distribution.rake +38 -0
- data/tasks/documentation.rake +31 -0
- data/tasks/rspec.rake +29 -0
- data/tasks/rubyforge.rake +51 -0
- data/tasks/utils.rb +80 -0
- metadata +156 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
module Stickler
|
2
|
+
#
|
3
|
+
#
|
4
|
+
#
|
5
|
+
class Configuration
|
6
|
+
|
7
|
+
attr_reader :config_file_name
|
8
|
+
|
9
|
+
def initialize( config_file_name )
|
10
|
+
@config_file_name = config_file_name
|
11
|
+
@hash = YAML.load_file( config_file_name )
|
12
|
+
@hash['sources'] = @hash['sources'].collect do |uri|
|
13
|
+
p = uri.split("/")
|
14
|
+
p.join("/") + "/" # ensure a trailing slash
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# the array of sources in this configuration
|
19
|
+
def sources
|
20
|
+
hash['sources']
|
21
|
+
end
|
22
|
+
|
23
|
+
# the downstream source this repository represents
|
24
|
+
def downstream_source
|
25
|
+
hash['downstream_source']
|
26
|
+
end
|
27
|
+
|
28
|
+
# The gem and version requirements for this repository
|
29
|
+
def gem_dependencies
|
30
|
+
unless @gem_dependencies
|
31
|
+
@gem_dependencies = []
|
32
|
+
if hash['gems'] then
|
33
|
+
hash['gems'].each do |name, reqs|
|
34
|
+
[ reqs ].flatten.each do |req|
|
35
|
+
@gem_dependencies << ::Gem::Dependency.new( name, req )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return @gem_dependencies
|
41
|
+
end
|
42
|
+
|
43
|
+
def write
|
44
|
+
File.open( config_file_name, 'w' ) do |f|
|
45
|
+
g = Hash.new { |h,k| h[k] = [] }
|
46
|
+
gem_dependencies.each do |d|
|
47
|
+
g[d.name] << d.requirement_list
|
48
|
+
g[d.name].flatten!
|
49
|
+
end
|
50
|
+
hash['gems'] = g
|
51
|
+
f.write hash.to_yaml
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def keys
|
56
|
+
@hash.keys
|
57
|
+
end
|
58
|
+
|
59
|
+
def []( key )
|
60
|
+
@hash[ key.to_s ]
|
61
|
+
end
|
62
|
+
|
63
|
+
def []=( key, value )
|
64
|
+
@hash[ key.to_s ] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
def ==( other )
|
68
|
+
self.class === other and hash == other.hash
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
attr_reader :hash
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
module Stickler
|
3
|
+
#
|
4
|
+
# containment for the output that a user should see. This uses a Logger so
|
5
|
+
# that it can be throttled based on level at some point
|
6
|
+
#
|
7
|
+
class Console
|
8
|
+
class << self
|
9
|
+
#
|
10
|
+
# Items that get logged to stderr for the user to see should use this logger
|
11
|
+
#
|
12
|
+
def logger
|
13
|
+
unless @logger
|
14
|
+
@logger = ::Logging::Logger['User']
|
15
|
+
@logger.level = :info
|
16
|
+
@logger.add_appenders(::Logging::Appender.stderr)
|
17
|
+
::Logging::Appender.stderr.layout = Logging::Layouts::Pattern.new( :pattern => "%m\n" )
|
18
|
+
::Logging::Appender.stderr.level = :info
|
19
|
+
end
|
20
|
+
return @logger
|
21
|
+
end
|
22
|
+
|
23
|
+
# force initialization
|
24
|
+
Console.logger
|
25
|
+
|
26
|
+
#
|
27
|
+
# default logging leve
|
28
|
+
#
|
29
|
+
def default_level
|
30
|
+
:info
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Quick wrappers around the log levels
|
35
|
+
#
|
36
|
+
::Logging::LEVELS.keys.each do |l|
|
37
|
+
module_eval <<-code
|
38
|
+
def #{ l }( msg )
|
39
|
+
@logger.#{l} msg
|
40
|
+
end
|
41
|
+
code
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Turn off the logging
|
46
|
+
#
|
47
|
+
def silent!
|
48
|
+
logger.level = :off
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Resume logging
|
53
|
+
#
|
54
|
+
def resume!
|
55
|
+
logger.level = self.default_level
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
#
|
60
|
+
# Turn off logging for the execution of a block
|
61
|
+
#
|
62
|
+
def silent( &block )
|
63
|
+
begin
|
64
|
+
silent!
|
65
|
+
block.call
|
66
|
+
ensure
|
67
|
+
resume!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. Licensed under the same terms as Ruby. No warranty is
|
4
|
+
# provided. See LICENSE and COPYING for details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
module Stickler
|
8
|
+
|
9
|
+
# Paths module used by all the other modules and classes for
|
10
|
+
# determining paths and default values.
|
11
|
+
#
|
12
|
+
module Paths
|
13
|
+
|
14
|
+
#
|
15
|
+
# The root directory of the project is considered to be the parent directory
|
16
|
+
# of the 'lib' directory. returns the full expanded path of the parent
|
17
|
+
# directory of 'lib' going up the path from the current file. A trailing
|
18
|
+
# File::SEPARATOR is guaranteed.
|
19
|
+
#
|
20
|
+
def self.root_dir
|
21
|
+
unless @root_dir
|
22
|
+
path_parts = ::File.expand_path(__FILE__).split(::File::SEPARATOR)
|
23
|
+
lib_index = path_parts.rindex("lib")
|
24
|
+
@root_dir = path_parts[0...lib_index].join(::File::SEPARATOR) + ::File::SEPARATOR
|
25
|
+
end
|
26
|
+
return @root_dir
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# return the full expanded path of the +config+ directory below +root_dir+.
|
31
|
+
# All parameters passed in are joined on to the result. a Trailing
|
32
|
+
# File::SEPARATOR is guaranteed if _args_ are *not* present
|
33
|
+
#
|
34
|
+
def self.config_path(*args)
|
35
|
+
self.sub_path("config", *args)
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# return the full expanded path of the +data+ directory below +root_dir+.
|
40
|
+
# All parameters passed in are joined on to the result. a Trailing
|
41
|
+
# File::SEPARATOR is guaranteed if _args_ are *not* present
|
42
|
+
#
|
43
|
+
def self.data_path(*args)
|
44
|
+
self.sub_path("data", *args)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# return the full expanded path of the +lib+ directory below +root_dir+.
|
49
|
+
# All parameters passed in are joined on to the result. a Trailing
|
50
|
+
# File::SEPARATOR is guaranteed if _args_ are *not* present
|
51
|
+
#
|
52
|
+
def self.lib_path(*args)
|
53
|
+
self.sub_path("lib", *args)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def self.sub_path(sub,*args)
|
58
|
+
sp = ::File.join(root_dir, sub) + File::SEPARATOR
|
59
|
+
sp = ::File.join(sp, *args) if args
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,502 @@
|
|
1
|
+
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. Licensed under the same terms as Ruby. No warranty is
|
4
|
+
# provided. See LICENSE and COPYING for details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'stickler'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'ostruct'
|
10
|
+
require 'highline'
|
11
|
+
require 'stickler/configuration'
|
12
|
+
require 'stickler/source_group'
|
13
|
+
|
14
|
+
module Stickler
|
15
|
+
#
|
16
|
+
# A Repository a directory with a particular layout used by stickler. It is
|
17
|
+
# is very similar to a GEM_HOME directory. The layout is as follows, this is
|
18
|
+
# all assumed to be under STICKLER_HOME.
|
19
|
+
#
|
20
|
+
# stickler.yml - the Stickler configuration file for this repository.
|
21
|
+
# The existence of this file indicates this is the root
|
22
|
+
# of a stickler repository
|
23
|
+
# gems/ - storage of the actual gem files
|
24
|
+
# cache/ - cache of gem files downloaded from upstream sources
|
25
|
+
# managed by the rubygems fetcher classes
|
26
|
+
# specifications/ - ruby gemspec files
|
27
|
+
# log/ - directory holding rotating logs files for stickler
|
28
|
+
# dist/ - directory holding the distributable gem index location,
|
29
|
+
# rsync this to your webserver, or serve from it directly.
|
30
|
+
#
|
31
|
+
#
|
32
|
+
class Repository
|
33
|
+
class Error < ::StandardError ; end
|
34
|
+
|
35
|
+
# The repository directory. the directory containing the stickler.yml file
|
36
|
+
attr_reader :directory
|
37
|
+
|
38
|
+
# The configuration
|
39
|
+
attr_reader :configuration
|
40
|
+
|
41
|
+
# Are requrements satisfied in a minimal or maximal approach
|
42
|
+
attr_reader :requirement_satisfaction_behavior
|
43
|
+
|
44
|
+
class << self
|
45
|
+
def other_dir_names
|
46
|
+
%w[ gems_dir log_dir specification_dir dist_dir cache_dir ]
|
47
|
+
end
|
48
|
+
|
49
|
+
def config_file_basename
|
50
|
+
"stickler.yml"
|
51
|
+
end
|
52
|
+
|
53
|
+
def basedir
|
54
|
+
"stickler"
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# What should the default stickler directory be, this is one of two
|
59
|
+
# things. If there is a stickler.yml file in the current directory, then
|
60
|
+
# the default directory is the current directory, otherwise, it is the
|
61
|
+
# _stickler_ directory below the current_directory
|
62
|
+
#
|
63
|
+
def default_directory
|
64
|
+
defaults = [ File.join( Dir.pwd, config_file_basename ), File.join( Dir.pwd, basedir ) ]
|
65
|
+
defaults.each do |def_dir|
|
66
|
+
if File.exist?( def_dir ) then
|
67
|
+
return File.dirname( def_dir ) if File.file?( def_dir )
|
68
|
+
return def_dir
|
69
|
+
end
|
70
|
+
end
|
71
|
+
return defaults.last
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# gem requirement information
|
76
|
+
#
|
77
|
+
def requirement_meta
|
78
|
+
[ [ "=" , "Equals version" ],
|
79
|
+
[ "!=" , "Not equal to version" ],
|
80
|
+
[ ">" , "Greater than version" ],
|
81
|
+
[ "<" , "Less than version" ],
|
82
|
+
[ ">=" , "Greater than or equal to" ],
|
83
|
+
[ "<=" , "Less than or equal to" ],
|
84
|
+
[ "~>" , "Approximately greater than" ]]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Initialize a stickler repository
|
90
|
+
#
|
91
|
+
def initialize( opts )
|
92
|
+
|
93
|
+
@directory = File.expand_path( opts['directory'] )
|
94
|
+
@requirement_satisfaction_behavior = ( opts['requirements'] || "maximum" ).to_sym
|
95
|
+
enhance_logging( opts ) if File.directory?( log_dir )
|
96
|
+
@overwrite = opts['force']
|
97
|
+
|
98
|
+
@configuration = nil
|
99
|
+
load_configuration if File.exist?( config_file )
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def configuration_loaded?
|
104
|
+
@configuration.nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# should existing items be overwritten
|
109
|
+
#
|
110
|
+
def overwrite?
|
111
|
+
@overwrite
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# update logging by turning on a log file in the repository directory, and
|
116
|
+
# possibly turning off the stdout logger that is the default.
|
117
|
+
#
|
118
|
+
def enhance_logging( opts )
|
119
|
+
|
120
|
+
layout = ::Logging::Layouts::Pattern.new(
|
121
|
+
:pattern => "[%d] %c %6p %5l : %m\n",
|
122
|
+
:date_pattern => "%Y-%m-%d %H:%M:%S"
|
123
|
+
)
|
124
|
+
Logging::Logger.root.add_appenders ::Logging::Appenders::RollingFile.new( 'stickler_rolling_logfile',
|
125
|
+
{ :filename => log_file,
|
126
|
+
:layout => layout,
|
127
|
+
# at 5MB roll the log
|
128
|
+
:size => 5 * (1024**2),
|
129
|
+
:keep => 5,
|
130
|
+
:safe => true,
|
131
|
+
:level => :debug
|
132
|
+
})
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# return a handle to the repository configuration found in stickler.yml.
|
137
|
+
#
|
138
|
+
def load_configuration
|
139
|
+
begin
|
140
|
+
@configuration = Configuration.new( config_file )
|
141
|
+
source_group # force load
|
142
|
+
rescue => e
|
143
|
+
logger.error "Failure to load configuration #{e}"
|
144
|
+
exit 1
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# The configuration file for the repository
|
150
|
+
#
|
151
|
+
def config_file
|
152
|
+
@config_file ||= File.join( directory, Repository.config_file_basename )
|
153
|
+
end
|
154
|
+
|
155
|
+
#
|
156
|
+
# The log directory
|
157
|
+
#
|
158
|
+
def log_dir
|
159
|
+
@log_dir ||= File.join( directory, 'log' )
|
160
|
+
end
|
161
|
+
|
162
|
+
#
|
163
|
+
# The log file
|
164
|
+
#
|
165
|
+
def log_file
|
166
|
+
@log_file ||= File.join( log_dir, 'stickler.log' )
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# The gem storage directory.
|
171
|
+
#
|
172
|
+
# This holds the raw gem files downloaded from the sources. This is
|
173
|
+
# equivalent to a gem installations 'gems' directory.
|
174
|
+
#
|
175
|
+
def gems_dir
|
176
|
+
@gems_dir ||= File.join( directory, 'gems' )
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# The Gem specification directory
|
181
|
+
#
|
182
|
+
def specification_dir
|
183
|
+
@specification_dir ||= File.join( directory, 'specifications' )
|
184
|
+
end
|
185
|
+
|
186
|
+
#
|
187
|
+
# The cache dir for the downloads
|
188
|
+
#
|
189
|
+
def cache_dir
|
190
|
+
@cache_dir ||= File.join( directory, 'cache' )
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# The Distribution directory
|
195
|
+
#
|
196
|
+
# this is the document root for the webserver that will serve your rubygems.
|
197
|
+
# Or they can be served directly from this location
|
198
|
+
#
|
199
|
+
def dist_dir
|
200
|
+
@dist_dir ||= File.join( directory, 'dist' )
|
201
|
+
end
|
202
|
+
|
203
|
+
#
|
204
|
+
# logging handler
|
205
|
+
#
|
206
|
+
def logger
|
207
|
+
@logger ||= ::Logging::Logger[self]
|
208
|
+
end
|
209
|
+
|
210
|
+
#
|
211
|
+
# The SourceGroup containing all of the sources for this repository
|
212
|
+
#
|
213
|
+
def source_group
|
214
|
+
unless @source_group
|
215
|
+
sg = SourceGroup.new( self )
|
216
|
+
Console.info "Setting up sources"
|
217
|
+
configuration.sources.each do |source_uri|
|
218
|
+
sg.add_source( source_uri )
|
219
|
+
end
|
220
|
+
@source_group = sg
|
221
|
+
end
|
222
|
+
return @source_group
|
223
|
+
end
|
224
|
+
|
225
|
+
#
|
226
|
+
# Is the repository valid?
|
227
|
+
#
|
228
|
+
def valid?
|
229
|
+
if @valid.nil? then
|
230
|
+
begin
|
231
|
+
valid!
|
232
|
+
@valid = true
|
233
|
+
rescue StandardError => e
|
234
|
+
logger.error "Repository is not valid : #{e}"
|
235
|
+
@valid = false
|
236
|
+
end
|
237
|
+
end
|
238
|
+
return @valid
|
239
|
+
end
|
240
|
+
|
241
|
+
#
|
242
|
+
# raise an error if the repository is not valid
|
243
|
+
#
|
244
|
+
def valid!
|
245
|
+
raise Error, "#{directory} does not exist" unless File.exist?( directory )
|
246
|
+
raise Error, "#{directory} is not writable" unless File.writable?( directory )
|
247
|
+
|
248
|
+
raise Error, "#{config_file} does not exist" unless File.exist?( config_file )
|
249
|
+
raise Error, "#{config_file} is not loaded" unless configuration
|
250
|
+
|
251
|
+
Repository.other_dir_names.each do |method|
|
252
|
+
other_dir = self.send( method )
|
253
|
+
raise Error, "#{other_dir} does not exist" unless File.exist?( other_dir )
|
254
|
+
raise Error, "#{other_dir} is not writeable" unless File.writable?( other_dir )
|
255
|
+
end
|
256
|
+
|
257
|
+
if File.exist?( log_file ) then
|
258
|
+
raise Error, "#{log_file} is not writable" unless File.writable?( log_file )
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
# Setup the repository.
|
264
|
+
#
|
265
|
+
# This is executed with the 'setup' mode on the command line. Only those
|
266
|
+
# files and directories that do not already exist are created. Nothing is
|
267
|
+
# destroyed.
|
268
|
+
#
|
269
|
+
def setup
|
270
|
+
if File.exist?( directory ) then
|
271
|
+
Console.info "repository root already exists #{directory}"
|
272
|
+
else
|
273
|
+
FileUtils.mkdir_p( directory )
|
274
|
+
Console.info "created repository root #{directory}"
|
275
|
+
end
|
276
|
+
|
277
|
+
Repository.other_dir_names.each do |method|
|
278
|
+
d = self.send( method )
|
279
|
+
if File.exist?( d ) then
|
280
|
+
Console.info "directory #{d} already exists"
|
281
|
+
else
|
282
|
+
FileUtils.mkdir_p( d )
|
283
|
+
Console.info "created directory #{d}"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
if overwrite? or not File.exist?( config_file ) then
|
288
|
+
FileUtils.cp Stickler::Paths.data_path( Repository.config_file_basename ), config_file
|
289
|
+
Console.info "copied in default configuration to #{config_file}"
|
290
|
+
else
|
291
|
+
Console.info "configuration file #{config_file} already exists"
|
292
|
+
end
|
293
|
+
|
294
|
+
# load the configuration for the repo
|
295
|
+
load_configuration
|
296
|
+
|
297
|
+
rescue => e
|
298
|
+
$stderr.puts "Unable to setup the respository"
|
299
|
+
$stderr.puts e
|
300
|
+
$stderr.puts e.backtrace.join("\n")
|
301
|
+
exit 1
|
302
|
+
end
|
303
|
+
|
304
|
+
#
|
305
|
+
# Report information about the repository. This is what is called from the
|
306
|
+
# 'info' mode on the commandline
|
307
|
+
#
|
308
|
+
def info
|
309
|
+
return unless valid?
|
310
|
+
|
311
|
+
Console.info ""
|
312
|
+
Console.info "Upstream Sources"
|
313
|
+
Console.info "----------------"
|
314
|
+
Console.info ""
|
315
|
+
|
316
|
+
max_width = configuration.sources.collect { |s| s.length }.max
|
317
|
+
source_group.sources.each do |source|
|
318
|
+
Console.info " #{source.uri.to_s.rjust( max_width )} : #{source.source_specs.size} gems available"
|
319
|
+
Console.info " #{" ".rjust( max_width )} : #{source_group.existing_specs_for_source_uri( source.uri ).size} gems existing"
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
Console.info ""
|
324
|
+
Console.info "Configured gems (in stickler.yml)"
|
325
|
+
Console.info "---------------------------------"
|
326
|
+
Console.info ""
|
327
|
+
configuration.gem_dependencies.sort.each do |dep|
|
328
|
+
Console.info "#{dep.name} : #{dep.version_requirements}"
|
329
|
+
end
|
330
|
+
|
331
|
+
Console.info ""
|
332
|
+
Console.info "Existing gems"
|
333
|
+
Console.info "-------------"
|
334
|
+
Console.info ""
|
335
|
+
|
336
|
+
source_group.gems.keys.sort.each do |g|
|
337
|
+
puts g
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
#
|
344
|
+
# Add a source to the repository
|
345
|
+
#
|
346
|
+
def add_source( source_uri )
|
347
|
+
load_configuration unless configuration_loaded?
|
348
|
+
if configuration.sources.include?( source_uri ) then
|
349
|
+
Console.info "#{source_uri} already in sources"
|
350
|
+
else
|
351
|
+
source_group.add_source( source_uri )
|
352
|
+
configuration.sources << source_uri
|
353
|
+
configuration.write
|
354
|
+
Console.info "#{source_uri} added to sources"
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
#
|
359
|
+
# Remove a source from the repository
|
360
|
+
#
|
361
|
+
def remove_source( source_uri )
|
362
|
+
load_configuration unless configuration_loaded?
|
363
|
+
uri = ::URI.parse source_uri
|
364
|
+
if configuration.sources.delete( source_uri ) then
|
365
|
+
source_group.remove_source( source_uri )
|
366
|
+
configuration.write
|
367
|
+
Console.info "#{source_uri} removed from sources"
|
368
|
+
else
|
369
|
+
Console.info "#{source_uri} is not one of your sources"
|
370
|
+
Console.info "Your sources are:"
|
371
|
+
configuration.sources.each do |src|
|
372
|
+
Console.info " #{src}"
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
#
|
378
|
+
# Add a gem to the repository
|
379
|
+
#
|
380
|
+
def add_gem( gem_name, version )
|
381
|
+
|
382
|
+
Console.info ""
|
383
|
+
|
384
|
+
::HighLine.track_eof = false
|
385
|
+
hl = ::HighLine.new( STDIN, STDOUT, :auto)
|
386
|
+
hl.say("You need to pick the #{gem_name} Requirement to configure Stickler.")
|
387
|
+
hl.say("This involves picking one of the following Requirement operators")
|
388
|
+
hl.say('See http://docs.rubygems.org/read/chapter/16#page74 for operator info.')
|
389
|
+
hl.say("\nYou need to (1) pick an operator and (2) pick a requirement.")
|
390
|
+
hl.say("The most common operators are >=, > and ~>")
|
391
|
+
|
392
|
+
op = hl.choose(*Repository.requirement_meta.collect { |k,v| "#{k.ljust(3)}#{v}" } ) do |m|
|
393
|
+
m.prompt = "(1) Pick an operator ? "
|
394
|
+
end
|
395
|
+
|
396
|
+
op = op.split.first # get only the operator, not the trailing text
|
397
|
+
|
398
|
+
version = ::Gem::Requirement.default if version == :latest
|
399
|
+
search_pattern = ::Gem::Dependency.new( gem_name, version )
|
400
|
+
choices = []
|
401
|
+
source_group.search( search_pattern ).each do |spec|
|
402
|
+
choices << "#{op} #{spec.version.to_s}"
|
403
|
+
end
|
404
|
+
choices = choices.sort.reverse
|
405
|
+
|
406
|
+
hl.say("\nNow to pick a requirement. Based upon your chosen operator '#{op}',")
|
407
|
+
hl.say("These are the available version of the #{gem_name} gem.")
|
408
|
+
requirement = hl.choose do |m|
|
409
|
+
m.flow = :columns_down
|
410
|
+
m.prompt = "(2) Pick a requirement ? "
|
411
|
+
m.choices( *choices )
|
412
|
+
end
|
413
|
+
|
414
|
+
Console.info ""
|
415
|
+
|
416
|
+
dep = ::Gem::Dependency.new( gem_name, requirement )
|
417
|
+
if configuration.gem_dependencies.include?( dep ) then
|
418
|
+
Console.info "#{dep} is already in your list of gems"
|
419
|
+
else
|
420
|
+
source_group.add_from_dependency( dep )
|
421
|
+
configuration.gem_dependencies << dep
|
422
|
+
configuration.write
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
#
|
427
|
+
# Remove a gem from the repository
|
428
|
+
#
|
429
|
+
def remove_gem( gem_name, version )
|
430
|
+
Console.info ""
|
431
|
+
version = ::Gem::Requirement.default if version == :all
|
432
|
+
search_pattern = ::Gem::Dependency.new( gem_name, version )
|
433
|
+
ulist = source_group.search_existing( search_pattern )
|
434
|
+
source_group.search_existing( search_pattern ).each do |spec|
|
435
|
+
source_group.remove( spec )
|
436
|
+
configuration.gem_dependencies.reject! { |d| d.name == spec.name }
|
437
|
+
end
|
438
|
+
configuration.write
|
439
|
+
end
|
440
|
+
|
441
|
+
#
|
442
|
+
# Sync the repository
|
443
|
+
#
|
444
|
+
def sync( rebuild = false )
|
445
|
+
Console.info ""
|
446
|
+
Console.info "Making sure that all gems listed in configuration are available"
|
447
|
+
Console.info ""
|
448
|
+
|
449
|
+
if rebuild then
|
450
|
+
Console.info "Removing existing gems and specifications ... "
|
451
|
+
Dir[ File.join( gems_dir, "*.gem" ) ].each { |g| FileUtils.rm_f g }
|
452
|
+
Dir[ File.join( specification_dir , "*.gemspec" ) ].each { |s| FileUtils.rm_f s }
|
453
|
+
end
|
454
|
+
configuration.gem_dependencies.each do |dep|
|
455
|
+
source_group.add_from_dependency( dep )
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
#
|
460
|
+
# generate the system configuration to be used by rubygem clients of the
|
461
|
+
# repository that stickler managers
|
462
|
+
#
|
463
|
+
def generate_sysconfig( to = $stdout )
|
464
|
+
Console.info "Generating configuration to stdout"
|
465
|
+
txt = <<-cfg
|
466
|
+
#
|
467
|
+
# This is the system wide configuration to be used by
|
468
|
+
# rubygem clients that install gems from the repository
|
469
|
+
# located at :
|
470
|
+
#
|
471
|
+
# #{configuration.downstream_source}
|
472
|
+
#
|
473
|
+
# On Unix like machines install in
|
474
|
+
#
|
475
|
+
# /etc/gemrc
|
476
|
+
#
|
477
|
+
# On Windows machines install in
|
478
|
+
#
|
479
|
+
# C:\\Documents and Settings\\All Users\\Application Data\\gemrc
|
480
|
+
#
|
481
|
+
---
|
482
|
+
:sources:
|
483
|
+
- #{configuration.downstream_source}
|
484
|
+
cfg
|
485
|
+
to.puts txt
|
486
|
+
end
|
487
|
+
|
488
|
+
#
|
489
|
+
# Generate the gem index that can be rsynced to another location
|
490
|
+
#
|
491
|
+
#
|
492
|
+
def generate_index
|
493
|
+
require 'rubygems/indexer'
|
494
|
+
Console.info "Generating rubygems index in #{dist_dir}"
|
495
|
+
FileUtils.rm_rf dist_dir
|
496
|
+
FileUtils.mkdir_p dist_dir
|
497
|
+
FileUtils.cp_r gems_dir, dist_dir
|
498
|
+
indexer = ::Gem::Indexer.new dist_dir
|
499
|
+
indexer.generate_index
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|