gel 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +74 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +39 -0
  5. data/exe/gel +13 -0
  6. data/lib/gel.rb +22 -0
  7. data/lib/gel/catalog.rb +153 -0
  8. data/lib/gel/catalog/common.rb +82 -0
  9. data/lib/gel/catalog/compact_index.rb +152 -0
  10. data/lib/gel/catalog/dependency_index.rb +125 -0
  11. data/lib/gel/catalog/legacy_index.rb +157 -0
  12. data/lib/gel/catalog/marshal_hacks.rb +16 -0
  13. data/lib/gel/command.rb +86 -0
  14. data/lib/gel/command/config.rb +11 -0
  15. data/lib/gel/command/env.rb +7 -0
  16. data/lib/gel/command/exec.rb +66 -0
  17. data/lib/gel/command/help.rb +7 -0
  18. data/lib/gel/command/install.rb +7 -0
  19. data/lib/gel/command/install_gem.rb +16 -0
  20. data/lib/gel/command/lock.rb +34 -0
  21. data/lib/gel/command/ruby.rb +10 -0
  22. data/lib/gel/command/shell_setup.rb +25 -0
  23. data/lib/gel/command/stub.rb +12 -0
  24. data/lib/gel/command/update.rb +11 -0
  25. data/lib/gel/compatibility.rb +4 -0
  26. data/lib/gel/compatibility/bundler.rb +54 -0
  27. data/lib/gel/compatibility/bundler/cli.rb +6 -0
  28. data/lib/gel/compatibility/bundler/friendly_errors.rb +3 -0
  29. data/lib/gel/compatibility/bundler/setup.rb +4 -0
  30. data/lib/gel/compatibility/rubygems.rb +192 -0
  31. data/lib/gel/compatibility/rubygems/command.rb +4 -0
  32. data/lib/gel/compatibility/rubygems/dependency_installer.rb +0 -0
  33. data/lib/gel/compatibility/rubygems/gem_runner.rb +6 -0
  34. data/lib/gel/config.rb +80 -0
  35. data/lib/gel/db.rb +294 -0
  36. data/lib/gel/direct_gem.rb +29 -0
  37. data/lib/gel/environment.rb +592 -0
  38. data/lib/gel/error.rb +104 -0
  39. data/lib/gel/gemfile_parser.rb +144 -0
  40. data/lib/gel/gemspec_parser.rb +95 -0
  41. data/lib/gel/git_catalog.rb +38 -0
  42. data/lib/gel/git_depot.rb +119 -0
  43. data/lib/gel/httpool.rb +148 -0
  44. data/lib/gel/installer.rb +251 -0
  45. data/lib/gel/lock_loader.rb +164 -0
  46. data/lib/gel/lock_parser.rb +64 -0
  47. data/lib/gel/locked_store.rb +126 -0
  48. data/lib/gel/multi_store.rb +96 -0
  49. data/lib/gel/package.rb +156 -0
  50. data/lib/gel/package/inspector.rb +23 -0
  51. data/lib/gel/package/installer.rb +267 -0
  52. data/lib/gel/path_catalog.rb +44 -0
  53. data/lib/gel/pinboard.rb +140 -0
  54. data/lib/gel/pub_grub/preference_strategy.rb +82 -0
  55. data/lib/gel/pub_grub/source.rb +153 -0
  56. data/lib/gel/runtime.rb +27 -0
  57. data/lib/gel/store.rb +205 -0
  58. data/lib/gel/store_catalog.rb +31 -0
  59. data/lib/gel/store_gem.rb +80 -0
  60. data/lib/gel/stub_set.rb +51 -0
  61. data/lib/gel/support/gem_platform.rb +225 -0
  62. data/lib/gel/support/gem_requirement.rb +264 -0
  63. data/lib/gel/support/gem_version.rb +398 -0
  64. data/lib/gel/support/tar.rb +13 -0
  65. data/lib/gel/support/tar/tar_header.rb +229 -0
  66. data/lib/gel/support/tar/tar_reader.rb +123 -0
  67. data/lib/gel/support/tar/tar_reader/entry.rb +154 -0
  68. data/lib/gel/support/tar/tar_writer.rb +339 -0
  69. data/lib/gel/tail_file.rb +205 -0
  70. data/lib/gel/version.rb +5 -0
  71. data/lib/gel/work_pool.rb +143 -0
  72. data/man/man1/gel-exec.1 +16 -0
  73. data/man/man1/gel-install.1 +16 -0
  74. data/man/man1/gel.1 +30 -0
  75. metadata +131 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4b6c69f6e2a7fa19983fee2c4dcf3cc803b34126b98e8fb57df4f7da155736a7
4
+ data.tar.gz: 57441eef503b6a6e7b0d3a901ab91306ada2957336aa5734328bd56fdb0da065
5
+ SHA512:
6
+ metadata.gz: eca5ccd35a08b25914a57c2dd691bd3adf483b2f6433c751e8e9db44f4e0dcf4fa3bc3206876c447da247cf769190966dd11da46a074af4f7887b2dcfbfe8626
7
+ data.tar.gz: 5aee75b8749c9d7136b50e38b1a9f9c0624e73e20c154628330e0f07691e5f76f2f05d67fe6ab96effeb02793ea3cbabd0b885a56452e5c1754a8714aa082ee3
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at team@gel.dev. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Gel Authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ <p align="center"><a href="https://gel.dev"><img src="https://gel.dev/images/gel.svg" width="150" /></a></p>
2
+
3
+ # Gel
4
+
5
+ A modern gem manager.
6
+
7
+ ## Installation
8
+
9
+ Install the gem:
10
+
11
+ $ gem install gel
12
+
13
+ Then, either activate gel in your current shell:
14
+
15
+ $ eval "$(gel shell-setup)"
16
+
17
+ Or add it to your `.bashrc` or `.zshrc` to enable it everywhere:
18
+
19
+ $ echo 'eval "$(gel shell-setup)"' >> ~/.bashrc
20
+
21
+ ## Usage
22
+
23
+ Use `gel install`, `gel lock`, and `gel exec` as you would the equivalent `bundle` subcommands.
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake test` to run the tests.
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gel-rb/gel. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
32
+
33
+ ## License
34
+
35
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
36
+
37
+ ## Code of Conduct
38
+
39
+ Everyone interacting in the Gel project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/gel-rb/gel/blob/master/CODE_OF_CONDUCT.md).
data/exe/gel ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby --disable=gems
2
+ # frozen_string_literal: true
3
+
4
+ if defined?(::Gem) && !defined?(::Gel)
5
+ exec ::Gem.ruby,
6
+ "-I", File.expand_path("../lib/gel/compatibility", __dir__),
7
+ "--",
8
+ __FILE__,
9
+ *ARGV
10
+ end
11
+
12
+ require_relative "../lib/gel/command"
13
+ Gel::Command.run(ARGV)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gel
4
+ module Support
5
+ end
6
+ end
7
+
8
+ require_relative "gel/support/gem_version"
9
+ require_relative "gel/support/gem_requirement"
10
+
11
+ require_relative "gel/config"
12
+ require_relative "gel/environment"
13
+ require_relative "gel/store"
14
+ require_relative "gel/store_gem"
15
+ require_relative "gel/direct_gem"
16
+ require_relative "gel/locked_store"
17
+ require_relative "gel/multi_store"
18
+
19
+ require_relative "gel/gemspec_parser"
20
+ require_relative "gel/gemfile_parser"
21
+ require_relative "gel/lock_parser"
22
+ require_relative "gel/lock_loader"
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "net/http"
5
+ require "uri"
6
+ require "digest"
7
+
8
+ require_relative "httpool"
9
+ require_relative "support/gem_platform"
10
+
11
+ class Gel::Catalog
12
+ UPDATE_CONCURRENCY = 8
13
+
14
+ def initialize(uri, httpool: Gel::Httpool.new, work_pool:, cache: ENV["GEL_CACHE"] || "~/.cache/gel", initial_gems: [])
15
+ @uri = normalize_uri(uri)
16
+ @httpool = httpool
17
+ @work_pool = work_pool
18
+ @cache = cache
19
+ @initial_gems = initial_gems
20
+
21
+ @indexes = [
22
+ :compact_index,
23
+ :dependency_index,
24
+ :legacy_index,
25
+ ]
26
+ end
27
+
28
+ def prepare
29
+ index.prepare(@initial_gems)
30
+ rescue Net::HTTPExceptions
31
+ if @indexes.size > 1
32
+ @indexes.shift
33
+ retry
34
+ else
35
+ raise
36
+ end
37
+ end
38
+
39
+ def compact_index
40
+ @compact_index ||= Gel::Catalog::CompactIndex.new(@uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
41
+ end
42
+
43
+ def dependency_index
44
+ @dependency_index ||= Gel::Catalog::DependencyIndex.new(self, @uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
45
+ end
46
+
47
+ def legacy_index
48
+ @legacy_index ||= Gel::Catalog::LegacyIndex.new(@uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
49
+ end
50
+
51
+ def index
52
+ send(@indexes.first)
53
+ end
54
+
55
+ def gem_info(name)
56
+ index.gem_info(name)
57
+ rescue Net::HTTPExceptions
58
+ if @indexes.size > 1
59
+ @indexes.shift
60
+ retry
61
+ else
62
+ raise
63
+ end
64
+ end
65
+
66
+ def cached_gem(name, version)
67
+ path = cache_path(name, version)
68
+ return path if File.exist?(path)
69
+ end
70
+
71
+ def download_gem(name, version)
72
+ path = cache_path(name, version)
73
+ return path if File.exist?(path)
74
+
75
+ name, version = guess_version(name, version)
76
+
77
+ response = http_get("/gems/#{name}-#{version}.gem")
78
+ FileUtils.mkdir_p(cache_dir) unless Dir.exist?(cache_dir)
79
+ File.open(path, "wb") do |f|
80
+ f.write(response.body)
81
+ end
82
+ path
83
+ end
84
+
85
+ VARIANT_GEMS = %w(libv8)
86
+ def guess_version(name, version)
87
+ if VARIANT_GEMS.include?(name)
88
+ [name, "#{version}-#{platform_specific_version}"]
89
+ else
90
+ [name, version]
91
+ end
92
+ end
93
+
94
+ def platform_specific_version
95
+ Gel::Support::GemPlatform.new(RbConfig::CONFIG["arch"])
96
+ end
97
+
98
+ def inspect
99
+ "#<#{self.class} #{to_s.inspect}>"
100
+ end
101
+
102
+ def to_s
103
+ @uri.to_s
104
+ end
105
+
106
+ private
107
+
108
+ def normalize_uri(uri)
109
+ uri = URI(uri).dup
110
+ uri.scheme = uri.scheme.downcase
111
+ uri.host = uri.host.downcase
112
+ if auth = Gel::Environment.config[uri.host]
113
+ uri.userinfo = auth
114
+ end
115
+ uri.path = "/" if uri.path == ""
116
+ uri
117
+ end
118
+
119
+ def uri_identifier
120
+ @uri.host + "-" + Digest(:SHA256).hexdigest(@uri.to_s)[0..10]
121
+ end
122
+
123
+ def cache_dir
124
+ File.expand_path("#{@cache}/gems/#{uri_identifier}")
125
+ end
126
+
127
+ def cache_path(name, version)
128
+ File.join(cache_dir, "#{name}-#{version}.gem")
129
+ end
130
+
131
+ def http_get(path)
132
+ original_uri = uri = URI(File.join(@uri.to_s, path))
133
+
134
+ 5.times do
135
+ response = @httpool.request(uri)
136
+
137
+ case response
138
+ when Net::HTTPRedirection
139
+ uri = URI(response["Location"])
140
+ next
141
+ else
142
+ response.value
143
+ return response
144
+ end
145
+ end
146
+
147
+ raise "Too many redirects for #{original_uri}"
148
+ end
149
+ end
150
+
151
+ require_relative "catalog/compact_index"
152
+ require_relative "catalog/dependency_index"
153
+ require_relative "catalog/legacy_index"
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "monitor"
5
+
6
+ module Gel::Catalog::Common
7
+ def initialize(uri, uri_identifier, httpool:, work_pool:, cache:)
8
+ @uri = uri
9
+ @uri_identifier = uri_identifier
10
+ @httpool = httpool
11
+ @work_pool = work_pool
12
+ @cache = cache
13
+
14
+ @pinboard = nil
15
+
16
+ @monitor = Monitor.new
17
+ @refresh_cond = @monitor.new_cond
18
+
19
+ @done_refresh = {}
20
+
21
+ @gem_info = {}
22
+ @error = nil
23
+ end
24
+
25
+ def gem_info(gem_name)
26
+ gems_to_refresh = []
27
+
28
+ @monitor.synchronize do
29
+ if info = _info(gem_name)
30
+ gems_to_refresh = walk_gem_dependencies(gem_name, info)
31
+ return info
32
+ end
33
+ end
34
+
35
+ refresh_gem gem_name, true
36
+
37
+ @monitor.synchronize do
38
+ info = nil
39
+ @refresh_cond.wait_until { info = _info(gem_name) }
40
+ gems_to_refresh = walk_gem_dependencies(gem_name, info)
41
+ info
42
+ end
43
+ ensure
44
+ gems_to_refresh.each do |dep_name|
45
+ refresh_gem dep_name
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def walk_gem_dependencies(gem_name, info)
52
+ unless @done_refresh[gem_name]
53
+ gems_to_refresh = info.values.flat_map { |v| v[:dependencies] }.map(&:first).uniq
54
+ @done_refresh[gem_name] = true
55
+ end
56
+
57
+ gems_to_refresh || []
58
+ end
59
+
60
+ def _info(name)
61
+ raise @error if @error
62
+ if i = @gem_info[name]
63
+ raise i if i.is_a?(Exception)
64
+ i
65
+ end
66
+ end
67
+
68
+ def pinboard
69
+ @pinboard || @monitor.synchronize do
70
+ @pinboard ||=
71
+ begin
72
+ pinboard_dir = File.expand_path("#{@cache}/#{self.class::CACHE_TYPE}/#{@uri_identifier}")
73
+ FileUtils.mkdir_p(pinboard_dir) unless Dir.exist?(pinboard_dir)
74
+ Gel::Pinboard.new(pinboard_dir, httpool: @httpool, work_pool: @work_pool)
75
+ end
76
+ end
77
+ end
78
+
79
+ def uri(*parts)
80
+ URI(File.join(@uri.to_s, *parts))
81
+ end
82
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ require_relative "../pinboard"
6
+
7
+ require_relative "common"
8
+
9
+ class Gel::Catalog::CompactIndex
10
+ include Gel::Catalog::Common
11
+ CACHE_TYPE = "index"
12
+
13
+ def initialize(*)
14
+ super
15
+
16
+ @gem_tokens = Hash.new("NONE")
17
+ @needs_update = true
18
+ @updating = false
19
+ @active_gems = Set.new
20
+ @pending_gems = Set.new
21
+ end
22
+
23
+ def prepare(gems)
24
+ @monitor.synchronize do
25
+ @pending_gems.merge(gems)
26
+ end
27
+ update
28
+ @monitor.synchronize do
29
+ @refresh_cond.wait_until { gems.all? { |g| _info(g) } }
30
+ end
31
+ end
32
+
33
+ def update
34
+ @monitor.synchronize do
35
+ return false unless @needs_update
36
+ @needs_update = false
37
+ @updating = true
38
+ end
39
+
40
+ error = lambda do |ex|
41
+ @monitor.synchronize do
42
+ @error = ex
43
+ @refresh_cond.broadcast
44
+ end
45
+ end
46
+
47
+ pinboard.async_file(uri("versions"), tail: true, error: error) do |f|
48
+ new_tokens = {}
49
+
50
+ started = false
51
+ f.each_line do |line|
52
+ unless started
53
+ started ||= line == "---\n"
54
+ next
55
+ end
56
+ line.chop!
57
+
58
+ name, _versions, token = line.split
59
+ new_tokens[name] = token
60
+ end
61
+
62
+ @monitor.synchronize do
63
+ @gem_tokens.update new_tokens
64
+ @updating = false
65
+ @refresh_cond.broadcast
66
+ end
67
+
68
+ (@active_gems | @pending_gems).each do |name|
69
+ refresh_gem(name)
70
+ end
71
+ end
72
+
73
+ true
74
+ end
75
+
76
+ def refresh_gem(gem_name, immediate = true)
77
+ update
78
+
79
+ already_active = nil
80
+ @monitor.synchronize do
81
+ if @updating && !@gem_tokens.key?(gem_name)
82
+ @pending_gems << gem_name
83
+ return
84
+ end
85
+
86
+ already_active = !@active_gems.add?(gem_name)
87
+ end
88
+
89
+ unless @gem_tokens.key?(gem_name)
90
+ @monitor.synchronize do
91
+ @gem_info[gem_name] = {}
92
+ @refresh_cond.broadcast
93
+ return
94
+ end
95
+ end
96
+
97
+ error = lambda do |ex|
98
+ @monitor.synchronize do
99
+ @gem_info[gem_name] = ex
100
+ @refresh_cond.broadcast
101
+ end
102
+ end
103
+
104
+ pinboard.async_file(uri("info", gem_name), token: @gem_tokens[gem_name], only_updated: already_active, error: error) do |f|
105
+ dependency_names = Set.new
106
+ info = {}
107
+
108
+ started = false
109
+ f.each_line do |line|
110
+ unless started
111
+ started ||= line == "---\n"
112
+ next
113
+ end
114
+ line.chop!
115
+
116
+ version, rest = line.split(" ", 2)
117
+ deps, attrs = rest.split("|", 2)
118
+
119
+ deps = deps.split(",").map do |entry|
120
+ key, constraints = entry.split(":", 2)
121
+ constraints = constraints.split("&")
122
+ [key, constraints]
123
+ end
124
+
125
+ attributes = { dependencies: deps }
126
+ attrs.scan(/(\w+):((?:[^,]+|,(?!\w+:))*)/) do |key, value|
127
+ attributes[key.to_sym] = value
128
+ end
129
+
130
+ deps.each do |name, _|
131
+ dependency_names << name
132
+ end
133
+
134
+ info[version] = attributes
135
+ end
136
+
137
+ @monitor.synchronize do
138
+ @gem_info[gem_name] = info
139
+ @refresh_cond.broadcast
140
+ end
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ def uri(*)
147
+ super.tap do |uri|
148
+ # https://github.com/rubygems/rubygems.org/issues/1698#issuecomment-348744676 ¯\_(ツ)_/¯
149
+ uri.host = "index.rubygems.org" if uri.host.downcase == "rubygems.org"
150
+ end
151
+ end
152
+ end