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