nexus 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/commands/abstract_command.rb +59 -33
- data/lib/commands/nexus.rb +81 -4
- data/lib/nexus/cipher.rb +52 -0
- data/lib/nexus/config.rb +164 -77
- data/lib/nexus/config_file.rb +113 -0
- data/lib/nexus/version.rb +1 -1
- data/test/abstract_command_test.rb +49 -49
- data/test/cipher_test.rb +68 -0
- data/test/config_test.rb +92 -139
- data/test/configfile_test.rb +78 -0
- data/test/nexus_command_test.rb +271 -1
- metadata +20 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e11879ab0b9f82568f91c8ce11f044da3956b7ff
|
4
|
+
data.tar.gz: bc935338806a858d17b87fc5834984bb10d2e64e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd857dc03ad489c39efd1a622868f9600398fc4f2721e581f89448d59b04023dbdf8fe3318894209f0e4a619a51fe960ff1e4923f38183056c9406f3abd2cf63
|
7
|
+
data.tar.gz: a0d1607d9297577d23af9b649629c460d176ca7e91b9f903b4d66d3b712cd7ec8abcb793ce34f2c7d0cca76ee9b8593b310ab038e56d44a620c4cf894aa63e3b
|
@@ -9,29 +9,29 @@ class Gem::AbstractCommand < Gem::Command
|
|
9
9
|
def initialize( name, summary )
|
10
10
|
super
|
11
11
|
|
12
|
-
add_option( '-
|
13
|
-
|
14
|
-
options[ :
|
12
|
+
add_option( '-r', '--repo KEY',
|
13
|
+
"pick the configuration under that key.\n can be used in conjuction with --clear-repo and the upload itself." ) do |value, options|
|
14
|
+
options[ :nexus_repo ] = value
|
15
15
|
end
|
16
16
|
|
17
|
-
add_option( '
|
18
|
-
'
|
19
|
-
options[ :
|
17
|
+
add_option( '-c', '--clear-repo',
|
18
|
+
'Clears the nexus config for the given repo or the default repo' ) do |value, options|
|
19
|
+
options[ :nexus_clear ] = value
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
options[ :
|
22
|
+
# backward compatibility
|
23
|
+
add_option( '--nexus-clear', 'DEPRECATED' ) do |value, options|
|
24
|
+
options[ :nexus_clear ] = value
|
25
25
|
end
|
26
26
|
|
27
|
-
add_option( '--
|
28
|
-
|
29
|
-
options[ :
|
27
|
+
add_option( '--nexus-config FILE',
|
28
|
+
"File location of nexus config to use.\n default #{Nexus::Config.default_file}" ) do |value, options|
|
29
|
+
options[ :nexus_config ] = File.expand_path( value )
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
def url
|
34
|
-
url = config
|
34
|
+
url = config.url
|
35
35
|
# no leading slash
|
36
36
|
url.sub!(/\/$/,'') if url
|
37
37
|
url
|
@@ -43,7 +43,7 @@ class Gem::AbstractCommand < Gem::Command
|
|
43
43
|
url = ask("URL: ")
|
44
44
|
|
45
45
|
if URI.parse( "#{url}" ).host != nil
|
46
|
-
config
|
46
|
+
config.url = url
|
47
47
|
|
48
48
|
say 'The Nexus URL has been stored in ~/.gem/nexus'
|
49
49
|
else
|
@@ -52,9 +52,27 @@ class Gem::AbstractCommand < Gem::Command
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def setup
|
55
|
-
|
55
|
+
prompt_encryption if config.encrypted?
|
56
|
+
configure_url if config.url.nil? || options[ :nexus_clear ]
|
56
57
|
use_proxy!( url ) if http_proxy( url )
|
57
|
-
|
58
|
+
if( authorization.nil? ||
|
59
|
+
config.always_prompt? ||
|
60
|
+
options[:nexus_clear] )
|
61
|
+
sign_in
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def prompt_encryption
|
66
|
+
password = ask_for_password( "Enter your Nexus encryption credentials (no prompt)" )
|
67
|
+
|
68
|
+
# recreate config with password
|
69
|
+
config.password = password
|
70
|
+
|
71
|
+
# if options[ :nexus_encrypt ] && !config.encrypted?
|
72
|
+
# config.encrypt_credentials
|
73
|
+
# elsif options[ :nexus_encrypt ] == false && config.encrypted?
|
74
|
+
# config.decrypt_credentials
|
75
|
+
# end
|
58
76
|
end
|
59
77
|
|
60
78
|
def sign_in
|
@@ -64,29 +82,32 @@ class Gem::AbstractCommand < Gem::Command
|
|
64
82
|
|
65
83
|
# mimic strict_encode64 which is not there on ruby1.8
|
66
84
|
token = "#{username}:#{password}"
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
config
|
85
|
+
auth = "Basic #{Base64.encode64(token).gsub(/\s+/, '')}"
|
86
|
+
@authorization = token == ':' ? nil : auth
|
87
|
+
|
88
|
+
unless config.always_prompt?
|
89
|
+
config.authorization = @authorization
|
90
|
+
if @authorization
|
91
|
+
say "Your Nexus credentials has been stored in #{config}"
|
92
|
+
else
|
93
|
+
say "Your Nexus credentials has been deleted from #{config}"
|
94
|
+
end
|
72
95
|
end
|
73
|
-
|
74
|
-
say "Your Nexus credentials has been stored in ~/.gem/nexus"
|
75
96
|
end
|
76
97
|
|
77
|
-
def this_config
|
78
|
-
Nexus::Config.new( options[ :
|
79
|
-
options[ :
|
80
|
-
options[ :nexus_secrets ] )
|
98
|
+
def this_config( pass = nil )
|
99
|
+
Nexus::Config.new( options[ :nexus_config ],
|
100
|
+
options[ :nexus_repo ] )
|
81
101
|
end
|
82
102
|
private :this_config
|
83
103
|
|
84
|
-
def config
|
104
|
+
def config( pass = nil )
|
105
|
+
@config = this_config( pass ) if pass
|
85
106
|
@config ||= this_config
|
86
107
|
end
|
87
108
|
|
88
109
|
def authorization
|
89
|
-
config
|
110
|
+
@authorization || config.authorization
|
90
111
|
end
|
91
112
|
|
92
113
|
def make_request(method, path)
|
@@ -125,13 +146,15 @@ class Gem::AbstractCommand < Gem::Command
|
|
125
146
|
|
126
147
|
if Gem.configuration.verbose.to_s.to_i > 0
|
127
148
|
warn "#{request.method} #{url.to_s}"
|
128
|
-
if authorization
|
149
|
+
if config.authorization
|
129
150
|
warn 'use authorization'
|
130
151
|
else
|
131
152
|
warn 'no authorization'
|
132
153
|
end
|
133
|
-
|
134
|
-
|
154
|
+
|
155
|
+
if http.proxy_address
|
156
|
+
warn "use proxy at #{http.proxy_address}:#{http.proxy_port}"
|
157
|
+
end
|
135
158
|
end
|
136
159
|
|
137
160
|
http.request(request)
|
@@ -139,7 +162,10 @@ class Gem::AbstractCommand < Gem::Command
|
|
139
162
|
|
140
163
|
def use_proxy!( url )
|
141
164
|
proxy_uri = http_proxy( url )
|
142
|
-
@proxy_class = Net::HTTP::Proxy(proxy_uri.host,
|
165
|
+
@proxy_class = Net::HTTP::Proxy( proxy_uri.host,
|
166
|
+
proxy_uri.port,
|
167
|
+
proxy_uri.user,
|
168
|
+
proxy_uri.password )
|
143
169
|
end
|
144
170
|
|
145
171
|
def proxy_class
|
data/lib/commands/nexus.rb
CHANGED
@@ -5,23 +5,100 @@ class Gem::Commands::NexusCommand < Gem::AbstractCommand
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def arguments
|
8
|
-
"GEM built gem to upload"
|
8
|
+
"GEM built gem to upload. some options do not require it."
|
9
9
|
end
|
10
10
|
|
11
11
|
def usage
|
12
12
|
"#{program_name} GEM"
|
13
13
|
end
|
14
14
|
|
15
|
+
def list_repos
|
16
|
+
puts
|
17
|
+
config.repos.each do |k,v|
|
18
|
+
puts "#{k}: #{v}"
|
19
|
+
end
|
20
|
+
puts
|
21
|
+
end
|
22
|
+
private :list_repos
|
23
|
+
|
15
24
|
def initialize
|
16
25
|
super 'nexus', description
|
17
26
|
add_proxy_option
|
27
|
+
|
28
|
+
add_option( '--all-repos',
|
29
|
+
'list all configured repos with their respective urls.' ) do |value, options|
|
30
|
+
options[ :nexus_all_repos ] = value
|
31
|
+
end
|
32
|
+
|
33
|
+
add_option( '--clear-all',
|
34
|
+
'clears all credentials' ) do |value, options|
|
35
|
+
options[ :nexus_clear_all ] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
add_option( '--secrets FILE',
|
40
|
+
'move the credentials to the given secrets file.' ) do |value, options|
|
41
|
+
options[ :nexus_secrets ] = File.expand_path( value )
|
42
|
+
end
|
43
|
+
|
44
|
+
add_option( '--no-secrets',
|
45
|
+
'move the credentials to the configuration file and delete the secrets file.' ) do |value, options|
|
46
|
+
options[ :nexus_secrets ] = false
|
47
|
+
end
|
48
|
+
|
49
|
+
# backward compatibility
|
50
|
+
add_option( '--password', 'DEPRECATED' ) do |value, options|
|
51
|
+
options[ :nexus_prompt_all ] = value
|
52
|
+
end
|
53
|
+
|
54
|
+
add_option( '--[no-]prompt',
|
55
|
+
'always prompt for the credentials.' ) do |value, options|
|
56
|
+
options[ :nexus_prompt_all ] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
add_option( '--[no-]encrypt',
|
60
|
+
'encrypt/decrypt the credentials with a master password.' ) do |value, options|
|
61
|
+
options[ :nexus_encrypt ] = value
|
62
|
+
end
|
63
|
+
|
18
64
|
end
|
19
65
|
|
20
66
|
def execute
|
21
67
|
name = get_one_gem_name rescue nil
|
22
|
-
|
23
|
-
|
24
|
-
|
68
|
+
if( name && ( options[ :nexus_all_repos ] != nil ||
|
69
|
+
options[ :nexus_clear_all ] != nil ||
|
70
|
+
options[ :nexus_prompt_all ] != nil ||
|
71
|
+
options[ :nexus_encrypt ] != nil ||
|
72
|
+
options[ :nexus_secrets ] != nil ) )
|
73
|
+
warn "given gemfile #{name} get ignored due to the options used"
|
74
|
+
end
|
75
|
+
|
76
|
+
if options[ :nexus_all_repos ]
|
77
|
+
list_repos
|
78
|
+
elsif options[ :nexus_prompt_all ]
|
79
|
+
if ask( "setup nexus to always prompt username/passwords and delete all current credentials ? (y/N)" ) == 'y'
|
80
|
+
config.always_prompt
|
81
|
+
end
|
82
|
+
elsif options[ :nexus_prompt_all ] == false
|
83
|
+
say( "setup nexus to store username/passwords" )
|
84
|
+
config.clear_always_prompt
|
85
|
+
elsif options[ :nexus_clear_all ]
|
86
|
+
if ask( "delete all current credentials ? (y/N)" ) == 'y'
|
87
|
+
config.clear_credentials
|
88
|
+
end
|
89
|
+
elsif options[ :nexus_encrypt ]
|
90
|
+
prompt_encryption
|
91
|
+
config.encrypt_credentials
|
92
|
+
elsif options[ :nexus_encrypt ] == false
|
93
|
+
prompt_encryption
|
94
|
+
config.decrypt_credentials
|
95
|
+
elsif options[ :nexus_secrets ] == false
|
96
|
+
config.new_secrets( nil )
|
97
|
+
elsif options[ :nexus_secrets ]
|
98
|
+
config.new_secrets( options[ :nexus_secrets ] )
|
99
|
+
else
|
100
|
+
setup
|
101
|
+
# if there is no gemname and no options which then fail with send_gem
|
25
102
|
send_gem
|
26
103
|
end
|
27
104
|
end
|
data/lib/nexus/cipher.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Nexus
|
5
|
+
class Cipher
|
6
|
+
|
7
|
+
def initialize( pass, token = nil )
|
8
|
+
@token = Base64.strict_decode64( token ) if token
|
9
|
+
@token ||= OpenSSL::Random.random_bytes( 32 )
|
10
|
+
|
11
|
+
iter = 20000
|
12
|
+
key_len = 32
|
13
|
+
@key = OpenSSL::PKCS5.pbkdf2_hmac_sha1( pass,
|
14
|
+
@token,
|
15
|
+
iter,
|
16
|
+
key_len )
|
17
|
+
end
|
18
|
+
|
19
|
+
def cipher
|
20
|
+
@c ||= OpenSSL::Cipher::AES.new( 256, :CBC )
|
21
|
+
end
|
22
|
+
private :cipher
|
23
|
+
|
24
|
+
def token
|
25
|
+
Base64.strict_encode64( @token )
|
26
|
+
end
|
27
|
+
|
28
|
+
def iv
|
29
|
+
Base64.strict_encode64( @iv ) if @iv
|
30
|
+
end
|
31
|
+
|
32
|
+
def iv=( iv )
|
33
|
+
@iv = Base64.strict_decode64( iv )
|
34
|
+
end
|
35
|
+
|
36
|
+
def encrypt( data )
|
37
|
+
c = cipher
|
38
|
+
c.encrypt
|
39
|
+
c.key = @key
|
40
|
+
@iv = c.random_iv
|
41
|
+
Base64.strict_encode64( c.update( data ) + c.final )
|
42
|
+
end
|
43
|
+
|
44
|
+
def decrypt( data )
|
45
|
+
c = cipher
|
46
|
+
c.decrypt
|
47
|
+
c.key = @key
|
48
|
+
c.iv = @iv
|
49
|
+
c.update( Base64.strict_decode64( data ) ) + c.final
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/nexus/config.rb
CHANGED
@@ -1,110 +1,197 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'base64'
|
1
|
+
require 'nexus/cipher'
|
2
|
+
require 'nexus/config_file'
|
4
3
|
|
5
4
|
module Nexus
|
6
5
|
class Config
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@file = file
|
12
|
-
|
13
|
-
if file && ::File.exists?( file )
|
14
|
-
@all = YAML.load( ::File.read( file ) )
|
15
|
-
end
|
16
|
-
elsif file
|
17
|
-
@file = file.instance_variable_get( '@file'.to_sym )
|
18
|
-
@all = file.instance_variable_get( '@all'.to_sym )
|
19
|
-
end
|
20
|
-
@all ||= {}
|
7
|
+
def self.default_file
|
8
|
+
File.join( Gem.user_home, '.gem', 'nexus' )
|
9
|
+
end
|
21
10
|
|
22
|
-
|
23
|
-
|
24
|
-
|
11
|
+
def initialize( file = nil, repo = nil )
|
12
|
+
@repo = repo
|
13
|
+
@conf = ConfigFile.new( file || self.class.default_file )
|
14
|
+
@secr = ConfigFile.new( @conf[ :secrets ] ) if @conf.key? :secrets
|
15
|
+
end
|
25
16
|
|
26
|
-
|
27
|
-
@data.key? key
|
28
|
-
end
|
17
|
+
private
|
29
18
|
|
30
|
-
|
31
|
-
|
19
|
+
def encrypt_or_decrypt_credentials
|
20
|
+
map = config.all
|
21
|
+
yield map if map[ :authorization ]
|
22
|
+
map.each do |k, v|
|
23
|
+
yield v if v.is_a?( Hash ) && v[ :authorization ]
|
32
24
|
end
|
25
|
+
end
|
33
26
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
def store
|
43
|
-
if @file
|
44
|
-
dirname = ::File.dirname( @file )
|
45
|
-
Dir.mkdir( dirname ) unless ::File.exists?( dirname )
|
46
|
-
new = !::File.exists?( @file )
|
47
|
-
|
48
|
-
::File.open( @file, 'w') do |f|
|
49
|
-
f.write @all.to_yaml
|
50
|
-
end
|
51
|
-
if new
|
52
|
-
::File.chmod( 0100600, @file ) rescue nil
|
53
|
-
end
|
54
|
-
true
|
55
|
-
else
|
56
|
-
false
|
27
|
+
def move_credentials( from, to )
|
28
|
+
keys = [ :secrets, :token, :iv, :authorization ]
|
29
|
+
([ nil ] + from.repos ).each do |repo|
|
30
|
+
keys.each do |k|
|
31
|
+
to[ k, repo ] = from[ k, repo ]
|
32
|
+
from[ k, repo ] = nil
|
57
33
|
end
|
58
34
|
end
|
59
35
|
end
|
60
36
|
|
61
|
-
def
|
62
|
-
|
37
|
+
def config
|
38
|
+
@map ||= @secr ? @secr : @conf
|
39
|
+
end
|
40
|
+
|
41
|
+
def key?( key )
|
42
|
+
config.key?( key, @repo )
|
43
|
+
end
|
44
|
+
|
45
|
+
def []( key )
|
46
|
+
config[ key, @repo ]
|
47
|
+
end
|
48
|
+
|
49
|
+
def []=( key, value )
|
50
|
+
config[ key, @repo ] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
public
|
54
|
+
|
55
|
+
def encrypted?
|
56
|
+
config.key?( :token )
|
57
|
+
end
|
58
|
+
|
59
|
+
def password=( pass )
|
60
|
+
@cipher = Cipher.new( pass, config[ :token ] )
|
61
|
+
end
|
62
|
+
|
63
|
+
def always_prompt?
|
64
|
+
@conf[ :always_prompt ]
|
63
65
|
end
|
64
66
|
|
65
|
-
def
|
66
|
-
|
67
|
-
conf
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
def clear_always_prompt
|
68
|
+
@conf.delete( :always_prompt )
|
69
|
+
@conf.store
|
70
|
+
end
|
71
|
+
|
72
|
+
def clear_credentials( store = true )
|
73
|
+
secrets = @conf[ :secrets ]
|
74
|
+
if secrets && !config.key?( :token )
|
75
|
+
FileUtils.rm_f( secrets )
|
76
|
+
@map = @conf
|
77
|
+
else
|
78
|
+
config.delete :iv, :authorization
|
71
79
|
end
|
72
|
-
@conf
|
73
|
-
@
|
80
|
+
@conf.delete( :secrets )
|
81
|
+
@conf.store if store
|
74
82
|
end
|
75
83
|
|
76
|
-
def
|
77
|
-
@conf
|
84
|
+
def always_prompt
|
85
|
+
@conf[ :always_prompt, nil ] = true
|
86
|
+
|
87
|
+
config.delete( :token )
|
88
|
+
|
89
|
+
clear_credentials( false )
|
90
|
+
|
91
|
+
@conf.store
|
78
92
|
end
|
79
93
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
94
|
+
def decrypt_credentials
|
95
|
+
unless encrypted?
|
96
|
+
warn 'not encrypted - nothing to do'
|
97
|
+
return
|
83
98
|
end
|
84
|
-
|
99
|
+
encrypt_or_decrypt_credentials do |c|
|
100
|
+
@cipher.iv = c[ :iv ]
|
101
|
+
c[ :authorization ] = @cipher.decrypt( c[ :authorization ] )
|
102
|
+
c.delete( :iv )
|
103
|
+
end
|
104
|
+
config.all.delete( :token )
|
105
|
+
config.store
|
106
|
+
@cipher = nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def encrypt_credentials
|
110
|
+
if encrypted?
|
111
|
+
warn 'already encrypted - nothing to do'
|
112
|
+
return
|
113
|
+
end
|
114
|
+
encrypt_or_decrypt_credentials do |c|
|
115
|
+
c[ :authorization ] = @cipher.encrypt( c[ :authorization ] )
|
116
|
+
c[ :iv ] = @cipher.iv
|
117
|
+
end
|
118
|
+
config.all[ :token ] = @cipher.token
|
119
|
+
config.store
|
85
120
|
end
|
121
|
+
|
122
|
+
def new_secrets( new )
|
123
|
+
old = @conf.all[ :secrets ]
|
124
|
+
if old and new
|
125
|
+
FileUtils.mv( old, new )
|
126
|
+
end
|
127
|
+
if new
|
128
|
+
@secr = ConfigFile.new( new )
|
129
|
+
end
|
130
|
+
|
131
|
+
if new.nil? && old
|
132
|
+
@conf.merge!( @secr )
|
133
|
+
FileUtils.rm_f( old )
|
134
|
+
@secr = nil
|
135
|
+
end
|
136
|
+
|
137
|
+
if old.nil? and new
|
138
|
+
move_credentials( @conf, @secr )
|
86
139
|
|
87
|
-
|
88
|
-
@secr[ :authorization ] = @conf[ :authorization ]
|
89
|
-
if @secr.store
|
90
|
-
@conf[ :authorization ] = nil
|
91
|
-
@conf.store
|
140
|
+
@secr.store
|
92
141
|
end
|
142
|
+
|
143
|
+
# store the new location
|
144
|
+
@conf[ :secrets, nil ] = new
|
145
|
+
@conf.store
|
93
146
|
end
|
94
|
-
private :copy_authorization
|
95
147
|
|
96
|
-
def
|
97
|
-
|
98
|
-
if
|
99
|
-
|
100
|
-
|
148
|
+
def repos
|
149
|
+
result = @conf.section( :url )
|
150
|
+
if url = result.delete( :url )
|
151
|
+
result[ 'DEFAULT' ] = url
|
152
|
+
end
|
153
|
+
result.keys.each do |key|
|
154
|
+
if key != 'DEFAULT'
|
155
|
+
result[ key ] = result[ key ][ :url ]
|
156
|
+
end
|
101
157
|
end
|
158
|
+
result
|
159
|
+
end
|
102
160
|
|
103
|
-
|
104
|
-
|
105
|
-
|
161
|
+
def authorization
|
162
|
+
auth = self[ :authorization ]
|
163
|
+
if @cipher && auth && self[ :iv ]
|
164
|
+
@cipher.iv = self[ :iv ]
|
165
|
+
@cipher.decrypt( auth )
|
166
|
+
elsif @cipher && auth
|
167
|
+
authorization = auth
|
168
|
+
else
|
169
|
+
auth
|
106
170
|
end
|
107
171
|
end
|
108
172
|
|
173
|
+
def authorization=( auth )
|
174
|
+
if @cipher && auth
|
175
|
+
self[ :authorization ] = @cipher.encrypt( auth )
|
176
|
+
self[ :iv ] = @cipher.iv
|
177
|
+
else
|
178
|
+
self[ :authorization ] = auth
|
179
|
+
end
|
180
|
+
config.store
|
181
|
+
auth
|
182
|
+
end
|
183
|
+
|
184
|
+
def url
|
185
|
+
@conf[ :url, @repo ]
|
186
|
+
end
|
187
|
+
|
188
|
+
def url=( u )
|
189
|
+
@conf[ :url, @repo ] = u
|
190
|
+
@conf.store
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_s
|
194
|
+
config.file
|
195
|
+
end
|
109
196
|
end
|
110
197
|
end
|