gemfury 0.10.0.rc1
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.
- checksums.yaml +7 -0
- data/README.md +62 -0
- data/bin/fury +10 -0
- data/bin/gemfury +2 -0
- data/lib/faraday/adapter/fury_http.rb +32 -0
- data/lib/faraday/request/multipart_with_file.rb +34 -0
- data/lib/gemfury.rb +53 -0
- data/lib/gemfury/client.rb +211 -0
- data/lib/gemfury/client/filters.rb +18 -0
- data/lib/gemfury/client/middleware.rb +23 -0
- data/lib/gemfury/command.rb +12 -0
- data/lib/gemfury/command/app.rb +295 -0
- data/lib/gemfury/command/authorization.rb +79 -0
- data/lib/gemfury/configuration.rb +72 -0
- data/lib/gemfury/const.rb +26 -0
- data/lib/gemfury/error.rb +28 -0
- data/lib/gemfury/platform.rb +19 -0
- data/lib/gemfury/tasks.rb +2 -0
- data/lib/gemfury/tasks/release.rake +36 -0
- data/lib/gemfury/version.rb +3 -0
- data/lib/rubygems/commands/fury_command.rb +29 -0
- data/lib/rubygems_plugin.rb +2 -0
- metadata +177 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 624ab4a41068c6d17c63693ff3bc4de20641ee87390f68df9f7d02c29a59e16e
|
4
|
+
data.tar.gz: f4657867345ca219161a3090be0fb2ba19c475cb3fb33ac637213aa0c32916c5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 569f391a1d8230204d9b1778042a57e53a72afae523ae7f1b4646971ad2db1918dc1260792f0fce000e995619d78f5c2bc3ca013945883cb01c6292463694219
|
7
|
+
data.tar.gz: f3906e7e9b1e360ba75f8615f5728009b3e8c01915be185777ea2fdef9d1207990abe3c86f405a18e8275461eef334efb8c1c738ceb62eb74765d63d24fa063e
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
Gemfury CLI
|
2
|
+
===========
|
3
|
+
|
4
|
+
[](http://badge.fury.io/rb/gemfury)
|
5
|
+
[](https://travis-ci.org/gemfury/gemfury)
|
6
|
+
[](https://codeclimate.com/github/gemfury/gemfury)
|
7
|
+
|
8
|
+
This is the Gemfury CLI used to manage your Gemfury packages from the command line. If you're
|
9
|
+
familiar with the service and want to jump straight into command line action, please proceed to
|
10
|
+
the [CLI documentation](https://gemfury.com/help/gemfury-cli).
|
11
|
+
|
12
|
+
Gemfury is your personal cloud for your private and custom RubyGems, Python packages, and NPM
|
13
|
+
modules. Once you upload your packages and enable Gemfury as a source, you can securely deploy
|
14
|
+
any package to any host. It's simple, reliable, and hassle-free.
|
15
|
+
|
16
|
+
|
17
|
+
### Introduction to Gemfury
|
18
|
+
* [Gemfury homepage](https://gemfury.com/)
|
19
|
+
* [Getting started with Gemfury](https://gemfury.com/help/getting-started)
|
20
|
+
|
21
|
+
### Using Gemfury CLI
|
22
|
+
* [CLI documentation](https://gemfury.com/help/gemfury-cli)
|
23
|
+
* [Uploading private packages](https://gemfury.com/help/gemfury-cli#uploading-packages)
|
24
|
+
* [Manage collaborators](https://gemfury.com/help/gemfury-cli#collaboration)
|
25
|
+
|
26
|
+
### Putting Gemfury to work
|
27
|
+
* [Install private RubyGems](https://gemfury.com/help/install-gems)
|
28
|
+
* [Install private NPM modules](https://gemfury.com/help/npm-registry)
|
29
|
+
* [Install private Python packages](https://gemfury.com/help/pypi-server)
|
30
|
+
* [Install private Composer packages](https://gemfury.com/help/php-composer-server)
|
31
|
+
* [Private RubyGems on Heroku](https://gemfury.com/help/private-gems-on-heroku)
|
32
|
+
|
33
|
+
## Contribution and Improvements
|
34
|
+
|
35
|
+
Please [email us](mailto:support@gemfury.com) if we've missed some key functionality or you have problems installing the CLI client. Better yet, fork the code, make the changes, and submit a pull request to speed things along.
|
36
|
+
|
37
|
+
### Submitting updates
|
38
|
+
|
39
|
+
If you would like to contribute to this project, just do the following:
|
40
|
+
|
41
|
+
1. Fork the repo on Github.
|
42
|
+
2. Add your features and make commits to your forked repo.
|
43
|
+
3. Make a pull request to this repo.
|
44
|
+
4. Review will be done and changes will be requested.
|
45
|
+
5. Once changes are done or no changes are required, pull request will be merged.
|
46
|
+
6. The next release will have your changes in it.
|
47
|
+
|
48
|
+
Please take a look at the issues page if you want to get started.
|
49
|
+
|
50
|
+
### Feature requests
|
51
|
+
|
52
|
+
If you think it would be nice to have a particular feature that is presently not implemented, we would love
|
53
|
+
to hear that and consider working on it. Just open an issue in Github.
|
54
|
+
|
55
|
+
### Dependency conflicts
|
56
|
+
|
57
|
+
Over time, dependencies for this gem will get stale and may interfere with your other gems. Please let us know if you run into this and we will re-test our gem with the new version of the dependency and update the _gemspec_.
|
58
|
+
|
59
|
+
|
60
|
+
## Questions
|
61
|
+
|
62
|
+
Please email support@gemfury.com or file a Github Issue if you have any other questions or problems.
|
data/bin/fury
ADDED
data/bin/gemfury
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# This is a Faraday adapter that bypasses Faraday's response body
|
2
|
+
# processing and streams body to STDOUT for text requests
|
3
|
+
|
4
|
+
class Faraday::Adapter
|
5
|
+
class FuryHttp < NetHttp
|
6
|
+
def perform_request(http, env)
|
7
|
+
accept = env.request_headers['Accept']
|
8
|
+
return super if accept !~ /text\z/
|
9
|
+
|
10
|
+
# Stream response body to STDOUT on success
|
11
|
+
http.request(create_request(env)) do |resp|
|
12
|
+
unless resp.is_a?(Net::HTTPSuccess)
|
13
|
+
resp.body # Cache error body
|
14
|
+
else
|
15
|
+
resp.read_body do |chunk|
|
16
|
+
$stdout.print(chunk)
|
17
|
+
$stdout.flush
|
18
|
+
end
|
19
|
+
|
20
|
+
# Prevent #body from calling #read_body again
|
21
|
+
klass = (class << resp; self; end)
|
22
|
+
klass.send(:define_method, :body) { nil }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return response to NetHttp adapter
|
26
|
+
return resp
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
register_middleware(:fury_http => FuryHttp)
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
# @private
|
4
|
+
module Faraday
|
5
|
+
# @private
|
6
|
+
class Request::MultipartWithFile < Faraday::Middleware
|
7
|
+
def call(env)
|
8
|
+
if env[:body].is_a?(Hash)
|
9
|
+
env[:body].each do |key, value|
|
10
|
+
if value.is_a?(File)
|
11
|
+
env[:body][key] = Faraday::UploadIO.new(value, mime_type(value), value.path)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def mime_type(file)
|
22
|
+
case file.path
|
23
|
+
when /\.jpe?g/i
|
24
|
+
'image/jpeg'
|
25
|
+
when /\.gif$/i
|
26
|
+
'image/gif'
|
27
|
+
when /\.png$/i
|
28
|
+
'image/png'
|
29
|
+
else
|
30
|
+
'application/octet-stream'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/gemfury.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
gem "multi_json", "~> 1.10"
|
2
|
+
gem "faraday", ">= 0.9.0", "< 0.16.0.pre"
|
3
|
+
gem "netrc", ">= 0.10.0", "< 0.12.0.pre"
|
4
|
+
|
5
|
+
require 'time'
|
6
|
+
require 'cgi'
|
7
|
+
require 'uri'
|
8
|
+
require 'netrc'
|
9
|
+
require 'multi_json'
|
10
|
+
require 'faraday'
|
11
|
+
require 'faraday/adapter/fury_http'
|
12
|
+
require 'faraday/request/multipart_with_file'
|
13
|
+
|
14
|
+
require 'gemfury/version'
|
15
|
+
require 'gemfury/const'
|
16
|
+
require 'gemfury/error'
|
17
|
+
require 'gemfury/platform'
|
18
|
+
require 'gemfury/configuration'
|
19
|
+
|
20
|
+
require 'gemfury/client/filters'
|
21
|
+
require 'gemfury/client/middleware'
|
22
|
+
require 'gemfury/client'
|
23
|
+
|
24
|
+
module Gemfury
|
25
|
+
extend Configuration
|
26
|
+
class << self
|
27
|
+
# Alias for Gemfury::Client.new
|
28
|
+
#
|
29
|
+
# @return [Gemfury::Client]
|
30
|
+
def new(options={})
|
31
|
+
Gemfury::Client.new(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Delegate to Gemfury::Client
|
35
|
+
def method_missing(method, *args, &block)
|
36
|
+
return super unless new.respond_to?(method)
|
37
|
+
new.send(method, *args, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
def respond_to?(method, include_private = false)
|
41
|
+
new.respond_to?(method, include_private) || super(method, include_private)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Hash
|
47
|
+
# Access nested hashes as a period-separated path
|
48
|
+
def path(path, separator = '.')
|
49
|
+
path.split(separator).inject(self) do |hash, part|
|
50
|
+
hash.is_a?(Hash) ? hash[part] : nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
module Gemfury
|
2
|
+
class Client
|
3
|
+
include Gemfury::Client::Filters
|
4
|
+
attr_accessor *Configuration::VALID_OPTIONS_KEYS
|
5
|
+
|
6
|
+
# Creates a new API
|
7
|
+
def initialize(options={})
|
8
|
+
options = Gemfury.options.merge(options)
|
9
|
+
Configuration::VALID_OPTIONS_KEYS.each do |key|
|
10
|
+
send("#{key}=", options[key])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get the information for the current account
|
15
|
+
def account_info
|
16
|
+
ensure_ready!(:authorization)
|
17
|
+
response = connection.get('users/me')
|
18
|
+
checked_response_body(response)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Get the information for the current account
|
22
|
+
def accounts
|
23
|
+
ensure_ready!(:authorization)
|
24
|
+
response = connection.get('accounts')
|
25
|
+
checked_response_body(response)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Uploading a gem file
|
29
|
+
def push_gem(file, options = {})
|
30
|
+
ensure_ready!(:authorization)
|
31
|
+
push_api = connection(:url => self.pushpoint)
|
32
|
+
response = push_api.post('uploads', options.merge(:file => file))
|
33
|
+
checked_response_body(response)
|
34
|
+
end
|
35
|
+
|
36
|
+
# List available gems
|
37
|
+
def list(options = {})
|
38
|
+
ensure_ready!(:authorization)
|
39
|
+
response = connection.get('gems', options)
|
40
|
+
checked_response_body(response)
|
41
|
+
end
|
42
|
+
|
43
|
+
# List versions for a gem
|
44
|
+
def versions(name, options = {})
|
45
|
+
ensure_ready!(:authorization)
|
46
|
+
url = "gems/#{escape(name)}/versions"
|
47
|
+
response = connection.get(url, options)
|
48
|
+
checked_response_body(response)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Delete a gem version
|
52
|
+
def yank_version(name, version, options = {})
|
53
|
+
ensure_ready!(:authorization)
|
54
|
+
url = "gems/#{escape(name)}/versions/#{escape(version)}"
|
55
|
+
response = connection.delete(url, options)
|
56
|
+
checked_response_body(response)
|
57
|
+
end
|
58
|
+
|
59
|
+
# LEGACY: Authentication token via email/password
|
60
|
+
def get_access_token(*args)
|
61
|
+
login(*args)['token']
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get authentication info via email/password
|
65
|
+
def login(email, password, opts = {})
|
66
|
+
ensure_ready!
|
67
|
+
opts = opts.merge(:email => email, :password => password)
|
68
|
+
checked_response_body(connection.post('login', opts))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Invalidate session token
|
72
|
+
def logout
|
73
|
+
ensure_ready!(:authorization)
|
74
|
+
response = connection.post('logout')
|
75
|
+
checked_response_body(response)
|
76
|
+
end
|
77
|
+
|
78
|
+
# List collaborators for this account
|
79
|
+
def list_collaborators(options = {})
|
80
|
+
ensure_ready!(:authorization)
|
81
|
+
response = connection.get('collaborators', options)
|
82
|
+
checked_response_body(response)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Add a collaborator to the account
|
86
|
+
def add_collaborator(login, options = {})
|
87
|
+
ensure_ready!(:authorization)
|
88
|
+
url = "collaborators/#{escape(login)}"
|
89
|
+
response = connection.put(url, options)
|
90
|
+
checked_response_body(response)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Remove a collaborator to the account
|
94
|
+
def remove_collaborator(login, options = {})
|
95
|
+
ensure_ready!(:authorization)
|
96
|
+
url = "collaborators/#{escape(login)}"
|
97
|
+
response = connection.delete(url, options)
|
98
|
+
checked_response_body(response)
|
99
|
+
end
|
100
|
+
|
101
|
+
# List Git repos for this account
|
102
|
+
def git_repos(options = {})
|
103
|
+
ensure_ready!(:authorization)
|
104
|
+
response = connection.get(git_repo_path, options)
|
105
|
+
checked_response_body(response)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Update repository name and settings
|
109
|
+
def git_update(repo, options = {})
|
110
|
+
ensure_ready!(:authorization)
|
111
|
+
response = connection.patch(git_repo_path(repo), options)
|
112
|
+
checked_response_body(response)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Reset repository to initial state
|
116
|
+
def git_reset(repo, options = {})
|
117
|
+
ensure_ready!(:authorization)
|
118
|
+
response = connection.delete(git_repo_path(repo), options)
|
119
|
+
checked_response_body(response)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Rebuild Git repository package
|
123
|
+
def git_rebuild(repo, options = {})
|
124
|
+
ensure_ready!(:authorization)
|
125
|
+
url = "#{git_repo_path(repo)}/builds"
|
126
|
+
api = connection(:api_format => :text)
|
127
|
+
checked_response_body(api.post(url, options))
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def escape(str)
|
132
|
+
CGI.escape(str)
|
133
|
+
end
|
134
|
+
|
135
|
+
def git_repo_path(*args)
|
136
|
+
rest = args.map { |a| escape(a) }
|
137
|
+
['git/repos', self.account || 'me'].concat(rest).join('/')
|
138
|
+
end
|
139
|
+
|
140
|
+
def connection(options = {})
|
141
|
+
# The 'Accept' HTTP header for API versioning
|
142
|
+
http_accept = begin
|
143
|
+
v = options.delete(:api_version) || self.api_version
|
144
|
+
f = options.delete(:api_format) || :json
|
145
|
+
"application/vnd.fury.v#{v.to_i}+#{f}"
|
146
|
+
end
|
147
|
+
|
148
|
+
# Faraday client options
|
149
|
+
options = {
|
150
|
+
:url => self.endpoint,
|
151
|
+
:params => {},
|
152
|
+
:headers => {
|
153
|
+
:accept => http_accept,
|
154
|
+
:user_agent => self.user_agent,
|
155
|
+
:x_gem_version => Gemfury::VERSION,
|
156
|
+
}.merge(options.delete(:headers) || {})
|
157
|
+
}.merge(options)
|
158
|
+
|
159
|
+
if self.user_api_key
|
160
|
+
options[:headers][:authorization] = self.user_api_key
|
161
|
+
end
|
162
|
+
|
163
|
+
if self.account
|
164
|
+
options[:params][:as] = self.account
|
165
|
+
end
|
166
|
+
|
167
|
+
Faraday.new(options) do |builder|
|
168
|
+
builder.use Faraday::Request::MultipartWithFile
|
169
|
+
builder.use Faraday::Request::Multipart
|
170
|
+
builder.use Faraday::Request::UrlEncoded
|
171
|
+
builder.use ParseJson
|
172
|
+
builder.use Handle503
|
173
|
+
builder.adapter :fury_http
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def checked_response_body(response)
|
178
|
+
if response.success?
|
179
|
+
return response.body
|
180
|
+
else
|
181
|
+
error = (response.body || {})['error'] || {}
|
182
|
+
error_class = case error['type']
|
183
|
+
when 'Forbidden' then Gemfury::Forbidden
|
184
|
+
when 'GemVersionError' then Gemfury::InvalidGemVersion
|
185
|
+
when 'InvalidGemFile' then Gemfury::CorruptGemFile
|
186
|
+
when 'DupeVersion' then Gemfury::DupeVersion
|
187
|
+
else
|
188
|
+
case response.status
|
189
|
+
when 401 then Gemfury::Unauthorized
|
190
|
+
when 403 then Gemfury::Forbidden
|
191
|
+
when 404 then Gemfury::NotFound
|
192
|
+
when 409 then Gemfury::Conflict
|
193
|
+
when 503 then Gemfury::TimeoutError
|
194
|
+
else Gemfury::Error
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
raise(error_class, error['message'])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def s3_put_file(uri, file)
|
203
|
+
Faraday::Connection.new(uri) do |f|
|
204
|
+
f.adapter :net_http
|
205
|
+
end.put(uri, file, {
|
206
|
+
:content_length => file.stat.size.to_s,
|
207
|
+
:content_type => ''
|
208
|
+
})
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Gemfury
|
2
|
+
class Client
|
3
|
+
module Filters
|
4
|
+
|
5
|
+
private
|
6
|
+
def ensure_ready!(*args)
|
7
|
+
# Ensure authorization
|
8
|
+
if args.include?(:authorization)
|
9
|
+
raise Unauthorized unless authenticated?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def authenticated?
|
14
|
+
self.user_api_key && !self.user_api_key.empty?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Gemfury
|
2
|
+
class Client
|
3
|
+
class Handle503 < Faraday::Middleware
|
4
|
+
def call(env)
|
5
|
+
# This prevents errors in ParseJson
|
6
|
+
@app.call(env).on_complete do |out|
|
7
|
+
out[:body] = '' if out[:status] == 503
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ParseJson < Faraday::Response::Middleware
|
13
|
+
def parse(body)
|
14
|
+
body =~ /\A\s*\z/ ? nil : MultiJson.decode(body)
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_complete(response)
|
18
|
+
ok = response.request_headers['Accept'] =~ /json\z/
|
19
|
+
response.body = parse(response.body) if ok
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
gem "highline", ">= 1.6.0", "< 2.1.0.pre"
|
2
|
+
gem "thor", ">= 0.14.0", "< 1.0.0.pre"
|
3
|
+
|
4
|
+
require 'thor'
|
5
|
+
require 'yaml'
|
6
|
+
require 'highline'
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
module Gemfury::Command; end
|
10
|
+
|
11
|
+
require 'gemfury/command/authorization'
|
12
|
+
require 'gemfury/command/app'
|
@@ -0,0 +1,295 @@
|
|
1
|
+
class Gemfury::Command::App < Thor
|
2
|
+
include Gemfury::Command::Authorization
|
3
|
+
UserAgent = "Gemfury CLI #{Gemfury::VERSION}".freeze
|
4
|
+
PackageExtensions = %w(gem egg tar.gz tgz nupkg)
|
5
|
+
|
6
|
+
# Impersonation
|
7
|
+
class_option :as, :desc => 'Access an account other than your own'
|
8
|
+
class_option :api_token, :desc => 'API token to use for commands'
|
9
|
+
|
10
|
+
map "-v" => :version
|
11
|
+
desc "version", "Show Gemfury version", :hide => true
|
12
|
+
def version
|
13
|
+
shell.say Gemfury::VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
### PACKAGE MANAGEMENT ###
|
17
|
+
option :public, :type => :boolean, :desc => "Create as public package"
|
18
|
+
desc "push FILE", "Upload a new version of a package"
|
19
|
+
def push(*gems)
|
20
|
+
with_checks_and_rescues do
|
21
|
+
push_files(:push, gems)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "list", "List your packages"
|
26
|
+
def list
|
27
|
+
with_checks_and_rescues do
|
28
|
+
gems = client.list
|
29
|
+
shell.say "\n*** GEMFURY PACKAGES ***\n\n"
|
30
|
+
|
31
|
+
va = [ %w{ name kind version privacy } ]
|
32
|
+
gems.each do |g|
|
33
|
+
va << [ g['name'], g['language'],
|
34
|
+
g.path('latest_version.version') || 'beta',
|
35
|
+
g['private'] ? 'private' : 'public ' ]
|
36
|
+
end
|
37
|
+
|
38
|
+
shell.print_table(va)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "versions NAME", "List all the package versions"
|
43
|
+
def versions(gem_name)
|
44
|
+
with_checks_and_rescues do
|
45
|
+
versions = client.versions(gem_name)
|
46
|
+
shell.say "\n*** #{gem_name.capitalize} Versions ***\n\n"
|
47
|
+
|
48
|
+
va = []
|
49
|
+
va = [ %w{ version uploaded uploaded_by } ]
|
50
|
+
versions.each do |v|
|
51
|
+
uploaded = Time.parse(v['created_at']).strftime('%F %R %z')
|
52
|
+
va << [ v['version'], uploaded, v['created_by']['name'] ]
|
53
|
+
end
|
54
|
+
|
55
|
+
shell.print_table(va)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "yank NAME", "Delete a package version"
|
60
|
+
method_options %w(version -v) => :required
|
61
|
+
def yank(gem_name)
|
62
|
+
with_checks_and_rescues do
|
63
|
+
version = options[:version]
|
64
|
+
client.yank_version(gem_name, version)
|
65
|
+
shell.say "\n*** Yanked #{gem_name}-#{version} ***\n\n"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
### AUTHENTICATION ###
|
70
|
+
desc "logout", "Remove Gemfury credentials"
|
71
|
+
def logout
|
72
|
+
if !has_credentials?
|
73
|
+
shell.say "You are logged out"
|
74
|
+
elsif shell.yes? "Are you sure you want to log out? [yN]"
|
75
|
+
with_checks_and_rescues { client.logout }
|
76
|
+
wipe_credentials!
|
77
|
+
shell.say "You have been logged out"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "login", "Save Gemfury credentials"
|
82
|
+
def login
|
83
|
+
with_checks_and_rescues do
|
84
|
+
me = client.account_info['name']
|
85
|
+
shell.say %Q(You are logged in as "#{me}"), :green
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "whoami", "Show current user"
|
90
|
+
def whoami
|
91
|
+
has_credentials? ? self.login : begin
|
92
|
+
shell.say %Q(You are not logged in), :green
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "accounts", "Show info about your Gemfury accounts"
|
97
|
+
def accounts
|
98
|
+
with_checks_and_rescues do
|
99
|
+
accounts = client.accounts
|
100
|
+
|
101
|
+
va = [ %w{ name kind permission } ]
|
102
|
+
accounts.each do |a|
|
103
|
+
va << [ a['name'], a['type'], a['viewer_permission'].downcase ]
|
104
|
+
end
|
105
|
+
|
106
|
+
shell.print_table(va)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
### COLLABORATION MANAGEMENT ###
|
111
|
+
map "sharing:add" => 'sharing_add'
|
112
|
+
map "sharing:remove" => 'sharing_remove'
|
113
|
+
|
114
|
+
desc "sharing", "List collaborators"
|
115
|
+
def sharing
|
116
|
+
with_checks_and_rescues do
|
117
|
+
me = client.account_info['username']
|
118
|
+
collaborators = client.list_collaborators
|
119
|
+
if collaborators.empty?
|
120
|
+
shell.say "You (#{me}) are the only collaborator", :green
|
121
|
+
else
|
122
|
+
shell.say %Q(\n*** Collaborators for "#{me}" ***\n), :green
|
123
|
+
usernames = [me] + collaborators.map { |c| c['username'] }
|
124
|
+
shell.say usernames.join("\n")
|
125
|
+
end
|
126
|
+
shell.say "\n"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
desc "sharing:add EMAIL", "Add a collaborator"
|
131
|
+
def sharing_add(username)
|
132
|
+
with_checks_and_rescues do
|
133
|
+
client.add_collaborator(username)
|
134
|
+
shell.say "Invited #{username} as a collaborator"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
desc "sharing:remove EMAIL", "Remove a collaborator"
|
139
|
+
def sharing_remove(username)
|
140
|
+
with_checks_and_rescues do
|
141
|
+
client.remove_collaborator(username)
|
142
|
+
shell.say "Removed #{username} as a collaborator"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
### MIGRATION (Pushing directories) ###
|
147
|
+
desc "migrate DIR", "Upload all packages within a directory"
|
148
|
+
def migrate(*paths)
|
149
|
+
with_checks_and_rescues do
|
150
|
+
gem_paths = Dir.glob(paths.map do |p|
|
151
|
+
if File.directory?(p)
|
152
|
+
PackageExtensions.map { |ext| "#{p}/**/*.#{ext}" }
|
153
|
+
elsif File.file?(p)
|
154
|
+
p
|
155
|
+
else
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
end.flatten.compact)
|
159
|
+
|
160
|
+
if gem_paths.empty?
|
161
|
+
die!("Problem: No valid packages found", nil, :migrate)
|
162
|
+
else
|
163
|
+
shell.say "Found the following packages:"
|
164
|
+
gem_paths.each { |p| shell.say " #{File.basename(p)}" }
|
165
|
+
if shell.yes? "Upload these files to Gemfury? [yN]", :green
|
166
|
+
push_files(:migrate, gem_paths)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
### GIT REPOSITORY MANAGEMENT ###
|
173
|
+
map "git:list" => 'git_list'
|
174
|
+
map "git:reset" => 'git_reset'
|
175
|
+
map "git:rename" => 'git_rename'
|
176
|
+
map "git:rebuild" => 'git_rebuild'
|
177
|
+
|
178
|
+
desc "git:list", "List Git repositories"
|
179
|
+
def git_list
|
180
|
+
with_checks_and_rescues do
|
181
|
+
repos = client.git_repos['repos']
|
182
|
+
shell.say "\n*** GEMFURY GIT REPOS ***\n\n"
|
183
|
+
names = repos.map { |r| r['name'] }
|
184
|
+
names.sort.each { |n| shell.say(n) }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
desc "git:rename", "Rename a Git repository"
|
189
|
+
def git_rename(repo, new_name)
|
190
|
+
with_checks_and_rescues do
|
191
|
+
client.git_update(repo, :repo => { :name => new_name })
|
192
|
+
shell.say "Renamed #{repo} repository to #{new_name}\n"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
desc "git:reset", "Remove a Git repository"
|
197
|
+
def git_reset(repo)
|
198
|
+
with_checks_and_rescues do
|
199
|
+
client.git_reset(repo)
|
200
|
+
shell.say "\n*** Yanked #{repo} repository ***\n\n"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
desc "git:rebuild", "Rebuild a Git repository"
|
205
|
+
method_options %w(revision -r) => :string
|
206
|
+
def git_rebuild(repo)
|
207
|
+
with_checks_and_rescues do
|
208
|
+
params = { :revision => options[:revision] }
|
209
|
+
shell.say "\n*** Rebuilding #{repo} repository ***\n\n"
|
210
|
+
shell.say client.git_rebuild(repo, :build => params)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
private
|
215
|
+
def client
|
216
|
+
opts = {}
|
217
|
+
opts[:user_api_key] = @user_api_key if @user_api_key
|
218
|
+
opts[:account] = options[:as] if options[:as]
|
219
|
+
client = Gemfury::Client.new(opts)
|
220
|
+
client.user_agent = UserAgent
|
221
|
+
return client
|
222
|
+
end
|
223
|
+
|
224
|
+
def with_checks_and_rescues(&block)
|
225
|
+
@user_api_key = options[:api_token] if options[:api_token]
|
226
|
+
with_authorization(&block)
|
227
|
+
rescue Gemfury::InvalidGemVersion => e
|
228
|
+
shell.say "You have a deprecated Gemfury client", :red
|
229
|
+
if shell.yes? "Would you like to update it now? [yN]"
|
230
|
+
exec("gem update gemfury")
|
231
|
+
else
|
232
|
+
shell.say %q(No problem. You can also run "gem update gemfury")
|
233
|
+
end
|
234
|
+
rescue Gemfury::Conflict => e
|
235
|
+
die!("Oops! Locked for another user. Try again later.", e)
|
236
|
+
rescue Gemfury::Forbidden => e
|
237
|
+
die!("Oops! You're not allowed to access this", e)
|
238
|
+
rescue Gemfury::NotFound => e
|
239
|
+
die!("Oops! Doesn't look like this exists", e)
|
240
|
+
rescue Gemfury::Error => e
|
241
|
+
die!("Oops! %s" % e.message, e)
|
242
|
+
rescue StandardError => e
|
243
|
+
die!("Oops! Something went wrong. Please contact support.", e)
|
244
|
+
end
|
245
|
+
|
246
|
+
def push_files(command, gem_paths)
|
247
|
+
files = gem_paths.map do |g|
|
248
|
+
g.is_a?(String) ? File.new(g) : g rescue nil
|
249
|
+
end.compact
|
250
|
+
|
251
|
+
if files.empty?
|
252
|
+
die!("Problem: No valid packages found", nil, command)
|
253
|
+
end
|
254
|
+
|
255
|
+
push_options = { }
|
256
|
+
unless options[:public].nil?
|
257
|
+
push_options[:public] = options[:public]
|
258
|
+
end
|
259
|
+
|
260
|
+
error_ex = nil
|
261
|
+
|
262
|
+
# Let's get uploading
|
263
|
+
files.each do |file|
|
264
|
+
begin
|
265
|
+
shell.say "Uploading #{File.basename(file.path)} "
|
266
|
+
client.push_gem(file, push_options)
|
267
|
+
shell.say "- done"
|
268
|
+
rescue Gemfury::CorruptGemFile => e
|
269
|
+
shell.say "- problem processing this package", :red
|
270
|
+
error_ex = e
|
271
|
+
rescue Gemfury::DupeVersion => e
|
272
|
+
shell.say "- this version already exists", :red
|
273
|
+
error_ex = e
|
274
|
+
rescue Gemfury::TimeoutError, Errno::EPIPE => e
|
275
|
+
shell.say "- this file is too much to handle", :red
|
276
|
+
shell.say " Visit http://www.gemfury.com/large-package for more info"
|
277
|
+
error_ex = e
|
278
|
+
rescue => e
|
279
|
+
shell.say "- oops", :red
|
280
|
+
error_ex = e
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
unless error_ex.nil?
|
285
|
+
die!('There was a problem uploading at least 1 package', error_ex)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def die!(msg, err = nil, command = nil)
|
290
|
+
shell.say msg, :red
|
291
|
+
help(command) if command
|
292
|
+
shell.say %Q(#{err.class.name}: #{err}\n#{err.backtrace.join("\n")}) if err && ENV['DEBUG']
|
293
|
+
exit(1)
|
294
|
+
end
|
295
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Gemfury::Command::Authorization
|
2
|
+
include Gemfury::Platform
|
3
|
+
|
4
|
+
def wipe_credentials!
|
5
|
+
FileUtils.rm(config_path, :force => true) # never raises exception
|
6
|
+
each_netrc_host { |h| netrc_conf.delete(h) }
|
7
|
+
netrc_conf.save
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_credentials?
|
11
|
+
!!netrc_conf[netrc_api_host] ||
|
12
|
+
read_config_file.key?(:gemfury_api_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def with_authorization(&block)
|
17
|
+
# Load up the credentials if user_api_key is not already set
|
18
|
+
load_credentials! if @user_api_key.nil?
|
19
|
+
|
20
|
+
# Attempt the operation and prompt user in case of
|
21
|
+
# lack of authorization or a 401 response from the server
|
22
|
+
begin
|
23
|
+
prompt_credentials! if @user_api_key.nil?
|
24
|
+
block.call
|
25
|
+
rescue Gemfury::Unauthorized
|
26
|
+
if acct = client.account
|
27
|
+
shell.say %Q(Oops! You don't have access to "#{acct}"), :red
|
28
|
+
else
|
29
|
+
shell.say "Oops! Authentication failure.", :red
|
30
|
+
@user_api_key = nil
|
31
|
+
retry
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def prompt_credentials!
|
37
|
+
# Prompt credentials
|
38
|
+
highline = HighLine.new
|
39
|
+
highline.say 'Please enter your Gemfury credentials.'
|
40
|
+
email = highline.ask('Email: ')
|
41
|
+
passw = highline.ask('Password: ') { |q| q.echo = false }
|
42
|
+
|
43
|
+
# Request and save the API access token
|
44
|
+
if !email.empty? && !passw.empty?
|
45
|
+
@user_api_key = client.get_access_token(email, passw)
|
46
|
+
write_credentials!(email)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_credentials!
|
51
|
+
# Get credentials from ~/.netrc
|
52
|
+
_, @user_api_key = netrc_conf[netrc_api_host]
|
53
|
+
# Legacy loading from ~/.gem/gemfury
|
54
|
+
@user_api_key ||= read_config_file[:gemfury_api_key]
|
55
|
+
end
|
56
|
+
|
57
|
+
def write_credentials!(email)
|
58
|
+
each_netrc_host { |h| netrc_conf[h] = email, @user_api_key }
|
59
|
+
netrc_conf.save
|
60
|
+
end
|
61
|
+
|
62
|
+
def read_config_file
|
63
|
+
File.exist?(config_path) ? YAML.load_file(config_path) : {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def netrc_conf
|
67
|
+
@netrc ||= Netrc.read
|
68
|
+
end
|
69
|
+
|
70
|
+
def netrc_api_host
|
71
|
+
URI.parse(client.endpoint).host
|
72
|
+
end
|
73
|
+
|
74
|
+
def each_netrc_host
|
75
|
+
[:endpoint, :gitpoint].each do |c|
|
76
|
+
yield(URI.parse(client.send(c)).host)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Gemfury
|
2
|
+
# Defines constants and methods related to configuration
|
3
|
+
module Configuration
|
4
|
+
# An array of valid keys in the options hash when configuring
|
5
|
+
VALID_OPTIONS_KEYS = [
|
6
|
+
:user_api_key,
|
7
|
+
:adapter,
|
8
|
+
:endpoint,
|
9
|
+
:gitpoint,
|
10
|
+
:pushpoint,
|
11
|
+
:user_agent,
|
12
|
+
:api_version,
|
13
|
+
:account].freeze
|
14
|
+
|
15
|
+
# The adapter that will be used to connect if none is set
|
16
|
+
DEFAULT_ADAPTER = :net_http
|
17
|
+
|
18
|
+
# The endpoint that will be used to connect if none is set
|
19
|
+
DEFAULT_ENDPOINT = 'https://api.fury.io/'.freeze
|
20
|
+
|
21
|
+
# The HTTP endpoint for git repo (used for .netrc credentials)
|
22
|
+
DEFAULT_GITPOINT = 'https://git.fury.io/'.freeze
|
23
|
+
|
24
|
+
# The endpoint for the Push API if not set
|
25
|
+
DEFAULT_PUSHPOINT = 'https://push.fury.io/'.freeze
|
26
|
+
|
27
|
+
# The value sent in the 'User-Agent' header if none is set
|
28
|
+
DEFAULT_USER_AGENT = "Gemfury RubyGem #{Gemfury::VERSION}".freeze
|
29
|
+
|
30
|
+
# Default API version
|
31
|
+
DEFAULT_API_VERSION = 1
|
32
|
+
|
33
|
+
# Default user API key
|
34
|
+
DEFAULT_API_KEY = nil
|
35
|
+
|
36
|
+
# Use the current account (no impersonation)
|
37
|
+
DEFAULT_ACCOUNT = nil
|
38
|
+
|
39
|
+
# @private
|
40
|
+
attr_accessor *VALID_OPTIONS_KEYS
|
41
|
+
|
42
|
+
# When this module is extended, set all configuration options to their default values
|
43
|
+
def self.extended(base)
|
44
|
+
base.reset
|
45
|
+
end
|
46
|
+
|
47
|
+
# Convenience method to allow configuration options to be set in a block
|
48
|
+
def configure
|
49
|
+
yield self
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create a hash of options and their values
|
53
|
+
def options
|
54
|
+
options = {}
|
55
|
+
VALID_OPTIONS_KEYS.each{|k| options[k] = send(k)}
|
56
|
+
options
|
57
|
+
end
|
58
|
+
|
59
|
+
# Reset all configuration options to defaults
|
60
|
+
def reset
|
61
|
+
self.user_api_key = DEFAULT_API_KEY
|
62
|
+
self.adapter = DEFAULT_ADAPTER
|
63
|
+
self.endpoint = DEFAULT_ENDPOINT
|
64
|
+
self.gitpoint = DEFAULT_GITPOINT
|
65
|
+
self.pushpoint = DEFAULT_PUSHPOINT
|
66
|
+
self.user_agent = DEFAULT_USER_AGENT
|
67
|
+
self.api_version = DEFAULT_API_VERSION
|
68
|
+
self.account = DEFAULT_ACCOUNT
|
69
|
+
self
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Gemfury
|
2
|
+
module Const
|
3
|
+
class << self
|
4
|
+
def host
|
5
|
+
'www.gemfury.com'
|
6
|
+
#'localhost:3000'
|
7
|
+
end
|
8
|
+
|
9
|
+
def welcome
|
10
|
+
"Welcome to Gemfury!\nPlease complete the following information"
|
11
|
+
end
|
12
|
+
|
13
|
+
def email_error
|
14
|
+
"Invalid email address. Please try again."
|
15
|
+
end
|
16
|
+
|
17
|
+
def email_regex
|
18
|
+
return @email_regex if @email_regex
|
19
|
+
email_name_regex = '[A-Z0-9_\.%\+\-\']+'
|
20
|
+
domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
|
21
|
+
domain_tld_regex = '(?:[A-Z]{2,4}|museum|travel)'
|
22
|
+
@email_regex = /^#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}$/i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Gemfury
|
2
|
+
# Base Error class
|
3
|
+
Error = Class.new(StandardError)
|
4
|
+
|
5
|
+
# The Gemfury gem version doesn't match the one on the server
|
6
|
+
InvalidGemVersion = Class.new(Error)
|
7
|
+
|
8
|
+
# Client#user_api_key is not defined or Gemfury returns 401
|
9
|
+
Unauthorized = Class.new(Error)
|
10
|
+
|
11
|
+
# Client is not allowed to perform this operation
|
12
|
+
Forbidden = Class.new(Error)
|
13
|
+
|
14
|
+
# Account is locked for another operation
|
15
|
+
Conflict = Class.new(Error)
|
16
|
+
|
17
|
+
# Returned if something is not found
|
18
|
+
NotFound = Class.new(Error)
|
19
|
+
|
20
|
+
# Corrupt Gem File
|
21
|
+
CorruptGemFile = Class.new(Error)
|
22
|
+
|
23
|
+
# Version already exists
|
24
|
+
DupeVersion = Class.new(Error)
|
25
|
+
|
26
|
+
# TimeoutError for 503s
|
27
|
+
TimeoutError = Class.new(Error)
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Gemfury
|
2
|
+
module Platform
|
3
|
+
def home_directory
|
4
|
+
on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
|
5
|
+
end
|
6
|
+
|
7
|
+
def config_path
|
8
|
+
File.expand_path('.gem/gemfury', home_directory)
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_windows?
|
12
|
+
RUBY_PLATFORM =~ /mswin32|mingw32/
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_mac?
|
16
|
+
RUBY_PLATFORM =~ /-darwin\d/
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
require 'gemfury'
|
3
|
+
require 'gemfury/command'
|
4
|
+
|
5
|
+
namespace 'fury' do
|
6
|
+
desc "Build gem and push it to Gemfury"
|
7
|
+
task :release, [:gemspec, :as] do |t, args|
|
8
|
+
gemspec = args[:gemspec] ||
|
9
|
+
FileList["#{Dir.pwd}/*.gemspec"][0]
|
10
|
+
|
11
|
+
if gemspec.nil? || !File.exist?(gemspec)
|
12
|
+
puts "No gemspec found"
|
13
|
+
else
|
14
|
+
puts "Building #{File.basename(gemspec)}"
|
15
|
+
spec = Gem::Specification.load(gemspec)
|
16
|
+
|
17
|
+
if Gem::Package.respond_to?(:build)
|
18
|
+
Gem::Package.build(spec)
|
19
|
+
else
|
20
|
+
require 'rubygems/builder'
|
21
|
+
Gem::Builder.new(spec).build
|
22
|
+
end
|
23
|
+
|
24
|
+
gemfile = File.basename(spec.cache_file)
|
25
|
+
|
26
|
+
params = ['push', gemfile]
|
27
|
+
params << "--as=#{args[:as]}" if args[:as]
|
28
|
+
|
29
|
+
Gemfury::Command::App.start(params)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
namespace 'gemfury' do
|
35
|
+
task :release => 'fury:release'
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'gemfury'
|
2
|
+
require 'gemfury/command'
|
3
|
+
|
4
|
+
class Gem::Commands::FuryCommand < Gem::Command
|
5
|
+
def description
|
6
|
+
'Push a private gem to your Gemfury account'
|
7
|
+
end
|
8
|
+
|
9
|
+
def arguments
|
10
|
+
"GEM built gem file to push"
|
11
|
+
end
|
12
|
+
|
13
|
+
def usage
|
14
|
+
"#{program_name} GEM"
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
super 'fury', description
|
19
|
+
add_option('-a', '--as USERNAME', 'Impersonate another account') do |value, options|
|
20
|
+
options[:as] = value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute
|
25
|
+
opts = options.dup
|
26
|
+
args = opts.delete(:args)
|
27
|
+
Gemfury::Command::App.send(:dispatch, "push", args, opts, {})
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gemfury
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.10.0.rc1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Rykov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-03-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: multi_json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.14.0
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 1.0.0.pre
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.14.0
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.0.0.pre
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: netrc
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.10.0
|
54
|
+
- - "<"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.12.0.pre
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.10.0
|
64
|
+
- - "<"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 0.12.0.pre
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: faraday
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 0.9.0
|
74
|
+
- - "<"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.16.0.pre
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 0.9.0
|
84
|
+
- - "<"
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 0.16.0.pre
|
87
|
+
- !ruby/object:Gem::Dependency
|
88
|
+
name: highline
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.6.0
|
94
|
+
- - "<"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.1.0.pre
|
97
|
+
type: :runtime
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.6.0
|
104
|
+
- - "<"
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 2.1.0.pre
|
107
|
+
description: 'Hosted repo for your public and private packages at https://gemfury.com
|
108
|
+
|
109
|
+
'
|
110
|
+
email: hello@gemfury.com
|
111
|
+
executables:
|
112
|
+
- gemfury
|
113
|
+
- fury
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- README.md
|
118
|
+
- bin/fury
|
119
|
+
- bin/gemfury
|
120
|
+
- lib/faraday/adapter/fury_http.rb
|
121
|
+
- lib/faraday/request/multipart_with_file.rb
|
122
|
+
- lib/gemfury.rb
|
123
|
+
- lib/gemfury/client.rb
|
124
|
+
- lib/gemfury/client/filters.rb
|
125
|
+
- lib/gemfury/client/middleware.rb
|
126
|
+
- lib/gemfury/command.rb
|
127
|
+
- lib/gemfury/command/app.rb
|
128
|
+
- lib/gemfury/command/authorization.rb
|
129
|
+
- lib/gemfury/configuration.rb
|
130
|
+
- lib/gemfury/const.rb
|
131
|
+
- lib/gemfury/error.rb
|
132
|
+
- lib/gemfury/platform.rb
|
133
|
+
- lib/gemfury/tasks.rb
|
134
|
+
- lib/gemfury/tasks/release.rake
|
135
|
+
- lib/gemfury/version.rb
|
136
|
+
- lib/rubygems/commands/fury_command.rb
|
137
|
+
- lib/rubygems_plugin.rb
|
138
|
+
homepage: https://gemfury.com
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
metadata: {}
|
142
|
+
post_install_message: |
|
143
|
+
************************************************************************
|
144
|
+
|
145
|
+
Upload your first package to start using Gemfury:
|
146
|
+
fury push my-package-1.0.0.gem
|
147
|
+
|
148
|
+
If you have a directory with packages, you can use:
|
149
|
+
fury migrate ./path/to/codez
|
150
|
+
|
151
|
+
Find out what else you can do:
|
152
|
+
fury help
|
153
|
+
|
154
|
+
Follow @gemfury on Twitter for announcements, updates, and news.
|
155
|
+
https://twitter.com/gemfury
|
156
|
+
|
157
|
+
************************************************************************
|
158
|
+
rdoc_options: []
|
159
|
+
require_paths:
|
160
|
+
- lib
|
161
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - ">"
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: 1.3.1
|
171
|
+
requirements: []
|
172
|
+
rubyforge_project:
|
173
|
+
rubygems_version: 2.7.8
|
174
|
+
signing_key:
|
175
|
+
specification_version: 4
|
176
|
+
summary: Hosted repo for your public and private packages
|
177
|
+
test_files: []
|