botanicus-gemcutter 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/commands/abstract_command.rb +115 -0
- data/lib/commands/migrate.rb +14 -0
- data/lib/commands/owner.rb +89 -0
- data/lib/commands/push.rb +39 -0
- data/lib/commands/tumble.rb +13 -0
- data/lib/rubygems_plugin.rb +9 -0
- data/test/command_helper.rb +30 -0
- metadata +80 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'rubygems/local_remote_options'
|
2
|
+
|
3
|
+
class Gem::AbstractCommand < Gem::Command
|
4
|
+
include Gem::LocalRemoteOptions
|
5
|
+
|
6
|
+
URL = "http://gemcutter.org"
|
7
|
+
|
8
|
+
def gemcutter_url
|
9
|
+
ENV['GEMCUTTER_URL'] || 'https://gemcutter.org'
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup
|
13
|
+
use_proxy! if http_proxy
|
14
|
+
sign_in unless api_key
|
15
|
+
end
|
16
|
+
|
17
|
+
def sign_in
|
18
|
+
say "Enter your Gemcutter credentials. Don't have an account yet? Create one at #{URL}/sign_up"
|
19
|
+
|
20
|
+
email = ask("Email: ")
|
21
|
+
password = ask_for_password("Password: ")
|
22
|
+
|
23
|
+
response = make_request(:get, "api_key") do |request|
|
24
|
+
request.basic_auth email, password
|
25
|
+
end
|
26
|
+
|
27
|
+
case response
|
28
|
+
when Net::HTTPSuccess
|
29
|
+
self.api_key = response.body
|
30
|
+
say "Signed in. Your api key has been stored in ~/.gem/credentials"
|
31
|
+
else
|
32
|
+
say response.body
|
33
|
+
terminate_interaction
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def credentials_path
|
38
|
+
File.join(Gem.user_home, '.gem', 'credentials')
|
39
|
+
end
|
40
|
+
|
41
|
+
def api_key
|
42
|
+
Gem.configuration.load_file(credentials_path)[:rubygems_api_key]
|
43
|
+
end
|
44
|
+
|
45
|
+
def api_key=(api_key)
|
46
|
+
config = Gem.configuration.load_file(credentials_path).merge(:rubygems_api_key => api_key)
|
47
|
+
|
48
|
+
dirname = File.dirname(credentials_path)
|
49
|
+
Dir.mkdir(dirname) unless File.exists?(dirname)
|
50
|
+
|
51
|
+
File.open(credentials_path, 'w') do |f|
|
52
|
+
f.write config.to_yaml
|
53
|
+
end
|
54
|
+
|
55
|
+
@rubygems_api_key = api_key
|
56
|
+
end
|
57
|
+
|
58
|
+
def make_request(method, path)
|
59
|
+
require 'net/http'
|
60
|
+
|
61
|
+
url = URI.parse("#{gemcutter_url}/api/v1/#{path}")
|
62
|
+
|
63
|
+
http = proxy_class.new(url.host, url.port)
|
64
|
+
|
65
|
+
if url.scheme == 'https'
|
66
|
+
require 'net/https'
|
67
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
68
|
+
http.use_ssl = true
|
69
|
+
end
|
70
|
+
|
71
|
+
request_method =
|
72
|
+
case method
|
73
|
+
when :get
|
74
|
+
proxy_class::Get
|
75
|
+
when :post
|
76
|
+
proxy_class::Post
|
77
|
+
when :put
|
78
|
+
proxy_class::Put
|
79
|
+
when :delete
|
80
|
+
proxy_class::Delete
|
81
|
+
else
|
82
|
+
raise ArgumentError
|
83
|
+
end
|
84
|
+
|
85
|
+
request = request_method.new(url.path)
|
86
|
+
request.add_field "User-Agent", "Gemcutter/0.2.0"
|
87
|
+
|
88
|
+
yield request if block_given?
|
89
|
+
http.request(request)
|
90
|
+
end
|
91
|
+
|
92
|
+
def use_proxy!
|
93
|
+
proxy_uri = http_proxy
|
94
|
+
@proxy_class = Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
|
95
|
+
end
|
96
|
+
|
97
|
+
def proxy_class
|
98
|
+
@proxy_class || Net::HTTP
|
99
|
+
end
|
100
|
+
|
101
|
+
# @return [URI, nil] the HTTP-proxy as a URI if set; +nil+ otherwise
|
102
|
+
def http_proxy
|
103
|
+
proxy = Gem.configuration[:http_proxy] || ENV['http_proxy'] || ENV['HTTP_PROXY']
|
104
|
+
return nil if proxy.nil? || proxy == :no_proxy
|
105
|
+
URI.parse(proxy)
|
106
|
+
end
|
107
|
+
|
108
|
+
def ask_for_password(message)
|
109
|
+
system "stty -echo"
|
110
|
+
password = ask(message)
|
111
|
+
system "stty echo"
|
112
|
+
ui.say("\n")
|
113
|
+
password
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Gem::Commands::MigrateCommand < Gem::AbstractCommand
|
2
|
+
def description
|
3
|
+
'Deprecate method for migrating a gem you own from Rubyforge to Gemcutter.'
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super 'migrate', description
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
say "This command is deprecated, RubyForge accounts/ownerships have been transferred to Gemcutter."
|
12
|
+
say "Please see http://gemcutter.org/pages/migrate for more information"
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
class Gem::Commands::OwnerCommand < Gem::AbstractCommand
|
2
|
+
def description
|
3
|
+
'Manage gem owners on Gemcutter.'
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super 'owner', description
|
8
|
+
defaults.merge!(:add => [], :remove => [])
|
9
|
+
|
10
|
+
add_option('-a', '--add EMAIL', 'Add an owner') do |value, options|
|
11
|
+
options[:add] << value
|
12
|
+
end
|
13
|
+
|
14
|
+
add_option('-r', '--remove EMAIL', 'Remove an owner') do |value, options|
|
15
|
+
options[:remove] << value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute
|
20
|
+
setup
|
21
|
+
name = get_one_gem_name
|
22
|
+
|
23
|
+
add_owners name, options[:add]
|
24
|
+
remove_owners name, options[:remove]
|
25
|
+
show_owners name
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_owners(name, owners)
|
29
|
+
owners.each do |owner|
|
30
|
+
response = make_request(:post, "gems/#{name}/owners.json") do |request|
|
31
|
+
request.set_form_data("email" => owner)
|
32
|
+
request.add_field("Authorization", api_key)
|
33
|
+
end
|
34
|
+
|
35
|
+
case response
|
36
|
+
when Net::HTTPSuccess
|
37
|
+
say "Added owner: #{owner}"
|
38
|
+
else
|
39
|
+
say "Error adding owner: #{owner}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove_owners(name, owners)
|
45
|
+
owners.each do |owner|
|
46
|
+
response = make_request(:delete, "gems/#{name}/owners.json") do |request|
|
47
|
+
request.set_form_data(:email => owner)
|
48
|
+
request.add_field("Authorization", api_key)
|
49
|
+
end
|
50
|
+
|
51
|
+
case response
|
52
|
+
when Net::HTTPSuccess
|
53
|
+
say "Removed owner: #{owner}"
|
54
|
+
else
|
55
|
+
say "Error removing owner: #{owner}"
|
56
|
+
end
|
57
|
+
end end
|
58
|
+
|
59
|
+
def show_owners(name)
|
60
|
+
require 'json/pure'
|
61
|
+
response = make_request(:get, "gems/#{name}/owners.json") do |request|
|
62
|
+
request.add_field("Authorization", api_key)
|
63
|
+
end
|
64
|
+
|
65
|
+
case response
|
66
|
+
when Net::HTTPSuccess
|
67
|
+
begin
|
68
|
+
owners = JSON.parse(response.body)
|
69
|
+
|
70
|
+
say "Owners for gem: #{name}"
|
71
|
+
owners.each do |owner|
|
72
|
+
say "- #{owner['email']}"
|
73
|
+
end
|
74
|
+
rescue JSON::ParserError => json_error
|
75
|
+
say "There was a problem parsing the data: #{json_error}"
|
76
|
+
terminate_interaction
|
77
|
+
end
|
78
|
+
when Net::HTTPNotFound
|
79
|
+
say "This gem is currently not hosted on Gemcutter."
|
80
|
+
terminate_interaction
|
81
|
+
when Net::HTTPUnauthorized
|
82
|
+
say "You do not have permission to manage this gem."
|
83
|
+
terminate_interaction
|
84
|
+
else
|
85
|
+
say "There was a problem processing your request."
|
86
|
+
terminate_interaction
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Gem::Commands::PushCommand < Gem::AbstractCommand
|
2
|
+
|
3
|
+
def description
|
4
|
+
'Push a gem up to Gemcutter'
|
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 Gemcutter..."
|
27
|
+
|
28
|
+
name = get_one_gem_name
|
29
|
+
response = make_request(:post, "gems") do |request|
|
30
|
+
request.body = File.open(name, 'rb'){|io| io.read }
|
31
|
+
request.add_field("Content-Length", request.body.size)
|
32
|
+
request.add_field("Content-Type", "application/octet-stream")
|
33
|
+
request.add_field("Authorization", api_key)
|
34
|
+
end
|
35
|
+
|
36
|
+
say response.body
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Gem::Commands::TumbleCommand < Gem::AbstractCommand
|
2
|
+
def description
|
3
|
+
"Deprecated method of upgrading to Gemcutter.org for gem downloads"
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super 'tumble', description
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
say "This command is deprecated, Gemcutter.org is the primary source for gems."
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
|
2
|
+
|
3
|
+
require 'rubygems/command_manager'
|
4
|
+
require 'commands/abstract_command'
|
5
|
+
|
6
|
+
%w[migrate owner push tumble].each do |command|
|
7
|
+
require "commands/#{command}"
|
8
|
+
Gem::CommandManager.instance.register_command command.to_sym
|
9
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require "#{File.dirname(__FILE__)}/../../vendor/bundler_gems/environment"
|
5
|
+
|
6
|
+
require 'shoulda'
|
7
|
+
begin
|
8
|
+
require 'redgreen'
|
9
|
+
rescue LoadError
|
10
|
+
end
|
11
|
+
require 'active_support'
|
12
|
+
require 'active_support/test_case'
|
13
|
+
require 'fakeweb'
|
14
|
+
require 'rr'
|
15
|
+
|
16
|
+
FakeWeb.allow_net_connect = false
|
17
|
+
|
18
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
19
|
+
|
20
|
+
require "rubygems_plugin"
|
21
|
+
|
22
|
+
class CommandTest < ActiveSupport::TestCase
|
23
|
+
include RR::Adapters::TestUnit unless include?(RR::Adapters::TestUnit)
|
24
|
+
end
|
25
|
+
|
26
|
+
def stub_config(config)
|
27
|
+
file = Gem::ConfigFile.new({})
|
28
|
+
config.each { |key, value| file[key] = value }
|
29
|
+
stub(Gem).configuration { file }
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: botanicus-gemcutter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Quaranto
|
8
|
+
- Jakub Stastny
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-12-10 00:00:00 +00:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: json_pure
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
description: Adds several commands for using gemcutter.org, such as pushing new gems, migrating gems from RubyForge, and more.
|
27
|
+
email: nick@quaran.to
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files: []
|
33
|
+
|
34
|
+
files:
|
35
|
+
- lib/commands/abstract_command.rb
|
36
|
+
- lib/commands/migrate.rb
|
37
|
+
- lib/commands/owner.rb
|
38
|
+
- lib/commands/push.rb
|
39
|
+
- lib/commands/tumble.rb
|
40
|
+
- lib/rubygems_plugin.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/qrush/gemcutter
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message: |+
|
46
|
+
|
47
|
+
========================================================================
|
48
|
+
|
49
|
+
Thanks for installing Gemcutter! You can now run:
|
50
|
+
|
51
|
+
gem push publish your gems for the world to use and enjoy
|
52
|
+
gem owner allow/disallow others to push to your gems
|
53
|
+
|
54
|
+
========================================================================
|
55
|
+
|
56
|
+
rdoc_options:
|
57
|
+
- --charset=UTF-8
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 1.3.5
|
71
|
+
version:
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project: gemcutter
|
75
|
+
rubygems_version: 1.3.5
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Commands to interact with gemcutter.org
|
79
|
+
test_files:
|
80
|
+
- test/command_helper.rb
|