gel 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/exe/gel +13 -0
- data/lib/gel.rb +22 -0
- data/lib/gel/catalog.rb +153 -0
- data/lib/gel/catalog/common.rb +82 -0
- data/lib/gel/catalog/compact_index.rb +152 -0
- data/lib/gel/catalog/dependency_index.rb +125 -0
- data/lib/gel/catalog/legacy_index.rb +157 -0
- data/lib/gel/catalog/marshal_hacks.rb +16 -0
- data/lib/gel/command.rb +86 -0
- data/lib/gel/command/config.rb +11 -0
- data/lib/gel/command/env.rb +7 -0
- data/lib/gel/command/exec.rb +66 -0
- data/lib/gel/command/help.rb +7 -0
- data/lib/gel/command/install.rb +7 -0
- data/lib/gel/command/install_gem.rb +16 -0
- data/lib/gel/command/lock.rb +34 -0
- data/lib/gel/command/ruby.rb +10 -0
- data/lib/gel/command/shell_setup.rb +25 -0
- data/lib/gel/command/stub.rb +12 -0
- data/lib/gel/command/update.rb +11 -0
- data/lib/gel/compatibility.rb +4 -0
- data/lib/gel/compatibility/bundler.rb +54 -0
- data/lib/gel/compatibility/bundler/cli.rb +6 -0
- data/lib/gel/compatibility/bundler/friendly_errors.rb +3 -0
- data/lib/gel/compatibility/bundler/setup.rb +4 -0
- data/lib/gel/compatibility/rubygems.rb +192 -0
- data/lib/gel/compatibility/rubygems/command.rb +4 -0
- data/lib/gel/compatibility/rubygems/dependency_installer.rb +0 -0
- data/lib/gel/compatibility/rubygems/gem_runner.rb +6 -0
- data/lib/gel/config.rb +80 -0
- data/lib/gel/db.rb +294 -0
- data/lib/gel/direct_gem.rb +29 -0
- data/lib/gel/environment.rb +592 -0
- data/lib/gel/error.rb +104 -0
- data/lib/gel/gemfile_parser.rb +144 -0
- data/lib/gel/gemspec_parser.rb +95 -0
- data/lib/gel/git_catalog.rb +38 -0
- data/lib/gel/git_depot.rb +119 -0
- data/lib/gel/httpool.rb +148 -0
- data/lib/gel/installer.rb +251 -0
- data/lib/gel/lock_loader.rb +164 -0
- data/lib/gel/lock_parser.rb +64 -0
- data/lib/gel/locked_store.rb +126 -0
- data/lib/gel/multi_store.rb +96 -0
- data/lib/gel/package.rb +156 -0
- data/lib/gel/package/inspector.rb +23 -0
- data/lib/gel/package/installer.rb +267 -0
- data/lib/gel/path_catalog.rb +44 -0
- data/lib/gel/pinboard.rb +140 -0
- data/lib/gel/pub_grub/preference_strategy.rb +82 -0
- data/lib/gel/pub_grub/source.rb +153 -0
- data/lib/gel/runtime.rb +27 -0
- data/lib/gel/store.rb +205 -0
- data/lib/gel/store_catalog.rb +31 -0
- data/lib/gel/store_gem.rb +80 -0
- data/lib/gel/stub_set.rb +51 -0
- data/lib/gel/support/gem_platform.rb +225 -0
- data/lib/gel/support/gem_requirement.rb +264 -0
- data/lib/gel/support/gem_version.rb +398 -0
- data/lib/gel/support/tar.rb +13 -0
- data/lib/gel/support/tar/tar_header.rb +229 -0
- data/lib/gel/support/tar/tar_reader.rb +123 -0
- data/lib/gel/support/tar/tar_reader/entry.rb +154 -0
- data/lib/gel/support/tar/tar_writer.rb +339 -0
- data/lib/gel/tail_file.rb +205 -0
- data/lib/gel/version.rb +5 -0
- data/lib/gel/work_pool.rb +143 -0
- data/man/man1/gel-exec.1 +16 -0
- data/man/man1/gel-install.1 +16 -0
- data/man/man1/gel.1 +30 -0
- metadata +131 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require "cgi"
|
5
|
+
require "zlib"
|
6
|
+
|
7
|
+
require_relative "../pinboard"
|
8
|
+
|
9
|
+
require_relative "common"
|
10
|
+
require_relative "marshal_hacks"
|
11
|
+
|
12
|
+
class Gel::Catalog::DependencyIndex
|
13
|
+
include Gel::Catalog::Common
|
14
|
+
CACHE_TYPE = "quick"
|
15
|
+
|
16
|
+
LIST_MAX = 40
|
17
|
+
|
18
|
+
def initialize(catalog, *args)
|
19
|
+
super(*args)
|
20
|
+
|
21
|
+
@catalog = catalog
|
22
|
+
|
23
|
+
@active_gems = Set.new
|
24
|
+
@pending_gems = Set.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def prepare(gems)
|
28
|
+
@monitor.synchronize do
|
29
|
+
@pending_gems.merge(gems)
|
30
|
+
end
|
31
|
+
force_refresh_including(gems.first)
|
32
|
+
@monitor.synchronize do
|
33
|
+
@refresh_cond.wait_until { gems.all? { |g| _info(g) } }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def force_refresh_including(gem_name)
|
38
|
+
gems_to_refresh = []
|
39
|
+
|
40
|
+
@monitor.synchronize do
|
41
|
+
return if _info(gem_name) || @active_gems.include?(gem_name)
|
42
|
+
|
43
|
+
gems_to_refresh << gem_name
|
44
|
+
@pending_gems.delete gem_name
|
45
|
+
|
46
|
+
while gems_to_refresh.size < LIST_MAX && @pending_gems.size > 0
|
47
|
+
a_gem = @pending_gems.first
|
48
|
+
@pending_gems.delete a_gem
|
49
|
+
gems_to_refresh << a_gem
|
50
|
+
end
|
51
|
+
|
52
|
+
@active_gems.merge gems_to_refresh
|
53
|
+
end
|
54
|
+
|
55
|
+
refresh_some_gems gems_to_refresh
|
56
|
+
end
|
57
|
+
|
58
|
+
def refresh_some_gems(gems)
|
59
|
+
gem_list = gems.map { |g| CGI.escape(g) }.sort.join(",")
|
60
|
+
@work_pool.queue(gem_list) do
|
61
|
+
response =
|
62
|
+
begin
|
63
|
+
@catalog.send(:http_get, "api/v1/dependencies?gems=#{gem_list}")
|
64
|
+
rescue Exception => ex
|
65
|
+
@monitor.synchronize do
|
66
|
+
@error = ex
|
67
|
+
@refresh_cond.broadcast
|
68
|
+
end
|
69
|
+
next
|
70
|
+
end
|
71
|
+
|
72
|
+
new_info = {}
|
73
|
+
gems.each { |g| new_info[g] = {} }
|
74
|
+
|
75
|
+
new_dependencies = Set.new
|
76
|
+
|
77
|
+
hashes = Marshal.load(response.body)
|
78
|
+
hashes.each do |hash|
|
79
|
+
v = hash[:number].to_s
|
80
|
+
v += "-#{hash[:platform]}" unless hash[:platform] == "ruby"
|
81
|
+
|
82
|
+
(new_info[hash[:name]] ||= {})[v] = {
|
83
|
+
dependencies: hash[:dependencies].map { |name, versions| [name, versions.split(/,\s*/)] },
|
84
|
+
ruby: lambda do
|
85
|
+
# The disadvantage of trying to avoid this per-version
|
86
|
+
# request is that when we do discover we need it, we need it
|
87
|
+
# immediately. :/
|
88
|
+
pinboard.file(uri("quick", "Marshal.4.8", "#{hash[:name]}-#{v}.gemspec.rz"), token: false, tail: false) do |f|
|
89
|
+
data = Zlib::Inflate.inflate(f.read)
|
90
|
+
# TODO: Extract the data we need without a full unmarshal
|
91
|
+
Marshal.load(data).required_ruby_version
|
92
|
+
end
|
93
|
+
end,
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
hashes.group_by { |h| h[:name] }.each do |_, group|
|
98
|
+
versions = group.group_by { |h| h[:number] }
|
99
|
+
latest = versions.keys.max_by { |v| Gel::Support::GemVersion.new(v) }
|
100
|
+
new_dependencies.merge versions[latest].flat_map { |h| h[:dependencies].map(&:first) }.uniq
|
101
|
+
end
|
102
|
+
|
103
|
+
@monitor.synchronize do
|
104
|
+
@gem_info.update new_info
|
105
|
+
@active_gems.subtract new_info.keys
|
106
|
+
|
107
|
+
@refresh_cond.broadcast
|
108
|
+
|
109
|
+
new_dependencies.subtract @active_gems
|
110
|
+
new_dependencies.subtract @gem_info.keys
|
111
|
+
@pending_gems.merge new_dependencies
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
@work_pool.start
|
116
|
+
end
|
117
|
+
|
118
|
+
def refresh_gem(gem_name, immediate = false)
|
119
|
+
@monitor.synchronize do
|
120
|
+
@pending_gems << gem_name unless _info(gem_name) || @active_gems.include?(gem_name)
|
121
|
+
end
|
122
|
+
|
123
|
+
force_refresh_including gem_name if immediate
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require "zlib"
|
5
|
+
|
6
|
+
require_relative "../pinboard"
|
7
|
+
|
8
|
+
require_relative "common"
|
9
|
+
require_relative "marshal_hacks"
|
10
|
+
|
11
|
+
class Gel::Catalog::LegacyIndex
|
12
|
+
include Gel::Catalog::Common
|
13
|
+
CACHE_TYPE = "quick"
|
14
|
+
|
15
|
+
def initialize(*)
|
16
|
+
super
|
17
|
+
|
18
|
+
@needs_update = true
|
19
|
+
@updating = false
|
20
|
+
@active_gems = Set.new
|
21
|
+
@pending_gems = Set.new
|
22
|
+
|
23
|
+
@gem_versions = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def prepare(gems)
|
27
|
+
@monitor.synchronize do
|
28
|
+
@pending_gems.merge(gems)
|
29
|
+
end
|
30
|
+
update
|
31
|
+
@monitor.synchronize do
|
32
|
+
@refresh_cond.wait_until { gems.all? { |g| _info(g) } }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def update
|
37
|
+
@monitor.synchronize do
|
38
|
+
return false unless @needs_update
|
39
|
+
@needs_update = false
|
40
|
+
@updating = true
|
41
|
+
end
|
42
|
+
|
43
|
+
specs = false
|
44
|
+
prerelease_specs = false
|
45
|
+
|
46
|
+
versions = {}
|
47
|
+
|
48
|
+
error = lambda do |ex|
|
49
|
+
@monitor.synchronize do
|
50
|
+
@error = ex
|
51
|
+
@updating = false
|
52
|
+
@refresh_cond.broadcast
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
spec_file_handler = lambda do |for_prerelease|
|
57
|
+
lambda do |f|
|
58
|
+
data = Zlib::GzipReader.new(f).read
|
59
|
+
data = Marshal.load(data)
|
60
|
+
|
61
|
+
data.each do |name, version, platform|
|
62
|
+
v = version.to_s
|
63
|
+
v += "-#{platform}" unless platform == "ruby"
|
64
|
+
(versions[name] ||= {})[v] = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
done = false
|
68
|
+
@monitor.synchronize do
|
69
|
+
if for_prerelease
|
70
|
+
prerelease_specs = true
|
71
|
+
else
|
72
|
+
specs = true
|
73
|
+
end
|
74
|
+
|
75
|
+
if specs && prerelease_specs
|
76
|
+
done = true
|
77
|
+
@gem_info.update versions
|
78
|
+
@updating = false
|
79
|
+
@refresh_cond.broadcast
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if done
|
84
|
+
(@active_gems | @pending_gems).each do |name|
|
85
|
+
refresh_gem(name)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
pinboard.async_file(uri("specs.4.8.gz"), tail: false, error: error, &spec_file_handler.(false))
|
92
|
+
pinboard.async_file(uri("prerelease_specs.4.8.gz"), tail: false, error: error, &spec_file_handler.(true))
|
93
|
+
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
def refresh_gem(gem_name, immediate = true)
|
98
|
+
update
|
99
|
+
|
100
|
+
versions = nil
|
101
|
+
@monitor.synchronize do
|
102
|
+
if @updating
|
103
|
+
@pending_gems << gem_name
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
unless info = @gem_info[gem_name]
|
108
|
+
@gem_info[gem_name] = {}
|
109
|
+
@refresh_cond.broadcast
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
versions = info.keys.select { |v| info[v].nil? }
|
114
|
+
versions.each do |v|
|
115
|
+
info[v] = :pending
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
loaded_data = {}
|
120
|
+
versions.each do |v|
|
121
|
+
loaded_data[v] = nil
|
122
|
+
end
|
123
|
+
|
124
|
+
versions.each do |v|
|
125
|
+
error = lambda do |ex|
|
126
|
+
@gem_info[gem_name][v] = ex
|
127
|
+
@refresh_cond.broadcast
|
128
|
+
end
|
129
|
+
|
130
|
+
pinboard.async_file(uri("quick", "Marshal.4.8", "#{gem_name}-#{v}.gemspec.rz"), token: false, tail: false, error: error) do |f|
|
131
|
+
data = Zlib::Inflate.inflate(f.read)
|
132
|
+
# TODO: Extract the data we need without a full unmarshal
|
133
|
+
spec = Marshal.load(data)
|
134
|
+
|
135
|
+
@monitor.synchronize do
|
136
|
+
loaded_data[v] = { dependencies: spec.dependencies, ruby: spec.required_ruby_version }
|
137
|
+
if loaded_data.values.all?
|
138
|
+
@gem_info[gem_name].update loaded_data
|
139
|
+
@refresh_cond.broadcast
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def _info(gem_name)
|
149
|
+
if i = super
|
150
|
+
if i.values.all? { |v| v.is_a?(Hash) }
|
151
|
+
i
|
152
|
+
elsif e = i.values.grep(Exception).first
|
153
|
+
raise e
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Gem::Specification
|
4
|
+
class Unmarshalled
|
5
|
+
attr_accessor :required_ruby_version
|
6
|
+
attr_accessor :dependencies
|
7
|
+
end
|
8
|
+
|
9
|
+
def self._load(str)
|
10
|
+
array = Marshal.load(str)
|
11
|
+
o = Unmarshalled.new
|
12
|
+
o.required_ruby_version = array[6].as_list
|
13
|
+
o.dependencies = array[9].map { |d| [d.name, d.requirement.as_list] if d.type == :runtime }.compact
|
14
|
+
o
|
15
|
+
end
|
16
|
+
end
|
data/lib/gel/command.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "compatibility"
|
4
|
+
require_relative "error"
|
5
|
+
|
6
|
+
class Gel::Command
|
7
|
+
def self.run(command_line)
|
8
|
+
command_line = command_line.dup
|
9
|
+
if command_name = extract_word(command_line)
|
10
|
+
const = command_name.downcase.sub(/^./, &:upcase).gsub(/[-_]./) { |s| s[1].upcase }
|
11
|
+
if Gel::Command.const_defined?(const, false)
|
12
|
+
command = Gel::Command.const_get(const, false).new
|
13
|
+
command.run(command_line)
|
14
|
+
elsif Gel::Environment.activate_for_executable(["gel-#{command_name}", command_name])
|
15
|
+
command_name = "gel-#{command_name}" if Gel::Environment.find_executable("gel-#{command_name}")
|
16
|
+
command = Gel::Command::Exec.new
|
17
|
+
command.run([command_name, *command_line])
|
18
|
+
else
|
19
|
+
raise "Unknown command #{command_name.inspect}"
|
20
|
+
end
|
21
|
+
else
|
22
|
+
puts <<~EOF
|
23
|
+
Gel doesn't have a default command; please run `gel install`
|
24
|
+
EOF
|
25
|
+
end
|
26
|
+
rescue Exception => ex
|
27
|
+
raise if $DEBUG || (command && command.reraise)
|
28
|
+
handle_error(ex)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.handle_error(ex)
|
32
|
+
case ex
|
33
|
+
when Gel::ReportableError
|
34
|
+
$stderr.puts "ERROR: #{ex.message}"
|
35
|
+
if more = ex.details
|
36
|
+
$stderr.puts more
|
37
|
+
end
|
38
|
+
|
39
|
+
exit ex.exit_code
|
40
|
+
when Interrupt
|
41
|
+
# Re-signal so our parent knows why we died
|
42
|
+
Signal.trap(ex.signo, "SYSTEM_DEFAULT")
|
43
|
+
Process.kill(ex.signo, Process.pid)
|
44
|
+
|
45
|
+
# Shouldn't be reached
|
46
|
+
raise ex
|
47
|
+
when SystemExit, SignalException
|
48
|
+
raise ex
|
49
|
+
when StandardError, ScriptError, NoMemoryError, SystemStackError
|
50
|
+
# We want basically everything here: we definitely care about
|
51
|
+
# StandardError and ScriptError... but we also assume that whatever
|
52
|
+
# caused NoMemoryError or SystemStackError was way down the call
|
53
|
+
# stack, so we've now unwound enough to safely handle even those.
|
54
|
+
|
55
|
+
$stderr.print "\n\n===== Gel Internal Error =====\n\n"
|
56
|
+
|
57
|
+
# We'll improve this later, but for now after the header we'll leave
|
58
|
+
# ruby to write the message & backtrace:
|
59
|
+
raise ex
|
60
|
+
else
|
61
|
+
raise ex
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.extract_word(arguments)
|
66
|
+
if idx = arguments.index { |w| w =~ /^[^-]/ }
|
67
|
+
arguments.delete_at(idx)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# If set to true, an error raised from #run will pass straight up to
|
72
|
+
# ruby instead of being treated as an internal Gel error
|
73
|
+
attr_accessor :reraise
|
74
|
+
end
|
75
|
+
|
76
|
+
require_relative "command/help"
|
77
|
+
require_relative "command/install"
|
78
|
+
require_relative "command/install_gem"
|
79
|
+
require_relative "command/env"
|
80
|
+
require_relative "command/exec"
|
81
|
+
require_relative "command/lock"
|
82
|
+
require_relative "command/update"
|
83
|
+
require_relative "command/ruby"
|
84
|
+
require_relative "command/stub"
|
85
|
+
require_relative "command/config"
|
86
|
+
require_relative "command/shell_setup"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Gel::Command::Config < Gel::Command
|
4
|
+
def run(command_line)
|
5
|
+
if command_line.size == 1
|
6
|
+
puts Gel::Environment.config[command_line.first]
|
7
|
+
else
|
8
|
+
Gel::Environment.config[command_line.shift] = command_line.join(" ")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Gel::Command::Exec < Gel::Command
|
4
|
+
def run(command_line, from_stub: false)
|
5
|
+
original_command = command_line.shift
|
6
|
+
expanded_command, command_source = expand_executable(original_command)
|
7
|
+
|
8
|
+
if from_stub && [:original, :path].include?(command_source)
|
9
|
+
raise Gel::Error::BrokenStubError.new(name: original_command)
|
10
|
+
end
|
11
|
+
|
12
|
+
gemfile = Gel::Environment.find_gemfile(error: false)
|
13
|
+
|
14
|
+
if gemfile && command_source != :gem
|
15
|
+
ENV["GEL_GEMFILE"] = File.expand_path(gemfile)
|
16
|
+
ENV["GEL_LOCKFILE"] = File.expand_path(Gel::Environment.lockfile_name(gemfile))
|
17
|
+
end
|
18
|
+
|
19
|
+
ENV["RUBYLIB"] = Gel::Environment.modified_rubylib
|
20
|
+
|
21
|
+
if execute_inline?(expanded_command)
|
22
|
+
if command_source == :path || command_source == :original
|
23
|
+
if ENV["GEL_LOCKFILE"]
|
24
|
+
Gel::Environment.activate(output: $stderr)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
$0 = original_command
|
29
|
+
ARGV.replace(command_line)
|
30
|
+
|
31
|
+
# Any error after this point should bypass Gel's error
|
32
|
+
# handling
|
33
|
+
self.reraise = true
|
34
|
+
Kernel.load(expanded_command)
|
35
|
+
else
|
36
|
+
Kernel.exec([original_command, expanded_command], *command_line)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def expand_executable(original_command)
|
41
|
+
if original_command.include?(File::SEPARATOR) || (File::ALT_SEPARATOR && original_command.include?(File::ALT_SEPARATOR))
|
42
|
+
return [File.expand_path(original_command), :path]
|
43
|
+
end
|
44
|
+
|
45
|
+
if source = Gel::Environment.activate_for_executable([original_command])
|
46
|
+
if found = Gel::Environment.find_executable(original_command)
|
47
|
+
return [found, source]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
path_attempts = ENV["PATH"].split(File::PATH_SEPARATOR).map { |e| File.join(e, original_command) }
|
52
|
+
if found = path_attempts.find { |path| File.executable?(path) }
|
53
|
+
return [File.expand_path(found), :path]
|
54
|
+
end
|
55
|
+
|
56
|
+
[original_command, :original]
|
57
|
+
end
|
58
|
+
|
59
|
+
def execute_inline?(expanded_command)
|
60
|
+
if File.exist?(expanded_command) && File.executable?(expanded_command)
|
61
|
+
File.open(expanded_command, "rb") do |f|
|
62
|
+
f.read(2) == "#!" && f.gets.chomp =~ /\bruby\b/
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|