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.
@@ -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
@@ -0,0 +1,62 @@
1
+ Gemfury CLI
2
+ ===========
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/gemfury.svg)](http://badge.fury.io/rb/gemfury)
5
+ [![Build Status](https://secure.travis-ci.org/gemfury/gemfury.svg?branch=master)](https://travis-ci.org/gemfury/gemfury)
6
+ [![Code Climate](https://codeclimate.com/github/gemfury/gemfury/badges/gpa.svg)](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.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'rubygems'
7
+ require 'gemfury'
8
+ require 'gemfury/command'
9
+
10
+ Gemfury::Command::App.start
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ exec(File.expand_path('../fury', __FILE__), *ARGV)
@@ -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
@@ -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,2 @@
1
+ # Load Gemfury rakefile extensions
2
+ load "gemfury/tasks/release.rake"
@@ -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,3 @@
1
+ module Gemfury
2
+ VERSION = '0.10.0.rc1'
3
+ 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
@@ -0,0 +1,2 @@
1
+ require 'rubygems/command_manager'
2
+ Gem::CommandManager.instance.register_command :fury
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: []