gemcutter 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +8 -14
- data/lib/commands/abstract_command.rb +66 -0
- data/lib/commands/migrate.rb +99 -0
- data/lib/commands/push.rb +11 -58
- data/lib/commands/tumble.rb +3 -3
- data/lib/rubygems_plugin.rb +14 -5
- data/test/command_helper.rb +19 -0
- data/test/unit/commands/abstract_command_test.rb +113 -0
- data/test/unit/commands/migrate_command_test.rb +141 -0
- data/test/unit/commands/push_command_test.rb +41 -0
- data/test/unit/commands/tumble_command_test.rb +58 -0
- metadata +30 -10
data/README.textile
CHANGED
@@ -23,27 +23,21 @@ No. In fact it will make their lives better: it will be easier to get to project
|
|
23
23
|
GitHub's gem system is great, but it's not the canonical source by default. The nice part is that it takes the approval process out, but the namespacing on gems makes it hard for those new to the community to determine what is the right copy to use. Also, the goals here are much different and the site as a whole can be improved over time by the community.
|
24
24
|
|
25
25
|
*How can I gain access of my gem that was available through RubyForge?*<br />
|
26
|
-
The
|
26
|
+
The full process is coming soon!
|
27
27
|
|
28
28
|
*How can I help?*<br />
|
29
29
|
Fork away, and read the next section! Feel free to bug qrush via a message or on FreeNode if you're interested.
|
30
30
|
|
31
|
-
h2.
|
31
|
+
h2. Contributions
|
32
32
|
|
33
|
-
|
33
|
+
Gemcutter has a 'commit-bit' policy, much like the Rubinius project. Submit a patch that is accepted, and you'll get full commit access to the project. Please check the wiki for more information.
|
34
34
|
|
35
|
-
|
36
|
-
* Releasing/updating gems via @gem push@
|
37
|
-
* Project pages
|
38
|
-
* Authentication/accounts
|
39
|
-
* Gem server up and running
|
35
|
+
If you're looking for things to hack on, please check GitHub Issues. If you've found bugs or have feature ideas don't be afraid to pipe up and ask the mailing list about them.
|
40
36
|
|
41
|
-
|
37
|
+
h2. Testing
|
42
38
|
|
43
|
-
|
44
|
-
* Fully fledged API documentation
|
45
|
-
* Search via the API and site
|
39
|
+
Make sure you run @rake gemcutter:index:create@ before running the test suite, otherwise it might complain. Better solutions to this are more than welcome.
|
46
40
|
|
47
|
-
h2.
|
41
|
+
h2. License
|
48
42
|
|
49
|
-
|
43
|
+
Gemcutter uses the MIT license. Please check the LICENSE file for more details.
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class Gem::AbstractCommand < Gem::Command
|
2
|
+
def api_key
|
3
|
+
Gem.configuration[:gemcutter_key]
|
4
|
+
end
|
5
|
+
|
6
|
+
def gemcutter_url
|
7
|
+
if ENV['test']
|
8
|
+
"http://gemcutter.local"
|
9
|
+
else
|
10
|
+
"https://gemcutter.heroku.com"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
use_proxy! if http_proxy
|
16
|
+
sign_in unless api_key
|
17
|
+
end
|
18
|
+
|
19
|
+
def sign_in
|
20
|
+
say "Enter your Gemcutter credentials. Don't have an account yet? Create one at #{URL}/sign_up"
|
21
|
+
|
22
|
+
email = ask("Email: ")
|
23
|
+
password = ask_for_password("Password: ")
|
24
|
+
|
25
|
+
url = URI.parse("#{gemcutter_url}/api_key")
|
26
|
+
|
27
|
+
http = Net::HTTP.new(url.host, url.port)
|
28
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
29
|
+
http.use_ssl = (url.scheme == 'https')
|
30
|
+
request = Net::HTTP::Get.new(url.path)
|
31
|
+
request.basic_auth email, password
|
32
|
+
response = http.request(request)
|
33
|
+
|
34
|
+
case response
|
35
|
+
when Net::HTTPSuccess
|
36
|
+
Gem.configuration[:gemcutter_key] = response.body
|
37
|
+
Gem.configuration.write
|
38
|
+
say "Signed in. Your api key has been stored in ~/.gemrc"
|
39
|
+
else
|
40
|
+
say response.body
|
41
|
+
terminate_interaction
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def use_proxy!
|
46
|
+
proxy_uri = http_proxy
|
47
|
+
@proxy_class = Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
|
48
|
+
end
|
49
|
+
|
50
|
+
def proxy_class
|
51
|
+
@proxy_class || Net::HTTP
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [URI, nil] the HTTP-proxy as a URI if set; +nil+ otherwise
|
55
|
+
def http_proxy
|
56
|
+
proxy = Gem.configuration[:http_proxy]
|
57
|
+
return nil if proxy.nil? || proxy == :no_proxy
|
58
|
+
URI.parse(proxy)
|
59
|
+
end
|
60
|
+
|
61
|
+
def ask_for_password(message)
|
62
|
+
password = ui.ask_for_password(message)
|
63
|
+
ui.say("\n")
|
64
|
+
password
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'net/scp'
|
3
|
+
require 'json'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
class Gem::Commands::MigrateCommand < Gem::AbstractCommand
|
7
|
+
attr_reader :rubygem
|
8
|
+
|
9
|
+
def description
|
10
|
+
'Migrate a gem your own from Rubyforge to Gemcutter.'
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super 'migrate', description
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute
|
18
|
+
setup
|
19
|
+
migrate
|
20
|
+
end
|
21
|
+
|
22
|
+
def migrate
|
23
|
+
find(get_one_gem_name)
|
24
|
+
token = get_token
|
25
|
+
upload_token(token)
|
26
|
+
check_for_approved
|
27
|
+
end
|
28
|
+
|
29
|
+
def find(name)
|
30
|
+
begin
|
31
|
+
data = open("#{gemcutter_url}/gems/#{name}.json")
|
32
|
+
@rubygem = JSON.parse(data.string)
|
33
|
+
rescue OpenURI::HTTPError
|
34
|
+
say "This gem is currently not hosted on Gemcutter."
|
35
|
+
terminate_interaction
|
36
|
+
rescue JSON::ParserError => json_error
|
37
|
+
say "There was a problem parsing the data: #{json_error}"
|
38
|
+
terminate_interaction
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_token
|
43
|
+
say "Starting migration of #{rubygem["name"]} from RubyForge..."
|
44
|
+
|
45
|
+
url = URI.parse("#{gemcutter_url}/gems/#{rubygem["slug"]}/migrate")
|
46
|
+
|
47
|
+
http = proxy_class.new(url.host, url.port)
|
48
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
49
|
+
http.use_ssl = (url.scheme == 'https')
|
50
|
+
request = proxy_class::Post.new(url.path)
|
51
|
+
request.add_field("Content-Length", 0)
|
52
|
+
request.add_field("Authorization", api_key)
|
53
|
+
response = http.request(request)
|
54
|
+
|
55
|
+
case response
|
56
|
+
when Net::HTTPSuccess
|
57
|
+
say "A migration token has been created."
|
58
|
+
response.body
|
59
|
+
else
|
60
|
+
say response.body
|
61
|
+
terminate_interaction
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def upload_token(token)
|
66
|
+
url = "#{self.rubygem['rubyforge_project']}.rubyforge.org"
|
67
|
+
say "Uploading the migration token to #{url}. Please enter your RubyForge login:"
|
68
|
+
login = ask("Login: ")
|
69
|
+
password = ask_for_password("Password: ")
|
70
|
+
|
71
|
+
begin
|
72
|
+
Net::SCP.start(url, login, :password => password) do |scp|
|
73
|
+
temp_token = Tempfile.new("token")
|
74
|
+
temp_token.chmod 0644
|
75
|
+
temp_token.write(token)
|
76
|
+
temp_token.close
|
77
|
+
scp.upload! temp_token.path, "/var/www/gforge-projects/#{rubygem['rubyforge_project']}/migrate-#{rubygem['name']}.html"
|
78
|
+
end
|
79
|
+
say "Successfully uploaded your token."
|
80
|
+
rescue Exception => e
|
81
|
+
say "There was a problem uploading your token: #{e}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def check_for_approved
|
86
|
+
say "Asking Gemcutter to verify the upload..."
|
87
|
+
url = URI.parse("#{gemcutter_url}/gems/#{rubygem["slug"]}/migrate")
|
88
|
+
|
89
|
+
http = proxy_class.new(url.host, url.port)
|
90
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
91
|
+
http.use_ssl = (url.scheme == 'https')
|
92
|
+
request = proxy_class::Put.new(url.path)
|
93
|
+
request.add_field("Content-Length", 0)
|
94
|
+
request.add_field("Authorization", api_key)
|
95
|
+
response = http.request(request)
|
96
|
+
|
97
|
+
say response.body
|
98
|
+
end
|
99
|
+
end
|
data/lib/commands/push.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
require 'net/https'
|
3
|
+
require 'rubygems/local_remote_options'
|
4
|
+
|
5
|
+
class Gem::Commands::PushCommand < Gem::AbstractCommand
|
6
|
+
|
7
|
+
include Gem::LocalRemoteOptions
|
3
8
|
|
4
|
-
class Gem::Commands::PushCommand < Gem::Command
|
5
9
|
def description
|
6
10
|
'Push a gem up to Gemcutter'
|
7
11
|
end
|
@@ -11,46 +15,29 @@ class Gem::Commands::PushCommand < Gem::Command
|
|
11
15
|
end
|
12
16
|
|
13
17
|
def usage
|
14
|
-
"#{
|
18
|
+
"#{program_name} GEM"
|
15
19
|
end
|
16
20
|
|
17
21
|
def initialize
|
18
22
|
super 'push', description
|
23
|
+
add_proxy_option
|
19
24
|
end
|
20
25
|
|
21
26
|
def execute
|
22
|
-
|
27
|
+
setup
|
23
28
|
send_gem
|
24
29
|
end
|
25
30
|
|
26
|
-
def ask_for_password(message)
|
27
|
-
password = ui.ask_for_password(message)
|
28
|
-
ui.say("\n")
|
29
|
-
password
|
30
|
-
end
|
31
|
-
|
32
|
-
def api_key
|
33
|
-
Gem.configuration[:gemcutter_key]
|
34
|
-
end
|
35
|
-
|
36
|
-
def push_url
|
37
|
-
if ENV['test']
|
38
|
-
"http://gemcutter.local"
|
39
|
-
else
|
40
|
-
"https://gemcutter.heroku.com"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
31
|
def send_gem
|
45
32
|
say "Pushing gem to Gemcutter..."
|
46
33
|
|
47
34
|
name = get_one_gem_name
|
48
|
-
url = URI.parse("#{
|
35
|
+
url = URI.parse("#{gemcutter_url}/gems")
|
49
36
|
|
50
|
-
http =
|
37
|
+
http = proxy_class.new(url.host, url.port)
|
51
38
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
52
39
|
http.use_ssl = (url.scheme == 'https')
|
53
|
-
request =
|
40
|
+
request = proxy_class::Post.new(url.path)
|
54
41
|
request.body = File.open(name).read
|
55
42
|
request.add_field("Content-Length", request.body.size)
|
56
43
|
request.add_field("Authorization", api_key)
|
@@ -60,38 +47,4 @@ class Gem::Commands::PushCommand < Gem::Command
|
|
60
47
|
say response.body
|
61
48
|
end
|
62
49
|
|
63
|
-
def sign_in
|
64
|
-
say "Enter your Gemcutter credentials. Don't have an account yet? Create one at #{URL}/sign_up"
|
65
|
-
|
66
|
-
email = ask("Email: ")
|
67
|
-
password = ask_for_password("Password: ")
|
68
|
-
|
69
|
-
url = URI.parse("#{push_url}/api_key")
|
70
|
-
|
71
|
-
http = Net::HTTP.new(url.host, url.port)
|
72
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
73
|
-
http.use_ssl = (url.scheme == 'https')
|
74
|
-
request = Net::HTTP::Get.new(url.path)
|
75
|
-
request.basic_auth email, password
|
76
|
-
response = http.request(request)
|
77
|
-
|
78
|
-
case response
|
79
|
-
when Net::HTTPSuccess
|
80
|
-
Gem.configuration[:gemcutter_key] = response.body
|
81
|
-
Gem.configuration.write
|
82
|
-
say "Signed in. Your api key has been stored in ~/.gemrc"
|
83
|
-
else
|
84
|
-
say response.body
|
85
|
-
terminate_interaction
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
class Gem::StreamUI
|
91
|
-
def ask_for_password(message)
|
92
|
-
system "stty -echo"
|
93
|
-
password = ask(message)
|
94
|
-
system "stty echo"
|
95
|
-
password
|
96
|
-
end
|
97
50
|
end
|
data/lib/commands/tumble.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
class Gem::Commands::TumbleCommand < Gem::
|
1
|
+
class Gem::Commands::TumbleCommand < Gem::AbstractCommand
|
2
2
|
def description
|
3
3
|
'Enable or disable Gemcutter as your primary gem source.'
|
4
4
|
end
|
@@ -24,9 +24,9 @@ class Gem::Commands::TumbleCommand < Gem::Command
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def show_sources
|
27
|
-
|
27
|
+
say "Your gem sources are now:"
|
28
28
|
Gem.sources.each do |source|
|
29
|
-
|
29
|
+
say "- #{source}"
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
data/lib/rubygems_plugin.rb
CHANGED
@@ -1,11 +1,20 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
|
2
2
|
|
3
3
|
require 'rubygems/command_manager'
|
4
|
+
require 'commands/abstract_command'
|
4
5
|
|
5
|
-
|
6
|
-
require
|
7
|
-
|
8
|
-
|
9
|
-
Gem::CommandManager.instance.register_command :tumble
|
6
|
+
%w[migrate push tumble].each do |command|
|
7
|
+
require "commands/#{command}"
|
8
|
+
Gem::CommandManager.instance.register_command command.to_sym
|
9
|
+
end
|
10
10
|
|
11
11
|
URL = "http://gemcutter.org" unless defined?(URL)
|
12
|
+
|
13
|
+
class Gem::StreamUI
|
14
|
+
def ask_for_password(message)
|
15
|
+
system "stty -echo"
|
16
|
+
password = ask(message)
|
17
|
+
system "stty echo"
|
18
|
+
password
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'redgreen'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/test_case'
|
7
|
+
gem 'fakeweb', '>= 1.2.5'
|
8
|
+
require 'fakeweb'
|
9
|
+
require 'rr'
|
10
|
+
|
11
|
+
FakeWeb.allow_net_connect = false
|
12
|
+
|
13
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
14
|
+
|
15
|
+
require "rubygems_plugin"
|
16
|
+
|
17
|
+
class CommandTest < ActiveSupport::TestCase
|
18
|
+
include RR::Adapters::TestUnit unless include?(RR::Adapters::TestUnit)
|
19
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../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
|
+
end
|
22
|
+
|
23
|
+
context "parsing the proxy" do
|
24
|
+
should "return nil if no proxy is set" do
|
25
|
+
stub(Gem).configuration { { :http_proxy => nil } }
|
26
|
+
assert_equal nil, @command.http_proxy
|
27
|
+
end
|
28
|
+
|
29
|
+
should "return nil if the proxy is set to :no_proxy" do
|
30
|
+
stub(Gem).configuration { { :http_proxy => :no_proxy } }
|
31
|
+
assert_equal nil, @command.http_proxy
|
32
|
+
end
|
33
|
+
|
34
|
+
should "return a proxy as a URI if set" do
|
35
|
+
stub(Gem).configuration { { :http_proxy => 'http://proxy.example.org:9192' } }
|
36
|
+
assert_equal 'proxy.example.org', @command.http_proxy.host
|
37
|
+
assert_equal 9192, @command.http_proxy.port
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# TODO: rewrite all of this junk using spies
|
42
|
+
#should "use a proxy if specified" do
|
43
|
+
# stub(@command).http_proxy { 'http://some.proxy' }
|
44
|
+
# mock(@command).use_proxy!
|
45
|
+
# mock(@command).sign_in
|
46
|
+
# @command.setup
|
47
|
+
#end
|
48
|
+
|
49
|
+
#should "not use a proxy if unspecified" do
|
50
|
+
# stub(@command).http_proxy { nil }
|
51
|
+
# mock(@command).use_proxy!.never
|
52
|
+
# mock(@command).sign_in
|
53
|
+
# @command.setup
|
54
|
+
#end
|
55
|
+
|
56
|
+
should "sign in if no api key" do
|
57
|
+
stub(Gem).configuration { {:gemcutter_key => nil} }
|
58
|
+
mock(@command).sign_in
|
59
|
+
@command.setup
|
60
|
+
end
|
61
|
+
|
62
|
+
should "not sign in if api key exists" do
|
63
|
+
stub(Gem).configuration { {:gemcutter_key => "1234567890"} }
|
64
|
+
mock(@command).sign_in.never
|
65
|
+
@command.setup
|
66
|
+
end
|
67
|
+
|
68
|
+
context "using the proxy" do
|
69
|
+
setup do
|
70
|
+
stub(Gem).configuration { { :http_proxy => "http://gilbert:sekret@proxy.example.org:8081" } }
|
71
|
+
@proxy_class = Object.new
|
72
|
+
mock(Net::HTTP).Proxy('proxy.example.org', 8081, 'gilbert', 'sekret') { @proxy_class }
|
73
|
+
@command.use_proxy!
|
74
|
+
end
|
75
|
+
|
76
|
+
should "replace Net::HTTP with a proxy version" do
|
77
|
+
assert_equal @proxy_class, @command.proxy_class
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "signing in" do
|
82
|
+
setup do
|
83
|
+
@email = "email"
|
84
|
+
@password = "password"
|
85
|
+
@key = "key"
|
86
|
+
mock(@command).say("Enter your Gemcutter credentials. Don't have an account yet? Create one at #{URL}/sign_up")
|
87
|
+
mock(@command).ask("Email: ") { @email }
|
88
|
+
mock(@command).ask_for_password("Password: ") { @password }
|
89
|
+
FakeWeb.register_uri :get, "https://#{@email}:#{@password}@gemcutter.heroku.com/api_key", :body => @key
|
90
|
+
|
91
|
+
@config = Object.new
|
92
|
+
stub(Gem).configuration { @config }
|
93
|
+
stub(@config)[:gemcutter_key] = @key
|
94
|
+
stub(@config).write
|
95
|
+
end
|
96
|
+
|
97
|
+
should "sign in" do
|
98
|
+
mock(@command).say("Signed in. Your api key has been stored in ~/.gemrc")
|
99
|
+
@command.sign_in
|
100
|
+
end
|
101
|
+
|
102
|
+
should "let the user know if there was a problem" do
|
103
|
+
@problem = "Access Denied"
|
104
|
+
mock(@command).say(@problem)
|
105
|
+
mock(@command).terminate_interaction
|
106
|
+
mock(@config).write.never
|
107
|
+
|
108
|
+
FakeWeb.register_uri :get, "https://#{@email}:#{@password}@gemcutter.heroku.com/api_key", :body => @problem, :status => 401
|
109
|
+
@command.sign_in
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../command_helper'
|
2
|
+
|
3
|
+
class MigrateCommandTest < CommandTest
|
4
|
+
context "executing the command" do
|
5
|
+
setup do
|
6
|
+
@command = Gem::Commands::MigrateCommand.new
|
7
|
+
stub(@command).setup
|
8
|
+
stub(@command).migrate
|
9
|
+
end
|
10
|
+
|
11
|
+
should "setup and send the gem" do
|
12
|
+
@command.execute
|
13
|
+
assert_received(@command) { |subject| subject.setup }
|
14
|
+
assert_received(@command) { |subject| subject.migrate }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "migrating" do
|
19
|
+
setup do
|
20
|
+
@command = Gem::Commands::MigrateCommand.new
|
21
|
+
@token = "deadbeef"
|
22
|
+
stub(@command).say
|
23
|
+
stub(@command).find
|
24
|
+
stub(@command).get_token { @token }
|
25
|
+
stub(@command).upload_token
|
26
|
+
stub(@command).check_for_approved
|
27
|
+
end
|
28
|
+
|
29
|
+
should "raise an error with no arguments" do
|
30
|
+
assert_raise Gem::CommandLineError do
|
31
|
+
@command.migrate
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
should "migrate the gem" do
|
36
|
+
stub(@command).get_one_gem_name { "mygem" }
|
37
|
+
@command.migrate
|
38
|
+
assert_received(@command) { |subject| subject.find("mygem") }
|
39
|
+
assert_received(@command) { |subject| subject.get_token }
|
40
|
+
assert_received(@command) { |subject| subject.upload_token(@token) }
|
41
|
+
assert_received(@command) { |subject| subject.check_for_approved }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "ask about the gem" do
|
46
|
+
setup do
|
47
|
+
@command = Gem::Commands::MigrateCommand.new
|
48
|
+
stub(@command).say
|
49
|
+
stub(@command).terminate_interaction
|
50
|
+
|
51
|
+
@name = "rails"
|
52
|
+
@json = "{\"downloads\":4,\"name\":\"rails\",\"slug\":\"rails\",\"authors\":\"David Heinemeier Hansson\",\"version\":\"2.3.3\",\"rubyforge_project\":\"rails\",\"info\":\"Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates.\"}"
|
53
|
+
end
|
54
|
+
|
55
|
+
should "find gem info if it exists" do
|
56
|
+
FakeWeb.register_uri :get, "https://gemcutter.heroku.com/gems/#{@name}.json", :body => @json
|
57
|
+
@command.find(@name)
|
58
|
+
assert_equal JSON.parse(@json), @command.rubygem
|
59
|
+
end
|
60
|
+
|
61
|
+
should "dump out if the gem couldn't be found" do
|
62
|
+
FakeWeb.register_uri :get, "https://gemcutter.heroku.com/gems/#{@name}.json", :body => "Not hosted here.", :status => 404
|
63
|
+
@command.find(@name)
|
64
|
+
assert_received(@command) { |subject| subject.say(anything) }
|
65
|
+
assert_received(@command) { |subject| subject.terminate_interaction }
|
66
|
+
end
|
67
|
+
|
68
|
+
should "dump out if bad json is returned" do
|
69
|
+
FakeWeb.register_uri :get, "https://gemcutter.heroku.com/gems/#{@name}.json", :body => "bad data is bad"
|
70
|
+
@command.find(@name)
|
71
|
+
assert_received(@command) { |subject| subject.say(anything) }
|
72
|
+
assert_received(@command) { |subject| subject.terminate_interaction }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "getting the token" do
|
77
|
+
setup do
|
78
|
+
@command = Gem::Commands::MigrateCommand.new
|
79
|
+
@name = "SomeGem"
|
80
|
+
@name = "somegem"
|
81
|
+
stub(@command).say
|
82
|
+
stub(@command).terminate_interaction
|
83
|
+
stub(@command).rubygem { { "name" => @name, "slug" => @slug } }
|
84
|
+
end
|
85
|
+
|
86
|
+
should "ask gemcutter to start the migration" do
|
87
|
+
token = "SECRET TOKEN"
|
88
|
+
FakeWeb.register_uri :post, "https://gemcutter.heroku.com/gems/#{@slug}/migrate", :body => token
|
89
|
+
assert_equal token, @command.get_token
|
90
|
+
end
|
91
|
+
|
92
|
+
should "dump out if gem could not be found" do
|
93
|
+
FakeWeb.register_uri :post, "https://gemcutter.heroku.com/gems/#{@slug}/migrate", :status => 404, :body => "not found"
|
94
|
+
@command.get_token
|
95
|
+
assert_received(@command) { |subject| subject.say("not found") }
|
96
|
+
assert_received(@command) { |subject| subject.terminate_interaction }
|
97
|
+
end
|
98
|
+
|
99
|
+
should "dump out if migration has already been completed" do
|
100
|
+
FakeWeb.register_uri :post, "https://gemcutter.heroku.com/gems/#{@slug}/migrate", :status => 403, :body => "already migrated"
|
101
|
+
@command.get_token
|
102
|
+
assert_received(@command) { |subject| subject.say("already migrated") }
|
103
|
+
assert_received(@command) { |subject| subject.terminate_interaction }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "uploading the token" do
|
108
|
+
setup do
|
109
|
+
@command = Gem::Commands::MigrateCommand.new
|
110
|
+
@token = "deadbeef"
|
111
|
+
stub(@command).say
|
112
|
+
stub(@command).rubygem { { "rubyforge_project" => "bostonrb" } }
|
113
|
+
stub(Net::SCP).start
|
114
|
+
end
|
115
|
+
|
116
|
+
should "connect to rubyforge and upload away" do
|
117
|
+
stub(@command).ask { "user" }
|
118
|
+
stub(@command).ask_for_password { "secret" }
|
119
|
+
@command.upload_token(@token)
|
120
|
+
|
121
|
+
# TODO: figure out how to test the upload! in the block
|
122
|
+
assert_received(Net::SCP) { |subject| subject.start("bostonrb.rubyforge.org", "user", :password => "secret") }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "checking if the rubygem was approved" do
|
127
|
+
setup do
|
128
|
+
@command = Gem::Commands::MigrateCommand.new
|
129
|
+
@slug = "rails"
|
130
|
+
|
131
|
+
stub(@command).say
|
132
|
+
stub(@command).rubygem { { "slug" => @slug } }
|
133
|
+
end
|
134
|
+
|
135
|
+
should "let the server decide the status" do
|
136
|
+
FakeWeb.register_uri :put, "https://gemcutter.heroku.com/gems/#{@slug}/migrate", :body => "Success!", :status => 400
|
137
|
+
@command.check_for_approved
|
138
|
+
assert_received(@command) { |subject| subject.say("Success!") }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../command_helper'
|
2
|
+
|
3
|
+
class PushCommandTest < CommandTest
|
4
|
+
context "pushing" do
|
5
|
+
setup do
|
6
|
+
@command = Gem::Commands::PushCommand.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
|
+
end
|
15
|
+
|
16
|
+
should "raise an error with no arguments" do
|
17
|
+
assert_raise Gem::CommandLineError do
|
18
|
+
@command.send_gem
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
should "push a gem" do
|
23
|
+
mock(@command).say("Pushing gem to Gemcutter...")
|
24
|
+
@response = "success"
|
25
|
+
FakeWeb.register_uri :post, "https://gemcutter.heroku.com/gems", :body => @response
|
26
|
+
|
27
|
+
@gem = "test"
|
28
|
+
@io = "io"
|
29
|
+
@config = { :gemcutter_key => "key" }
|
30
|
+
|
31
|
+
stub(File).open(@gem) { @io }
|
32
|
+
stub(@io).read.stub!.size
|
33
|
+
|
34
|
+
stub(@command).options { {:args => [@gem]} }
|
35
|
+
stub(Gem).configuration { @config }
|
36
|
+
|
37
|
+
mock(@command).say(@response)
|
38
|
+
@command.send_gem
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../command_helper'
|
2
|
+
|
3
|
+
class TumbleCommandTest < CommandTest
|
4
|
+
context "with a tumbler and some sources" do
|
5
|
+
setup do
|
6
|
+
@sources = ["gems.rubyforge.org", URL]
|
7
|
+
stub(Gem).sources { @sources }
|
8
|
+
@command = Gem::Commands::TumbleCommand.new
|
9
|
+
end
|
10
|
+
|
11
|
+
should "show sources" do
|
12
|
+
mock(@command).puts("Your gem sources are now:")
|
13
|
+
mock(@command).puts("- #{@sources.first}")
|
14
|
+
mock(@command).puts("- #{URL}")
|
15
|
+
@command.show_sources
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "tumbling the gem sources" do
|
20
|
+
setup do
|
21
|
+
@sources = ["http://rubyforge.org"]
|
22
|
+
stub(Gem).sources { @sources }
|
23
|
+
@config = Object.new
|
24
|
+
stub(Gem).configuration { @config }
|
25
|
+
|
26
|
+
@command = Gem::Commands::TumbleCommand.new
|
27
|
+
end
|
28
|
+
|
29
|
+
should "add gemcutter as first source" do
|
30
|
+
mock(@sources).unshift(URL)
|
31
|
+
mock(@config).write
|
32
|
+
|
33
|
+
@command.tumble
|
34
|
+
end
|
35
|
+
|
36
|
+
should "remove gemcutter if it's in the sources" do
|
37
|
+
mock(@sources).include?(URL) { true }
|
38
|
+
mock(@config).write
|
39
|
+
mock(@sources).delete(URL)
|
40
|
+
|
41
|
+
@command.tumble
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "executing the tumbler" do
|
46
|
+
setup do
|
47
|
+
@command = Gem::Commands::TumbleCommand.new
|
48
|
+
end
|
49
|
+
|
50
|
+
should "say thanks, tumble and show the sources" do
|
51
|
+
mock(@command).say(anything)
|
52
|
+
mock(@command).tumble
|
53
|
+
mock(@command).show_sources
|
54
|
+
|
55
|
+
@command.execute
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gemcutter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Quaranto
|
@@ -9,10 +9,19 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-10 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
16
25
|
description:
|
17
26
|
email: nick@quaran.to
|
18
27
|
executables: []
|
@@ -22,6 +31,8 @@ extensions: []
|
|
22
31
|
extra_rdoc_files:
|
23
32
|
- README.textile
|
24
33
|
files:
|
34
|
+
- lib/commands/abstract_command.rb
|
35
|
+
- lib/commands/migrate.rb
|
25
36
|
- lib/commands/push.rb
|
26
37
|
- lib/commands/tumble.rb
|
27
38
|
- lib/rubygems_plugin.rb
|
@@ -32,9 +43,14 @@ licenses: []
|
|
32
43
|
|
33
44
|
post_install_message: |+
|
34
45
|
|
35
|
-
|
36
|
-
|
37
|
-
|
46
|
+
========================================================================
|
47
|
+
Thanks for installing Gemcutter! You can now run:
|
48
|
+
|
49
|
+
gem tumble use Gemcutter as your primary RubyGem source
|
50
|
+
gem push publish your gems for the world to use and enjoy
|
51
|
+
gem migrate take over your gem from RubyForge on Gemcutter
|
52
|
+
|
53
|
+
========================================================================
|
38
54
|
|
39
55
|
rdoc_options:
|
40
56
|
- --charset=UTF-8
|
@@ -55,9 +71,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
71
|
requirements: []
|
56
72
|
|
57
73
|
rubyforge_project: gemcutter
|
58
|
-
rubygems_version: 1.3.
|
74
|
+
rubygems_version: 1.3.3
|
59
75
|
signing_key:
|
60
76
|
specification_version: 3
|
61
77
|
summary: Awesome gem hosting
|
62
|
-
test_files:
|
63
|
-
|
78
|
+
test_files:
|
79
|
+
- test/command_helper.rb
|
80
|
+
- test/unit/commands/migrate_command_test.rb
|
81
|
+
- test/unit/commands/tumble_command_test.rb
|
82
|
+
- test/unit/commands/push_command_test.rb
|
83
|
+
- test/unit/commands/abstract_command_test.rb
|