gel 0.3.0 → 0.8.0.pre1

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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -3
  3. data/RELEASING.md +12 -0
  4. data/exe/gel +4 -2
  5. data/gemlib/gel/stub.rb +20 -0
  6. data/lib/gel/catalog/common.rb +4 -2
  7. data/lib/gel/catalog/compact_index.rb +6 -10
  8. data/lib/gel/catalog/dependency_index.rb +10 -10
  9. data/lib/gel/catalog/legacy_index.rb +4 -6
  10. data/lib/gel/catalog/marshal_hacks.rb +2 -0
  11. data/lib/gel/catalog.rb +33 -52
  12. data/lib/gel/catalog_set.rb +100 -0
  13. data/lib/gel/command/help.rb +13 -2
  14. data/lib/gel/command/lock.rb +3 -3
  15. data/lib/gel/command/open.rb +24 -0
  16. data/lib/gel/command/shell_setup.rb +11 -8
  17. data/lib/gel/command/stub.rb +45 -2
  18. data/lib/gel/command/version.rb +7 -0
  19. data/lib/gel/command.rb +43 -6
  20. data/lib/gel/compatibility/rubygems.rb +10 -197
  21. data/lib/gel/compatibility.rb +2 -2
  22. data/lib/gel/config.rb +41 -7
  23. data/lib/gel/db.rb +93 -83
  24. data/lib/gel/direct_gem.rb +16 -4
  25. data/lib/gel/environment.rb +542 -249
  26. data/lib/gel/error.rb +156 -24
  27. data/lib/gel/gemfile_parser.rb +74 -12
  28. data/lib/gel/gemspec_parser.rb +26 -7
  29. data/lib/gel/git_catalog.rb +15 -3
  30. data/lib/gel/git_depot.rb +62 -28
  31. data/lib/gel/httpool.rb +5 -2
  32. data/lib/gel/installer.rb +61 -23
  33. data/lib/gel/lock_loader.rb +87 -112
  34. data/lib/gel/lock_parser.rb +23 -31
  35. data/lib/gel/locked_store.rb +30 -21
  36. data/lib/gel/multi_store.rb +13 -4
  37. data/lib/gel/null_solver.rb +67 -0
  38. data/lib/gel/package/abortable.rb +18 -0
  39. data/lib/gel/package/installer.rb +124 -49
  40. data/lib/gel/package.rb +21 -4
  41. data/lib/gel/path_catalog.rb +1 -1
  42. data/lib/gel/pinboard.rb +4 -2
  43. data/lib/gel/platform.rb +38 -0
  44. data/lib/gel/pub_grub/package.rb +67 -0
  45. data/lib/gel/pub_grub/preference_strategy.rb +10 -6
  46. data/lib/gel/pub_grub/solver.rb +37 -0
  47. data/lib/gel/pub_grub/source.rb +64 -92
  48. data/lib/gel/resolved_gem_set.rb +234 -0
  49. data/lib/gel/runtime.rb +3 -3
  50. data/lib/gel/set.rb +62 -0
  51. data/lib/gel/stdlib.rb +83 -0
  52. data/lib/gel/store.rb +94 -25
  53. data/lib/gel/store_catalog.rb +2 -2
  54. data/lib/gel/store_gem.rb +54 -6
  55. data/lib/gel/stub_set.rb +32 -2
  56. data/lib/gel/support/cgi_escape.rb +34 -0
  57. data/lib/gel/support/gem_platform.rb +0 -2
  58. data/lib/gel/support/sha512.rb +142 -0
  59. data/lib/gel/support/tar/tar_writer.rb +2 -2
  60. data/lib/gel/tail_file.rb +2 -1
  61. data/lib/gel/util.rb +108 -0
  62. data/lib/gel/vendor/pstore.rb +3 -0
  63. data/lib/gel/vendor/pub_grub.rb +3 -0
  64. data/lib/gel/vendor/ruby_digest.rb +3 -0
  65. data/lib/gel/vendor_catalog.rb +38 -0
  66. data/lib/gel/version.rb +1 -1
  67. data/lib/gel.rb +15 -0
  68. data/man/man1/gel-exec.1 +1 -1
  69. data/man/man1/gel-install.1 +1 -1
  70. data/man/man1/gel.1 +14 -1
  71. data/{lib/gel/compatibility → slib}/bundler/cli.rb +0 -0
  72. data/{lib/gel/compatibility → slib}/bundler/friendly_errors.rb +0 -0
  73. data/{lib/gel/compatibility/rubygems/dependency_installer.rb → slib/bundler/gem_helper.rb} +0 -0
  74. data/slib/bundler/gem_tasks.rb +0 -0
  75. data/{lib/gel/compatibility → slib}/bundler/setup.rb +0 -0
  76. data/{lib/gel/compatibility → slib}/bundler.rb +39 -3
  77. data/{lib/gel/compatibility → slib}/rubygems/command.rb +0 -0
  78. data/slib/rubygems/dependency_installer.rb +12 -0
  79. data/{lib/gel/compatibility → slib}/rubygems/gem_runner.rb +0 -0
  80. data/slib/rubygems/package.rb +6 -0
  81. data/slib/rubygems/package_task.rb +7 -0
  82. data/slib/rubygems/specification.rb +0 -0
  83. data/slib/rubygems/version.rb +0 -0
  84. data/slib/rubygems.rb +297 -0
  85. data/vendor/pstore/LICENSE.txt +22 -0
  86. data/vendor/pstore/lib/pstore.rb +488 -0
  87. data/vendor/pub_grub/LICENSE.txt +21 -0
  88. data/vendor/pub_grub/lib/pub_grub/assignment.rb +20 -0
  89. data/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +183 -0
  90. data/vendor/pub_grub/lib/pub_grub/failure_writer.rb +182 -0
  91. data/vendor/pub_grub/lib/pub_grub/incompatibility.rb +143 -0
  92. data/vendor/pub_grub/lib/pub_grub/package.rb +35 -0
  93. data/vendor/pub_grub/lib/pub_grub/partial_solution.rb +121 -0
  94. data/vendor/pub_grub/lib/pub_grub/rubygems.rb +45 -0
  95. data/vendor/pub_grub/lib/pub_grub/solve_failure.rb +17 -0
  96. data/vendor/pub_grub/lib/pub_grub/static_package_source.rb +53 -0
  97. data/vendor/pub_grub/lib/pub_grub/term.rb +105 -0
  98. data/vendor/pub_grub/lib/pub_grub/version.rb +3 -0
  99. data/vendor/pub_grub/lib/pub_grub/version_constraint.rb +124 -0
  100. data/vendor/pub_grub/lib/pub_grub/version_range.rb +399 -0
  101. data/vendor/pub_grub/lib/pub_grub/version_solver.rb +247 -0
  102. data/vendor/pub_grub/lib/pub_grub/version_union.rb +174 -0
  103. data/vendor/pub_grub/lib/pub_grub.rb +31 -0
  104. data/vendor/ruby-digest/UNLICENSE +24 -0
  105. data/vendor/ruby-digest/lib/ruby_digest.rb +812 -0
  106. metadata +95 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ac61e9a6e0ebdc00fe6c63fe59040cb50f66cfd375f4f4041a1f535f1c8e492
4
- data.tar.gz: 28812a81b32b67c6d23370974de0963229caef262585ecd91f81d4045a1e407e
3
+ metadata.gz: 12fd6cb40e3a3ad1f972f80bda31dab9b894f61393469c6030fbd05394d0a248
4
+ data.tar.gz: ec8b72583200dc1c5679a6eedde63c333970acc5834a8e454dbd00aaa30d0135
5
5
  SHA512:
6
- metadata.gz: 55fc3ff597234684ef052023204ac18a7fafe8289b88a8dfc896f66f06081bd72d4560092f88c3f74e5054d0030a80732ca30cf2ec7f560806cb260730b2a6ac
7
- data.tar.gz: 2631449a64f1b976833b17a632acd28a1f44b3c0821f6e96163ddb53767b0c96a7ccfa02bd49d3b2381ec0738a8aad2c25cb1261f714701f1ae75f8bc645ec74
6
+ metadata.gz: f27419701ba1c70f4ce7eef187ab61d9b681eade093f1966d5cc81a183a78ab8ff31dac4c365376b38e46331f3b66e0905930ff89044d948d26b9bc89b296df8
7
+ data.tar.gz: 1567767fb08472aa0fc1291ed0cfe56376acb28c7d5c3e56978952320694149910f9b8892f7a3937acef0aa5e22c89643afa1fc1e39b774ad04d0e39e834a53f
data/README.md CHANGED
@@ -4,8 +4,7 @@
4
4
 
5
5
  A modern gem manager.
6
6
 
7
- Gel is a lightweight alternative to Bundler.
8
-
7
+ Gel is a lightweight alternative to Bundler.
9
8
 
10
9
  | | Gel | Bundler & Rubygems |
11
10
  |---------|--------------------|--------------------|
@@ -88,10 +87,34 @@ Or add it to your `.bashrc` or `.zshrc` to enable it everywhere:
88
87
 
89
88
  Use `gel install`, `gel lock`, `gel update`, and `gel exec` as you would the equivalent `bundle` subcommands.
90
89
 
90
+ ## ENVIRONMENT VARIABLES
91
+
92
+ * `GEL_GEMFILE`
93
+ The path to the gemfile gel should use
94
+
95
+ * `GEL_LOCKFILE`
96
+ The path to the lockfile that gel should use
97
+
98
+ * `GEL_CACHE`
99
+ The path to the gel version information cache
100
+
101
+ * `GEL_AUTH`
102
+ Gem server credentials as a space-separated list of URIs, e.g.:
103
+ "http://user:pass@ruby.example.com/ http://user2:pass2@gems.example.org/"
104
+
91
105
  ## Development
92
106
 
93
107
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake test` to run the tests.
94
108
 
109
+ To use your development instance as your primary Gel, add its `exe/` to your `$PATH` before running `shell-setup`, ensuring it comes before any RubyGems `bin` directory that might override it.
110
+
111
+ For example:
112
+
113
+ ```sh
114
+ PATH="$HOME/projects/gel/exe:$PATH"
115
+ eval "$(gel shell-setup)"
116
+ ```
117
+
95
118
  ## Contributing
96
119
 
97
120
  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.
@@ -102,4 +125,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
102
125
 
103
126
  ## Code of Conduct
104
127
 
105
- 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).
128
+ 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/main/CODE_OF_CONDUCT.md).
data/RELEASING.md ADDED
@@ -0,0 +1,12 @@
1
+ # Releasing Gel
2
+
3
+ 1. Bump the version in `lib/gel/version.rb`
4
+ 1. `gel lock`
5
+ 1. `rake build`
6
+ 1. `git commit`
7
+ 1. `git tag vX.Y.Z`
8
+ 1. `git push --tags origin main vX.Y.Z`
9
+ 1. On GitHub, convert the tag to a release, and write a changelog
10
+ 1. `gem push pkg/gel-X.Y.Z.gem`
11
+ 1. `brew bump-formula-pr --url=https://github.com/gel-rb/gel/archive/vX.Y.Z.tar.gz`
12
+ 1. Tweet a link to https://github.com/gel-rb/gel/releases/tag/vX.Y.Z
data/exe/gel CHANGED
@@ -1,9 +1,11 @@
1
- #!/usr/bin/env ruby --disable=gems
1
+ #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
4
  if defined?(::Gem) && !defined?(::Gel)
5
+ # Note there's a similar re-exec in gemlib/gel/stub.rb
6
+
5
7
  exec ::Gem.ruby,
6
- "-I", File.expand_path("../lib/gel/compatibility", __dir__),
8
+ "-I", File.expand_path("../slib", __dir__),
7
9
  "--",
8
10
  __FILE__,
9
11
  *ARGV
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file exists to be required from a Gel binstub if it has been
4
+ # loaded via 'ruby -S' and Rubygems was loaded. It (only) defines an
5
+ # alternative implementation of Gel.stub, which will re-exec Ruby to
6
+ # switch to Gel.
7
+
8
+ module Gel
9
+ def self.stub(name)
10
+ # Note there's a similar re-exec in exe/gel
11
+
12
+ exec ::Gem.ruby,
13
+ "-I", File.expand_path("../slib", __dir__),
14
+ "--",
15
+ File.expand_path("../exe/gel", __dir__),
16
+ "stub",
17
+ name,
18
+ *ARGV
19
+ end
20
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fileutils"
4
3
  require "monitor"
4
+ require_relative "../util"
5
5
 
6
6
  module Gel::Catalog::Common
7
7
  def initialize(uri, uri_identifier, httpool:, work_pool:, cache:)
@@ -66,11 +66,13 @@ module Gel::Catalog::Common
66
66
  end
67
67
 
68
68
  def pinboard
69
+ require_relative "../pinboard"
70
+
69
71
  @pinboard || @monitor.synchronize do
70
72
  @pinboard ||=
71
73
  begin
72
74
  pinboard_dir = File.expand_path("#{@cache}/#{self.class::CACHE_TYPE}/#{@uri_identifier}")
73
- FileUtils.mkdir_p(pinboard_dir) unless Dir.exist?(pinboard_dir)
75
+ Gel::Util.mkdir_p(pinboard_dir)
74
76
  Gel::Pinboard.new(pinboard_dir, httpool: @httpool, work_pool: @work_pool)
75
77
  end
76
78
  end
@@ -1,23 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
-
5
- require_relative "../pinboard"
6
-
7
- require_relative "common"
3
+ require_relative "../set"
8
4
 
9
5
  class Gel::Catalog::CompactIndex
10
6
  include Gel::Catalog::Common
11
7
  CACHE_TYPE = "index"
12
8
 
13
- def initialize(*)
14
- super
9
+ def initialize(*args, **kwargs)
10
+ super(*args, **kwargs)
15
11
 
16
12
  @gem_tokens = Hash.new("NONE")
17
13
  @needs_update = true
18
14
  @updating = false
19
- @active_gems = Set.new
20
- @pending_gems = Set.new
15
+ @active_gems = Gel::Set.new
16
+ @pending_gems = Gel::Set.new
21
17
  end
22
18
 
23
19
  def prepare(gems)
@@ -102,7 +98,7 @@ class Gel::Catalog::CompactIndex
102
98
  end
103
99
 
104
100
  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
101
+ dependency_names = Gel::Set.new
106
102
  info = {}
107
103
 
108
104
  started = false
@@ -1,27 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
- require "cgi"
5
3
  require "zlib"
6
4
 
7
- require_relative "../pinboard"
5
+ require_relative "../set"
6
+ require_relative "../support/cgi_escape"
8
7
 
9
- require_relative "common"
10
8
  require_relative "marshal_hacks"
11
9
 
12
10
  class Gel::Catalog::DependencyIndex
13
11
  include Gel::Catalog::Common
12
+ include Gel::Support::CGIEscape
13
+
14
14
  CACHE_TYPE = "quick"
15
15
 
16
16
  LIST_MAX = 40
17
17
 
18
- def initialize(catalog, *args)
19
- super(*args)
18
+ def initialize(catalog, *args, **kwargs)
19
+ super(*args, **kwargs)
20
20
 
21
21
  @catalog = catalog
22
22
 
23
- @active_gems = Set.new
24
- @pending_gems = Set.new
23
+ @active_gems = Gel::Set.new
24
+ @pending_gems = Gel::Set.new
25
25
  end
26
26
 
27
27
  def prepare(gems)
@@ -56,7 +56,7 @@ class Gel::Catalog::DependencyIndex
56
56
  end
57
57
 
58
58
  def refresh_some_gems(gems)
59
- gem_list = gems.map { |g| CGI.escape(g) }.sort.join(",")
59
+ gem_list = gems.map { |g| cgi_escape(g) }.sort.join(",")
60
60
  @work_pool.queue(gem_list) do
61
61
  response =
62
62
  begin
@@ -72,7 +72,7 @@ class Gel::Catalog::DependencyIndex
72
72
  new_info = {}
73
73
  gems.each { |g| new_info[g] = {} }
74
74
 
75
- new_dependencies = Set.new
75
+ new_dependencies = Gel::Set.new
76
76
 
77
77
  hashes = Marshal.load(response.body)
78
78
  hashes.each do |hash|
@@ -1,24 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
3
  require "zlib"
5
4
 
6
- require_relative "../pinboard"
5
+ require_relative "../set"
7
6
 
8
- require_relative "common"
9
7
  require_relative "marshal_hacks"
10
8
 
11
9
  class Gel::Catalog::LegacyIndex
12
10
  include Gel::Catalog::Common
13
11
  CACHE_TYPE = "quick"
14
12
 
15
- def initialize(*)
13
+ def initialize(*args, **kwargs)
16
14
  super
17
15
 
18
16
  @needs_update = true
19
17
  @updating = false
20
- @active_gems = Set.new
21
- @pending_gems = Set.new
18
+ @active_gems = Gel::Set.new
19
+ @pending_gems = Gel::Set.new
22
20
 
23
21
  @gem_versions = {}
24
22
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module Gem; end unless defined? Gem
4
+
3
5
  class Gem::Specification
4
6
  class Unmarshalled
5
7
  attr_accessor :required_ruby_version
data/lib/gel/catalog.rb CHANGED
@@ -1,16 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fileutils"
4
- require "net/http"
5
3
  require "uri"
6
- require "digest"
7
4
 
8
5
  require_relative "httpool"
6
+ require_relative "util"
9
7
  require_relative "support/gem_platform"
8
+ require_relative "vendor/ruby_digest"
10
9
 
11
10
  class Gel::Catalog
12
11
  UPDATE_CONCURRENCY = 8
13
12
 
13
+ autoload :Common, File.expand_path("catalog/common", __dir__)
14
+ autoload :CompactIndex, File.expand_path("catalog/compact_index", __dir__)
15
+ autoload :DependencyIndex, File.expand_path("catalog/dependency_index", __dir__)
16
+ autoload :LegacyIndex, File.expand_path("catalog/legacy_index", __dir__)
17
+
14
18
  def initialize(uri, httpool: Gel::Httpool.new, work_pool:, cache: ENV["GEL_CACHE"] || "~/.cache/gel", initial_gems: [])
15
19
  @uri = normalize_uri(uri)
16
20
  @httpool = httpool
@@ -25,10 +29,10 @@ class Gel::Catalog
25
29
  ]
26
30
  end
27
31
 
28
- def prepare
29
- index.prepare(@initial_gems)
30
- rescue Net::HTTPExceptions
31
- if @indexes.size > 1
32
+ def attempting_each_index
33
+ yield send(@indexes.first)
34
+ rescue => ex
35
+ if recoverable?(ex) && @indexes.size > 1
32
36
  @indexes.shift
33
37
  retry
34
38
  else
@@ -36,65 +40,44 @@ class Gel::Catalog
36
40
  end
37
41
  end
38
42
 
39
- def compact_index
40
- @compact_index ||= Gel::Catalog::CompactIndex.new(@uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
43
+ def recoverable?(exception)
44
+ defined?(Net::HTTPExceptions) && Net::HTTPExceptions === exception
41
45
  end
42
46
 
43
- def dependency_index
44
- @dependency_index ||= Gel::Catalog::DependencyIndex.new(self, @uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
47
+ def prepare
48
+ attempting_each_index { |index| index.prepare(@initial_gems) }
45
49
  end
46
50
 
47
- def legacy_index
48
- @legacy_index ||= Gel::Catalog::LegacyIndex.new(@uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
51
+ def gem_info(name)
52
+ attempting_each_index { |index| index.gem_info(name) }
49
53
  end
50
54
 
51
- def index
52
- send(@indexes.first)
55
+ def compact_index
56
+ @compact_index ||= Gel::Catalog::CompactIndex.new(@uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
53
57
  end
54
58
 
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
59
+ def dependency_index
60
+ @dependency_index ||= Gel::Catalog::DependencyIndex.new(self, @uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
64
61
  end
65
62
 
66
- def cached_gem(name, version)
67
- path = cache_path(name, version)
68
- return path if File.exist?(path)
63
+ def legacy_index
64
+ @legacy_index ||= Gel::Catalog::LegacyIndex.new(@uri, uri_identifier, httpool: @httpool, work_pool: @work_pool, cache: @cache)
69
65
  end
70
66
 
71
67
  def download_gem(name, version)
72
68
  path = cache_path(name, version)
73
69
  return path if File.exist?(path)
74
70
 
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]
71
+ if gem_info(name)
72
+ response = http_get("/gems/#{name}-#{version}.gem")
73
+ Gel::Util.mkdir_p(cache_dir)
74
+ File.open(path, "wb") do |f|
75
+ f.write(response.body)
76
+ end
77
+ path
91
78
  end
92
79
  end
93
80
 
94
- def platform_specific_version
95
- Gel::Support::GemPlatform.new(RbConfig::CONFIG["arch"])
96
- end
97
-
98
81
  def inspect
99
82
  "#<#{self.class} #{to_s.inspect}>"
100
83
  end
@@ -117,7 +100,7 @@ class Gel::Catalog
117
100
  end
118
101
 
119
102
  def uri_identifier
120
- @uri.host + "-" + Digest(:SHA256).hexdigest(@uri.to_s)[0..10]
103
+ @uri.host + "-" + Gel::Vendor::RubyDigest::SHA256.hexdigest(@uri.to_s)[0..10]
121
104
  end
122
105
 
123
106
  def cache_dir
@@ -129,6 +112,8 @@ class Gel::Catalog
129
112
  end
130
113
 
131
114
  def http_get(path)
115
+ require "net/http"
116
+
132
117
  original_uri = uri = URI(File.join(@uri.to_s, path))
133
118
 
134
119
  5.times do
@@ -147,7 +132,3 @@ class Gel::Catalog
147
132
  raise Gel::Error::TooManyRedirectsError.new(original_uri: original_uri)
148
133
  end
149
134
  end
150
-
151
- require_relative "catalog/compact_index"
152
- require_relative "catalog/dependency_index"
153
- require_relative "catalog/legacy_index"
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "platform"
4
+
5
+ class Gel::CatalogSet
6
+ CatalogEntry = Struct.new(:catalog, :name, :version, :info) do
7
+ def gem_version
8
+ @gem_version ||= Gel::Support::GemVersion.new(version)
9
+ end
10
+ end
11
+
12
+ def initialize(catalogs)
13
+ @catalogs = catalogs
14
+
15
+ @cached_specs = Hash.new { |h, k| h[k] = {} }
16
+ @specs_by_package_version = {}
17
+ end
18
+
19
+ def catalog_for_version(package, version)
20
+ @specs_by_package_version[package.name][version.to_s]&.catalog
21
+ end
22
+
23
+ def entries_for(package)
24
+ fetch_package_info(package)
25
+
26
+ @specs_by_package_version[package.name].values.select do |spec|
27
+ available_platforms = spec.info.map(&:first)
28
+ Gel::Platform.match(package.platform, available_platforms)
29
+ end
30
+ end
31
+
32
+ # Returns a list of [name, version_contraint] pairs representing the
33
+ # specified package's dependencies. Note that a given +name+ may
34
+ # appear multiple times, and the resulting dependency is the
35
+ # intersection of all constraints.
36
+ def dependencies_for(package, version)
37
+ fetch_package_info(package) # probably already done, can't hurt
38
+
39
+ spec = @specs_by_package_version[package.name][version.to_s]
40
+
41
+ raise "missing spec #{package.inspect} / #{version.inspect}" if spec.nil?
42
+
43
+ info = spec.info
44
+
45
+ available_platforms = info.map(&:first)
46
+ matching_platform = Gel::Platform.match(package.platform, available_platforms)
47
+
48
+ info = info.select { |p, i| p == matching_platform }
49
+
50
+ # FIXME: ruby_constraints ???
51
+
52
+ info.flat_map { |_, i| i[:dependencies] }
53
+ end
54
+
55
+ def platform_for(package, version)
56
+ fetch_package_info(package) # probably already done, can't hurt
57
+
58
+ spec = @specs_by_package_version[package.name][version.to_s]
59
+
60
+ info = spec.info
61
+
62
+ available_platforms = info.map(&:first)
63
+ Gel::Platform.match(package.platform, available_platforms)
64
+ end
65
+
66
+ private
67
+
68
+ def fetch_package_info(package)
69
+ return if @specs_by_package_version.key?(package.name)
70
+
71
+ specs = []
72
+ @catalogs.each do |catalog|
73
+ if catalog.nil?
74
+ break unless specs.empty?
75
+ next
76
+ end
77
+
78
+ if info = catalog.gem_info(package.name)
79
+ @cached_specs[catalog][package.name] ||=
80
+ begin
81
+ grouped_versions = info.to_a.map do |full_version, attributes|
82
+ version, platform = full_version.split("-", 2)
83
+ platform ||= "ruby"
84
+ [version, platform, attributes]
85
+ end.group_by(&:first)
86
+
87
+ grouped_versions.map { |version, tuples| CatalogEntry.new(catalog, package.name, version, tuples.map { |_, p, a| [p, a] }) }
88
+ end
89
+
90
+ specs.concat @cached_specs[catalog][package.name]
91
+ end
92
+ end
93
+
94
+ @specs_by_package_version[package.name] = {}
95
+ specs.each do |spec|
96
+ # TODO: are we going to find specs in multiple catalogs this way?
97
+ @specs_by_package_version[package.name][spec.version] = spec
98
+ end
99
+ end
100
+ end
@@ -1,7 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Gel::Command::Help < Gel::Command
4
- def run(command_line)
5
- raise "TODO"
4
+ def run(_command_line)
5
+ puts <<~HELP
6
+ Gel is a modern gem manager.
7
+
8
+ Usage:
9
+ gel help Print this help message.
10
+ gel install Install the gems from Gemfile.
11
+ gel lock Update lockfile without installing.
12
+ gel exec Run command in context of the gel.
13
+
14
+ Further information:
15
+ https://gel.dev/
16
+ HELP
6
17
  end
7
18
  end
@@ -25,10 +25,10 @@ class Gel::Command::Lock < Gel::Command
25
25
  end
26
26
 
27
27
  require_relative "../pub_grub/preference_strategy"
28
- options[:preference_strategy] = lambda do |loader|
29
- Gel::PubGrub::PreferenceStrategy.new(loader, overrides, bump: mode, strict: strict)
28
+ options[:preference_strategy] = lambda do |gem_set|
29
+ Gel::PubGrub::PreferenceStrategy.new(gem_set, overrides, bump: mode, strict: strict)
30
30
  end
31
31
 
32
- Gel::Environment.lock(output: $stderr, **options)
32
+ Gel::Environment.write_lock(output: $stderr, **options)
33
33
  end
34
34
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Gel::Command::Open < Gel::Command
4
+ def run(command_line)
5
+ require "shellwords"
6
+
7
+ raise "Please provide the name of a gem to open in your editor" if command_line.empty?
8
+ raise "Too many arguments, only 1 gem name is supported" if command_line.length > 1
9
+ gem_name = command_line.shift
10
+
11
+ editor = ENV.fetch("GEL_EDITOR", ENV["EDITOR"])
12
+ raise "An editor must be set using either $GEL_EDITOR or $EDITOR" unless editor
13
+
14
+ Gel::Environment.activate(output: $stderr)
15
+
16
+ found_gem = Gel::Environment.find_gem(gem_name)
17
+ raise "Can't find gem `#{gem_name}`" unless found_gem
18
+
19
+ command = [*Shellwords.split(editor), found_gem.root]
20
+ Dir.chdir(found_gem.root) do
21
+ exec(*command)
22
+ end
23
+ end
24
+ end
@@ -4,22 +4,25 @@ class Gel::Command::ShellSetup < Gel::Command
4
4
  def run(command_line)
5
5
  require "shellwords"
6
6
 
7
- variables = []
7
+ shell = command_line[0] || File.basename(ENV.fetch("SHELL", "bash"))
8
8
 
9
9
  bin_dir = File.expand_path("~/.local/gel/bin")
10
10
  unless ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).include?(bin_dir)
11
- puts "PATH=\"#{Shellwords.shellescape bin_dir}#{File::PATH_SEPARATOR}$PATH\""
12
- variables << "PATH"
11
+ puts export("PATH", "\"#{Shellwords.shellescape bin_dir}#{File::PATH_SEPARATOR}$PATH\"", shell: shell)
13
12
  end
14
13
 
15
- lib_dir = File.expand_path("../compatibility", __dir__)
14
+ lib_dir = File.expand_path("../../slib", __dir__)
16
15
  unless ENV.fetch("RUBYLIB", "").split(File::PATH_SEPARATOR).include?(lib_dir)
17
- puts "RUBYLIB=\"#{Shellwords.shellescape lib_dir}:$RUBYLIB\""
18
- variables << "RUBYLIB"
16
+ puts export("RUBYLIB", "\"#{Shellwords.shellescape lib_dir}:$RUBYLIB\"", shell: shell)
19
17
  end
18
+ end
20
19
 
21
- unless variables.empty?
22
- puts "export #{variables.join(" ")}"
20
+ def export(env, value, shell: nil)
21
+ case shell
22
+ when "fish"
23
+ "set -x #{env} #{value}"
24
+ else
25
+ "export #{env}=#{value}"
23
26
  end
24
27
  end
25
28
  end
@@ -2,11 +2,54 @@
2
2
 
3
3
  class Gel::Command::Stub < Gel::Command
4
4
  def run(command_line)
5
- stub_command, _path, *arguments = command_line
5
+ if command_line.first == "--rebuild"
6
+ Gel::Environment.store.stub_set.rebuild!
7
+ return
8
+ end
9
+
10
+ # Note that the most common stub invocation doesn't actually pass
11
+ # through here at all: a stubfile being run directly will present
12
+ # as `gel <full-path-to-stubfile>` and will be handled by the
13
+ # corresponding special case in the top level Gel::Command.run.
14
+ #
15
+ # We do get here when invoked by Gel.stub, or manual `gel stub foo`
16
+ # execution, though. In both of those cases, the first element of
17
+ # command_line will be the unqualified command to run, and the rest
18
+ # will be its arguments -- so we can just pass command_line along to
19
+ # Exec unmodified.
20
+ #
21
+ # However, there is one more situation that will end up here: when a
22
+ # legacy stub file is invoked. In that case, after the unqualified
23
+ # command name and before any supplied arguments, the shebang
24
+ # invocation will have inserted the fully-qualified stubfile path as
25
+ # well. We need to detect that, and strip it out.
26
+
27
+ command_line.slice!(1) if redundant_stub_argument?(command_line)
6
28
 
7
29
  command = Gel::Command::Exec.new
8
- command.run([stub_command, *arguments], from_stub: true)
30
+ command.run(command_line, from_stub: true)
9
31
  ensure
10
32
  self.reraise = command.reraise if command
11
33
  end
34
+
35
+ private
36
+
37
+ def redundant_stub_argument?((command, possible_stub_path, *))
38
+ return false unless possible_stub_path
39
+
40
+ # Gel.stub injects a symbol to disambiguate the subsequent
41
+ # arguments; we don't need to look any further, and definitely want
42
+ # to strip it out.
43
+ return true if possible_stub_path == :stub
44
+
45
+ # A true redundant argument is a fully-qualified version of the
46
+ # stubbed command. Does it even look like the names match?
47
+ return false unless possible_stub_path.end_with?(command)
48
+
49
+ # Okay, it seems plausible; in that case, it's time to check
50
+ # properly.
51
+ stub_set = Gel::Environment.store.stub_set
52
+ stub_set.own_stub?(possible_stub_path) &&
53
+ stub_set.parse_stub(possible_stub_path) == command
54
+ end
12
55
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Gel::Command::Version < Gel::Command
4
+ def run(_command_line)
5
+ puts "Gel version #{Gel::VERSION}"
6
+ end
7
+ end