nexus 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009, Nick Quaranto
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :default => [:test]
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['test/*_command_test.rb']
9
+ t.verbose = true
10
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'bundler/monkey_patch'
4
+
5
+ load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,21 @@
1
+ require 'bundler/fetcher'
2
+ require 'bundler/rubygems_integration'
3
+ require 'bundler/rubygems_mirror'
4
+ module Bundler
5
+ class RubygemsIntegration
6
+ def download_gem(spec, uri, path)
7
+ uri = RubygemsMirror.to_uri(uri)
8
+ Gem::RemoteFetcher.fetcher.download(spec, uri, path)
9
+ end
10
+ end
11
+ end
12
+ module Bundler
13
+ # Handles all the fetching with the rubygems server
14
+ class Fetcher
15
+ def initialize(remote_uri)
16
+ @remote_uri = RubygemsMirror.to_uri(remote_uri)
17
+ @has_api = true # will be set to false if the rubygems index is ever fetched
18
+ @@connection ||= Net::HTTP::Persistent.new nil, :ENV
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ module Bundler
2
+ class RubygemsMirror
3
+
4
+ private
5
+
6
+ def self.mirrors
7
+ @mirrors ||= Bundler.settings.all.inject({}) do |h, k|
8
+ if k =~ /^mirror./
9
+ uri = add_slash(k.sub(/^mirror./, ''))
10
+ h[uri] = URI.parse(Bundler.settings[k])
11
+ end
12
+ h
13
+ end
14
+ end
15
+
16
+ def self.add_slash(uri)
17
+ uri = uri.to_s
18
+ uri =~ /\/$/ ? uri : uri + '/'
19
+ end
20
+
21
+ public
22
+
23
+ def self.to_uri(uri)
24
+ mirrors[add_slash(uri)] || uri
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,124 @@
1
+ require 'rubygems/local_remote_options'
2
+ begin
3
+ require 'always_verify_ssl_certificates'
4
+ rescue LoadError
5
+ warn 'skip "always_verify_ssl_certificates"'
6
+ end
7
+ require 'base64'
8
+
9
+ class Gem::AbstractCommand < Gem::Command
10
+ include Gem::LocalRemoteOptions
11
+
12
+ def url
13
+ url = config[:url]
14
+ # no leadng slash
15
+ url.sub!(/\/$/,'') if url
16
+ url
17
+ end
18
+
19
+ def configure_url
20
+ say "Enter the URL of the rubygems repository on a Nexus server"
21
+
22
+ url = ask("URL: ")
23
+
24
+ store_config(:url, url)
25
+
26
+ say "The Nexus URL has been stored in ~/.gem/nexus"
27
+ end
28
+
29
+ def setup
30
+ use_proxy! if http_proxy
31
+ configure_url unless url
32
+ sign_in unless authorization
33
+ end
34
+
35
+ def sign_in
36
+ say "Enter your Nexus credentials"
37
+ username = ask("Username: ")
38
+ password = ask_for_password("Password: ")
39
+
40
+ store_config(:authorization,
41
+ "Basic #{Base64.encode64(username + ':' + password).strip}")
42
+
43
+ say "Your Nexus credentials has been stored in ~/.gem/nexus"
44
+ end
45
+
46
+ def config_path
47
+ File.join(Gem.user_home, '.gem', 'nexus')
48
+ end
49
+
50
+ def config
51
+ @config ||= Gem.configuration.load_file(config_path)
52
+ end
53
+
54
+ def authorization
55
+ config[:authorization]
56
+ end
57
+
58
+ def store_config(key, value)
59
+ config.merge!(key => value)
60
+ dirname = File.dirname(config_path)
61
+ Dir.mkdir(dirname) unless File.exists?(dirname)
62
+
63
+ File.open(config_path, 'w') do |f|
64
+ f.write config.to_yaml
65
+ end
66
+ end
67
+
68
+ def make_request(method, path)
69
+ require 'net/http'
70
+ require 'net/https'
71
+
72
+ url = URI.parse("#{self.url}/#{path}")
73
+
74
+ http = proxy_class.new(url.host, url.port)
75
+
76
+ if url.scheme == 'https'
77
+ http.use_ssl = true
78
+ end
79
+
80
+ request_method =
81
+ case method
82
+ when :get
83
+ proxy_class::Get
84
+ when :post
85
+ proxy_class::Post
86
+ when :put
87
+ proxy_class::Put
88
+ when :delete
89
+ proxy_class::Delete
90
+ else
91
+ raise ArgumentError
92
+ end
93
+
94
+ request = request_method.new(url.path)
95
+ request.add_field "User-Agent", "Nexus Gem Command"
96
+
97
+ yield request if block_given?
98
+ http.request(request)
99
+ end
100
+
101
+ def use_proxy!
102
+ proxy_uri = http_proxy
103
+ @proxy_class = Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
104
+ end
105
+
106
+ def proxy_class
107
+ @proxy_class || Net::HTTP
108
+ end
109
+
110
+ # @return [URI, nil] the HTTP-proxy as a URI if set; +nil+ otherwise
111
+ def http_proxy
112
+ proxy = Gem.configuration[:http_proxy] || ENV['http_proxy'] || ENV['HTTP_PROXY']
113
+ return nil if proxy.nil? || proxy == :no_proxy
114
+ URI.parse(proxy)
115
+ end
116
+
117
+ def ask_for_password(message)
118
+ system "stty -echo"
119
+ password = ask(message)
120
+ system "stty echo"
121
+ ui.say("\n")
122
+ password
123
+ end
124
+ end
@@ -0,0 +1,48 @@
1
+ class Gem::Commands::NexusCommand < Gem::AbstractCommand
2
+
3
+ def description
4
+ 'Push a gem up to Nexus server'
5
+ end
6
+
7
+ def arguments
8
+ "GEM built gem to push up"
9
+ end
10
+
11
+ def usage
12
+ "#{program_name} GEM"
13
+ end
14
+
15
+ def initialize
16
+ super 'push', description
17
+ add_proxy_option
18
+ end
19
+
20
+ def execute
21
+ setup
22
+ send_gem
23
+ end
24
+
25
+ def send_gem
26
+ say "Pushing gem to Nexus..."
27
+
28
+ path = get_one_gem_name
29
+
30
+ response = make_request(:post, "gems/#{File.basename(path)}") do |request|
31
+ request.body = Gem.read_binary(path)
32
+ request.add_field("Content-Length", request.body.size)
33
+ request.add_field("Content-Type", "application/octet-stream")
34
+ request.add_field("Authorization", authorization.strip)
35
+ end
36
+
37
+ case response.code
38
+ when "401"
39
+ say "Unauthorized"
40
+ when "400"
41
+ say "something went wrong - maybe (re)deployment is not allowed"
42
+ when "500"
43
+ say "something went wrong"
44
+ else
45
+ say response.message
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems/command_manager'
2
+ require 'commands/abstract_command'
3
+
4
+ require "commands/nexus"
5
+ Gem::CommandManager.instance.register_command :nexus
6
+
@@ -0,0 +1,134 @@
1
+ require 'command_helper'
2
+
3
+ class Gem::Commands::FakeCommand < Gem::AbstractCommand
4
+ def description
5
+ 'fake command'
6
+ end
7
+
8
+ def initialize
9
+ super 'fake', description
10
+ end
11
+
12
+ def execute
13
+ end
14
+ end
15
+
16
+ class AbstractCommandTest < CommandTest
17
+ context "with an fake command" do
18
+ setup do
19
+ @command = Gem::Commands::FakeCommand.new
20
+ stub(@command).say
21
+ ENV['http_proxy'] = nil
22
+ ENV['HTTP_PROXY'] = nil
23
+ end
24
+
25
+ context "parsing the proxy" do
26
+ should "return nil if no proxy is set" do
27
+ stub_config(:http_proxy => nil)
28
+ assert_equal nil, @command.http_proxy
29
+ end
30
+
31
+ should "return nil if the proxy is set to :no_proxy" do
32
+ stub_config(:http_proxy => :no_proxy)
33
+ assert_equal nil, @command.http_proxy
34
+ end
35
+
36
+ should "return a proxy as a URI if set" do
37
+ stub_config(:http_proxy => 'http://proxy.example.org:9192')
38
+ assert_equal 'proxy.example.org', @command.http_proxy.host
39
+ assert_equal 9192, @command.http_proxy.port
40
+ end
41
+
42
+ should "return a proxy as a URI if set by environment variable" do
43
+ ENV['http_proxy'] = "http://jack:duck@192.168.1.100:9092"
44
+ assert_equal "192.168.1.100", @command.http_proxy.host
45
+ assert_equal 9092, @command.http_proxy.port
46
+ assert_equal "jack", @command.http_proxy.user
47
+ assert_equal "duck", @command.http_proxy.password
48
+ end
49
+ end
50
+
51
+ should "sign in if authorization and no nexus url" do
52
+ stub(@command).authorization { nil }
53
+ stub(@command).url { nil }
54
+ stub(@command).sign_in
55
+ stub(@command).configure_url
56
+ @command.setup
57
+ assert_received(@command) { |command| command.configure_url }
58
+ assert_received(@command) { |command| command.sign_in }
59
+ end
60
+
61
+ should "not sign in nor configure if authorizaton and url exists" do
62
+ stub(@command).authorization { "1234567890" }
63
+ stub(@command).url { "abc" }
64
+ stub(@command).sign_in
65
+ stub(@command).configure_url
66
+ @command.setup
67
+ assert_received(@command) { |command| command.configure_url.never }
68
+ assert_received(@command) { |command| command.sign_in.never }
69
+ end
70
+
71
+ context "using the proxy" do
72
+ setup do
73
+ stub_config(:http_proxy => "http://gilbert:sekret@proxy.example.org:8081")
74
+ @proxy_class = Object.new
75
+ mock(Net::HTTP).Proxy('proxy.example.org', 8081, 'gilbert', 'sekret') { @proxy_class }
76
+ @command.use_proxy!
77
+ end
78
+
79
+ should "replace Net::HTTP with a proxy version" do
80
+ assert_equal @proxy_class, @command.proxy_class
81
+ end
82
+ end
83
+
84
+ context "signing in" do
85
+ setup do
86
+ @username = "username"
87
+ @password = "password"
88
+ @key = "key"
89
+
90
+ stub(@command).say
91
+ stub(@command).ask { @username }
92
+ stub(@command).ask_for_password { @password }
93
+ stub(@command).store_config { {:authorization => @key} }
94
+ end
95
+
96
+ should "ask for username and password" do
97
+ @command.sign_in
98
+ assert_received(@command) { |command| command.ask("Username: ") }
99
+ assert_received(@command) { |command| command.ask_for_password("Password: ") }
100
+ assert_received(@command) { |command| command.store_config(:authorization, "Basic dXNlcm5hbWU6cGFzc3dvcmQ=") }
101
+ end
102
+
103
+ should "say that we signed in" do
104
+ @command.sign_in
105
+ assert_received(@command) { |command| command.say("Enter your Nexus credentials") }
106
+ assert_received(@command) { |command| command.say("Your Nexus credentials has been stored in ~/.gem/nexus") }
107
+ assert_received(@command) { |command| command.store_config(:authorization, "Basic dXNlcm5hbWU6cGFzc3dvcmQ=") }
108
+ end
109
+ end
110
+
111
+ context "configure nexus url" do
112
+ setup do
113
+ @url = "url"
114
+
115
+ stub(@command).say
116
+ stub(@command).ask { @url }
117
+ stub(@command).store_config { {:url => @url} }
118
+ end
119
+
120
+ should "ask for nexus url" do
121
+ @command.configure_url
122
+ assert_received(@command) { |command| command.ask("URL: ") }
123
+ assert_received(@command) { |command| command.store_config(:url, "url") }
124
+ end
125
+
126
+ should "say that we configured the url" do
127
+ @command.configure_url
128
+ assert_received(@command) { |command| command.say("Enter the URL of the rubygems repository on a Nexus server") }
129
+ assert_received(@command) { |command| command.say("The Nexus URL has been stored in ~/.gem/nexus") }
130
+ assert_received(@command) { |command| command.store_config(:url, "url") }
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ require 'shoulda'
5
+ require 'active_support'
6
+ require 'active_support/test_case'
7
+ require 'webmock'
8
+ require 'rr'
9
+
10
+ begin
11
+ require 'redgreen'
12
+ rescue LoadError
13
+ end
14
+
15
+ WebMock.disable_net_connect!
16
+
17
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), ".."))
18
+
19
+ require "rubygems_plugin"
20
+
21
+ class CommandTest < ActiveSupport::TestCase
22
+ include RR::Adapters::TestUnit unless include?(RR::Adapters::TestUnit)
23
+ include WebMock::API
24
+
25
+ def teardown
26
+ WebMock.reset!
27
+ end
28
+ end
29
+
30
+ def stub_config(config)
31
+ # file = Gem::ConfigFile.new({})
32
+ #xs config.each { |key, value| file[key] = value }
33
+ # stub(:config).configuration { config }
34
+ end
35
+
36
+ def assert_said(command, what)
37
+ assert_received(command) do |command|
38
+ command.say(what)
39
+ end
40
+ end
41
+
42
+ def assert_never_said(command, what)
43
+ assert_received(command) do |command|
44
+ command.say(what).never
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ require 'command_helper'
2
+
3
+ class NexusCommandTest < CommandTest
4
+ context "pushing" do
5
+ setup do
6
+ @command = Gem::Commands::NexusCommand.new
7
+ stub(@command).say
8
+ end
9
+
10
+ should "setup and send the gem" do
11
+ mock(@command).setup
12
+ mock(@command).send_gem
13
+ @command.execute
14
+ assert_received(@command) { |command| command.setup }
15
+ assert_received(@command) { |command| command.send_gem }
16
+ end
17
+
18
+ should "raise an error with no arguments" do
19
+ assert_raise Gem::CommandLineError do
20
+ @command.send_gem
21
+ end
22
+ end
23
+
24
+ context "pushing a gem" do
25
+ setup do
26
+
27
+ @gem_path = "path/to/foo-0.0.0.gem"
28
+ baseurl = 'http://localhost:8081/nexus/content/repositories/localgems'
29
+ @url = baseurl + @gem_path.sub(/.*\//, '/gems/')
30
+ @gem_binary = StringIO.new("gem")
31
+
32
+ stub(@command).say
33
+ stub(@command).options { {:args => [@gem_path]} }
34
+ stub(Gem).read_binary(@gem_path) { @gem_binary }
35
+ stub(@command).config { { :authorization => "key", :url => baseurl } }
36
+ stub_request(:post, @url).to_return(:status => 201)
37
+
38
+ @command.send_gem
39
+ end
40
+
41
+ should "say push was successful" do
42
+ assert_received(@command) { |command| command.say("Pushing gem to Nexus...") }
43
+ # due to webmock there is no status message
44
+ assert_received(@command) { |command| command.say("") }
45
+ end
46
+
47
+ should "post to api" do
48
+ # webmock doesn't pass body params on correctly :[
49
+ assert_requested(:post, @url,
50
+ :times => 1)
51
+ assert_requested(:post, @url,
52
+ :headers => {
53
+ 'Authorization' => 'key',
54
+ 'Content-Type' => 'application/octet-stream',
55
+ 'User-Agent'=>'Nexus Gem Command'
56
+ })
57
+ #assert_requested(:post, @url,
58
+ # :headers => {'Content-Length' => @gem_binary.size.to_s})
59
+ end
60
+ end
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nexus
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Nick Quaranto
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-08-17 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: always_verify_ssl_certificates
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 0.3.0
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - "="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.9.2.2
35
+ type: :development
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: shoulda
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.1.1
46
+ type: :development
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: webmock
50
+ prerelease: false
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.8.8
57
+ type: :development
58
+ version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: rr
61
+ prerelease: false
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :development
69
+ version_requirements: *id005
70
+ description: Adds a command to RubyGems for uploading gems to a nexus server.
71
+ email:
72
+ - nick@quaran.to
73
+ executables:
74
+ - nbundle
75
+ extensions: []
76
+
77
+ extra_rdoc_files: []
78
+
79
+ files:
80
+ - MIT-LICENSE
81
+ - Rakefile
82
+ - bin/nbundle
83
+ - lib/rubygems_plugin.rb
84
+ - lib/commands/nexus.rb
85
+ - lib/commands/abstract_command.rb
86
+ - lib/bundler/monkey_patch.rb
87
+ - lib/bundler/rubygems_mirror.rb
88
+ - test/abstract_command_test.rb
89
+ - test/command_helper.rb
90
+ - test/nexus_command_test.rb
91
+ homepage: https://github.com/sonatype/nexus-ruby-support/tree/master/nexus-gem
92
+ licenses: []
93
+
94
+ post_install_message: |+
95
+
96
+ ========================================================================
97
+
98
+ Thanks for installing Nexus gem! You can now run:
99
+
100
+ gem nexus publish your gems onto Nexus server
101
+
102
+ nbundle a bundler fork with mirror support.
103
+ just add a mirror with:
104
+
105
+ bundle config mirror.http://rubygems.org http://localhost:8081/nexus/content/repositories/rubygems.org
106
+
107
+ and use 'nbundle' instead of 'bundle'
108
+
109
+ ========================================================================
110
+
111
+ rdoc_options: []
112
+
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: "0"
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: "0"
127
+ requirements: []
128
+
129
+ rubyforge_project:
130
+ rubygems_version: 1.8.24
131
+ signing_key:
132
+ specification_version: 3
133
+ summary: Commands to interact with nexus server
134
+ test_files: []
135
+