gel 0.3.0 → 0.8.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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