botanicus-gemcutter 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|