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,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gel::PubGrub
|
4
|
+
class PreferenceStrategy
|
5
|
+
def initialize(loader, overrides, bump: :major, strict: false)
|
6
|
+
@loader = loader
|
7
|
+
@overrides = overrides
|
8
|
+
@bump = bump
|
9
|
+
@strict = strict
|
10
|
+
end
|
11
|
+
|
12
|
+
# Overrides first, then packages for which we have a preference (and
|
13
|
+
# that preference is still in play), then everything else.
|
14
|
+
def package_priority(package, versions)
|
15
|
+
if package.name =~ /^~/
|
16
|
+
-1000
|
17
|
+
elsif @overrides.key?(package.name)
|
18
|
+
-100
|
19
|
+
elsif range = ranges[package.name]
|
20
|
+
yes, no = versions.partition { |version| range.satisfied_by?(version) }
|
21
|
+
if yes.any? && no.any?
|
22
|
+
-50
|
23
|
+
else
|
24
|
+
0
|
25
|
+
end
|
26
|
+
else
|
27
|
+
0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def sort_versions_by_preferred(package, versions)
|
32
|
+
return versions if @strict # already filtered
|
33
|
+
return versions unless range = ranges[package.name]
|
34
|
+
versions.partition { |version| range.satisfied_by?(version) }.inject(:+)
|
35
|
+
end
|
36
|
+
|
37
|
+
def constraints
|
38
|
+
ranges = @strict ? self.ranges : @overrides
|
39
|
+
|
40
|
+
result = {}
|
41
|
+
ranges.each do |package_name, range|
|
42
|
+
result[package_name] = [range] if range
|
43
|
+
end
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def ranges
|
50
|
+
@ranges ||=
|
51
|
+
begin
|
52
|
+
result = @overrides.dup
|
53
|
+
@loader.each_gem do |section, body, name, version, platform, deps|
|
54
|
+
next if @overrides.key?(name)
|
55
|
+
|
56
|
+
result[name] = range_for(version, @bump)
|
57
|
+
end
|
58
|
+
result.delete_if { |_, v| !v }
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def range_for(version, bump)
|
64
|
+
version = Gel::Support::GemVersion.new(version)
|
65
|
+
|
66
|
+
case bump
|
67
|
+
when :major
|
68
|
+
Gel::Support::GemRequirement.new ">= #{version}"
|
69
|
+
when :minor
|
70
|
+
next_major = version.bump
|
71
|
+
next_major = next_major.bump while next_major.segments.size > 2
|
72
|
+
Gel::Support::GemRequirement.new [">= #{version}", "< #{next_major}"]
|
73
|
+
when :patch
|
74
|
+
next_minor = version.bump
|
75
|
+
next_minor = next_minor.bump while next_minor.segments.size > 3
|
76
|
+
Gel::Support::GemRequirement.new [">= #{version}", "< #{next_minor}"]
|
77
|
+
when :hold
|
78
|
+
Gel::Support::GemRequirement.new "= #{version}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pub_grub"
|
4
|
+
require "pub_grub/basic_package_source"
|
5
|
+
require "pub_grub/rubygems"
|
6
|
+
|
7
|
+
module Gel::PubGrub
|
8
|
+
class Source < ::PubGrub::BasicPackageSource
|
9
|
+
Spec = Struct.new(:catalog, :name, :version, :info) do
|
10
|
+
def gem_version
|
11
|
+
@gem_version ||= Gel::Support::GemVersion.new(version)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :root, :root_version
|
16
|
+
|
17
|
+
def initialize(gemfile, catalogs, active_platforms, preference_strategy)
|
18
|
+
@gemfile = gemfile
|
19
|
+
@catalogs = catalogs
|
20
|
+
@active_platforms = active_platforms
|
21
|
+
@preference_strategy = preference_strategy
|
22
|
+
|
23
|
+
@packages = Hash.new {|h, k| h[k] = PubGrub::Package.new(k) }
|
24
|
+
@root = PubGrub::Package.root
|
25
|
+
@root_version = PubGrub::Package.root_version
|
26
|
+
|
27
|
+
@cached_specs = Hash.new { |h, k| h[k] = {} }
|
28
|
+
@specs_by_package_version = {}
|
29
|
+
|
30
|
+
super()
|
31
|
+
end
|
32
|
+
|
33
|
+
def spec_for_version(package, version)
|
34
|
+
if package.name =~ /^~/
|
35
|
+
return Spec.new(nil, package.name, version, [])
|
36
|
+
end
|
37
|
+
|
38
|
+
@specs_by_package_version[package][version.to_s]
|
39
|
+
end
|
40
|
+
|
41
|
+
def all_versions_for(package)
|
42
|
+
if package.name =~ /^~/
|
43
|
+
return [Gem::Version.new("0")]
|
44
|
+
end
|
45
|
+
|
46
|
+
fetch_package_info(package)
|
47
|
+
|
48
|
+
@specs_by_package_version[package].values.map(&:gem_version)
|
49
|
+
end
|
50
|
+
|
51
|
+
def sort_versions_by_preferred(package, sorted_versions)
|
52
|
+
sorted_versions = sorted_versions.reverse
|
53
|
+
|
54
|
+
if @preference_strategy
|
55
|
+
sorted_versions = @preference_strategy.sort_versions_by_preferred(package, sorted_versions)
|
56
|
+
end
|
57
|
+
|
58
|
+
prereleases, releases = sorted_versions.partition(&:prerelease?)
|
59
|
+
releases.concat(prereleases)
|
60
|
+
end
|
61
|
+
|
62
|
+
def dependencies_for(package, version)
|
63
|
+
deps = {}
|
64
|
+
|
65
|
+
case package.name
|
66
|
+
when "~arguments"
|
67
|
+
if @preference_strategy
|
68
|
+
@preference_strategy.constraints.each do |name, constraints|
|
69
|
+
deps[name] ||= []
|
70
|
+
deps[name].concat constraints.flatten
|
71
|
+
end
|
72
|
+
end
|
73
|
+
when /^~/
|
74
|
+
raise "Unknown pseudo-package"
|
75
|
+
else
|
76
|
+
fetch_package_info(package) # probably already done, can't hurt
|
77
|
+
|
78
|
+
spec = @specs_by_package_version[package][version.to_s]
|
79
|
+
info = spec.info
|
80
|
+
info = info.select { |p, i| @active_platforms.include?(p) }
|
81
|
+
|
82
|
+
info.flat_map { |_, i| i[:dependencies] }.each do |n, cs|
|
83
|
+
deps[n] ||= []
|
84
|
+
deps[n].concat cs
|
85
|
+
end
|
86
|
+
|
87
|
+
# FIXME: ruby_constraints ???
|
88
|
+
end
|
89
|
+
|
90
|
+
deps
|
91
|
+
end
|
92
|
+
|
93
|
+
def root_dependencies
|
94
|
+
deps = { "~arguments" => [] }
|
95
|
+
|
96
|
+
@gemfile.gems.select do |_, _, options|
|
97
|
+
next true unless platforms = options[:platforms]
|
98
|
+
!([*platforms] & [:ruby, :mri]).empty?
|
99
|
+
end.each do |name, constraints, _|
|
100
|
+
deps[name] ||= []
|
101
|
+
deps[name].concat constraints.flatten
|
102
|
+
end
|
103
|
+
|
104
|
+
deps.values.each(&:uniq!)
|
105
|
+
|
106
|
+
deps
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_dependency(package, requirement)
|
110
|
+
::PubGrub::VersionConstraint.new(@packages[package], range: to_range(requirement))
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def fetch_package_info(package)
|
116
|
+
return if @specs_by_package_version.key?(package)
|
117
|
+
|
118
|
+
specs = []
|
119
|
+
@catalogs.each do |catalog|
|
120
|
+
if catalog.nil?
|
121
|
+
break unless specs.empty?
|
122
|
+
next
|
123
|
+
end
|
124
|
+
|
125
|
+
if info = catalog.gem_info(package.name)
|
126
|
+
@cached_specs[catalog][package.name] ||=
|
127
|
+
begin
|
128
|
+
grouped_versions = info.to_a.map do |full_version, attributes|
|
129
|
+
version, platform = full_version.split("-", 2)
|
130
|
+
platform ||= "ruby"
|
131
|
+
[version, platform, attributes]
|
132
|
+
end.group_by(&:first)
|
133
|
+
|
134
|
+
grouped_versions.map { |version, tuples| Spec.new(catalog, package.name, version, tuples.map { |_, p, a| [p, a] }) }
|
135
|
+
end
|
136
|
+
|
137
|
+
specs.concat @cached_specs[catalog][package.name]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
@specs_by_package_version[package] = {}
|
142
|
+
specs.each do |spec|
|
143
|
+
# TODO: are we going to find specs in multiple catalogs this way?
|
144
|
+
@specs_by_package_version[package][spec.version] = spec
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_range(constraints)
|
149
|
+
requirement = Gel::Support::GemRequirement.new(constraints)
|
150
|
+
::PubGrub::RubyGems.requirement_to_range(requirement)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/gel/runtime.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../gel"
|
4
|
+
|
5
|
+
dir = ENV["GEL_STORE"] || "~/.local/gel"
|
6
|
+
dir = File.expand_path(dir)
|
7
|
+
|
8
|
+
unless Dir.exist?(dir)
|
9
|
+
require "fileutils"
|
10
|
+
FileUtils.mkdir_p(dir)
|
11
|
+
end
|
12
|
+
|
13
|
+
dir = File.realpath(dir)
|
14
|
+
|
15
|
+
stores = {}
|
16
|
+
Gel::Environment.store_set.each do |key|
|
17
|
+
subdir = File.join(dir, key)
|
18
|
+
Dir.mkdir(subdir) unless Dir.exist?(subdir)
|
19
|
+
stores[key] = Gel::Store.new(subdir)
|
20
|
+
end
|
21
|
+
store = Gel::MultiStore.new(dir, stores)
|
22
|
+
|
23
|
+
Gel::Environment.open(Gel::LockedStore.new(store))
|
24
|
+
|
25
|
+
if ENV["GEL_LOCKFILE"] && ENV["GEL_LOCKFILE"] != ""
|
26
|
+
Gel::Environment.activate(output: $stderr)
|
27
|
+
end
|
data/lib/gel/store.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "db"
|
4
|
+
|
5
|
+
class Gel::Store
|
6
|
+
attr_reader :root
|
7
|
+
attr_reader :monitor
|
8
|
+
|
9
|
+
def initialize(root)
|
10
|
+
@root = File.realpath(File.expand_path(root))
|
11
|
+
@primary_db = Gel::DB.new(root, "store")
|
12
|
+
@lib_db = Gel::DB.new(root, "libs")
|
13
|
+
@rlib_db = Gel::DB::File.new(root, "meta")
|
14
|
+
|
15
|
+
@monitor = Monitor.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def stub_set
|
19
|
+
@stub_set ||= Gel::StubSet.new(@root)
|
20
|
+
end
|
21
|
+
|
22
|
+
def paths
|
23
|
+
[@root.dup]
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_gem(name, version, bindir, executables, require_paths, dependencies, extensions)
|
27
|
+
name = normalize_string(name)
|
28
|
+
version = normalize_string(version)
|
29
|
+
bindir = normalize_string(bindir)
|
30
|
+
executables = executables.map { |v| normalize_string(v) }
|
31
|
+
require_paths = require_paths.map { |v| normalize_string(v) }
|
32
|
+
_dependencies = {}
|
33
|
+
dependencies.each do |key, dep|
|
34
|
+
_dependencies[normalize_string(key)] = dep.map { |pair| pair.map { |v| normalize_string(v) } }
|
35
|
+
end
|
36
|
+
dependencies = _dependencies
|
37
|
+
extensions = !!extensions
|
38
|
+
|
39
|
+
@primary_db.writing do
|
40
|
+
vs = @primary_db["v/#{name}"] || []
|
41
|
+
raise "already installed" if vs.include?(version)
|
42
|
+
vs << version
|
43
|
+
vs.sort_by! { |v| Gel::Support::GemVersion.new(v) }
|
44
|
+
vs.reverse!
|
45
|
+
@primary_db["v/#{name}"] = vs
|
46
|
+
|
47
|
+
d = {}
|
48
|
+
d[:bindir] = bindir unless bindir == "bin"
|
49
|
+
d[:executables] = executables unless executables.empty?
|
50
|
+
d[:require_paths] = require_paths unless require_paths == ["lib"]
|
51
|
+
d[:dependencies] = dependencies unless dependencies.empty?
|
52
|
+
d[:extensions] = extensions if extensions
|
53
|
+
@primary_db["i/#{name}/#{version}"] = d
|
54
|
+
|
55
|
+
yield if block_given?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_lib(name, version, files)
|
60
|
+
name = normalize_string(name)
|
61
|
+
version = version.is_a?(Array) ? version.map { |v| normalize_string(v) } : normalize_string(version)
|
62
|
+
files = files.map { |v| normalize_string(v) }
|
63
|
+
|
64
|
+
@lib_db.writing do
|
65
|
+
@rlib_db.writing do |rst|
|
66
|
+
files.each do |file|
|
67
|
+
h = @lib_db[file] || {}
|
68
|
+
d = h[name] || []
|
69
|
+
raise "already installed" if d.include?(version)
|
70
|
+
d << version
|
71
|
+
h[name] = d.sort_by.with_index { |(v, _), idx| [Gel::Support::GemVersion.new(v), -idx] }.reverse
|
72
|
+
@lib_db[file] = h
|
73
|
+
end
|
74
|
+
|
75
|
+
v, d = version
|
76
|
+
ls = @rlib_db["#{name}-#{v}"] || []
|
77
|
+
unless sls = ls.assoc(d)
|
78
|
+
sls = [d, []]
|
79
|
+
ls << sls
|
80
|
+
end
|
81
|
+
sls.last.concat files
|
82
|
+
@rlib_db["#{name}-#{v}"] = ls
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def gem?(name, version, _platform = nil)
|
88
|
+
!!gem_info(name, version)
|
89
|
+
end
|
90
|
+
|
91
|
+
def gem(name, version)
|
92
|
+
info = gem_info(name, version)
|
93
|
+
info && _gem(name, version, info)
|
94
|
+
end
|
95
|
+
|
96
|
+
def gems(name_version_pairs)
|
97
|
+
result = {}
|
98
|
+
|
99
|
+
name_version_pairs.each do |name, version|
|
100
|
+
if info = gem_info(name, version)
|
101
|
+
result[name] = _gem(name, version, info)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
result
|
106
|
+
end
|
107
|
+
|
108
|
+
def gem_root(name, version)
|
109
|
+
"#{@root}/gems/#{name}-#{version}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def extension_path(name, version)
|
113
|
+
"#{@root}/ext/#{name}-#{version}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def prepare(versions)
|
117
|
+
end
|
118
|
+
|
119
|
+
def libs_for_gems(versions)
|
120
|
+
@rlib_db.reading do
|
121
|
+
versions.each do |name, version|
|
122
|
+
if libs = @rlib_db["#{name}-#{version}"]
|
123
|
+
yield name, version, libs
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def gems_for_lib(file)
|
130
|
+
if h = @lib_db[file]
|
131
|
+
h.each do |name, versions|
|
132
|
+
versions.each do |version|
|
133
|
+
if version.is_a?(Array)
|
134
|
+
version, subdir = version
|
135
|
+
yield gem(name, version), subdir
|
136
|
+
else
|
137
|
+
yield gem(name, version)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def each(gem_name = nil)
|
145
|
+
return enum_for(__callee__, gem_name) unless block_given?
|
146
|
+
|
147
|
+
if gem_name
|
148
|
+
@primary_db.reading do
|
149
|
+
return unless vs = @primary_db["v/#{gem_name}"]
|
150
|
+
vs.each do |version|
|
151
|
+
if info = @primary_db["i/#{gem_name}/#{version}"]
|
152
|
+
yield _gem(gem_name, version, info)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
else
|
157
|
+
gem_names = []
|
158
|
+
@primary_db.each_key do |k|
|
159
|
+
gem_names << $1 if k =~ /\Av\/(.*)\z/
|
160
|
+
end
|
161
|
+
|
162
|
+
block = Proc.new
|
163
|
+
gem_names.each do |n|
|
164
|
+
each(n, &block)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def inspect
|
170
|
+
content = each.map { |g| "#{g.name}-#{g.version}" }
|
171
|
+
content = ["(none)"] if content.empty?
|
172
|
+
content.sort!
|
173
|
+
|
174
|
+
"#<#{self.class} root=#{@root.inspect} content=#{content.join(",")}>"
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def _gem(name, version, info)
|
180
|
+
info = inflate_info(info)
|
181
|
+
extensions = extension_path(name, version) if info[:extensions]
|
182
|
+
Gel::StoreGem.new(gem_root(name, version), name, version, extensions, info)
|
183
|
+
end
|
184
|
+
|
185
|
+
def gem_info(name, version)
|
186
|
+
@primary_db["i/#{name}/#{version}"]
|
187
|
+
end
|
188
|
+
|
189
|
+
def inflate_info(d)
|
190
|
+
d = d.dup
|
191
|
+
d[:bindir] = "bin" unless d.key?(:bindir)
|
192
|
+
d[:executables] = [] unless d.key?(:executables)
|
193
|
+
d[:require_paths] = ["lib"] unless d.key?(:require_paths)
|
194
|
+
d[:dependencies] = {} unless d.key?(:dependencies)
|
195
|
+
d
|
196
|
+
end
|
197
|
+
|
198
|
+
# Almost every string we store is pure ASCII, and binary strings
|
199
|
+
# marshal better.
|
200
|
+
def normalize_string(str)
|
201
|
+
str = str.to_s
|
202
|
+
str = str.b if str.ascii_only?
|
203
|
+
str
|
204
|
+
end
|
205
|
+
end
|