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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +74 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +39 -0
  5. data/exe/gel +13 -0
  6. data/lib/gel.rb +22 -0
  7. data/lib/gel/catalog.rb +153 -0
  8. data/lib/gel/catalog/common.rb +82 -0
  9. data/lib/gel/catalog/compact_index.rb +152 -0
  10. data/lib/gel/catalog/dependency_index.rb +125 -0
  11. data/lib/gel/catalog/legacy_index.rb +157 -0
  12. data/lib/gel/catalog/marshal_hacks.rb +16 -0
  13. data/lib/gel/command.rb +86 -0
  14. data/lib/gel/command/config.rb +11 -0
  15. data/lib/gel/command/env.rb +7 -0
  16. data/lib/gel/command/exec.rb +66 -0
  17. data/lib/gel/command/help.rb +7 -0
  18. data/lib/gel/command/install.rb +7 -0
  19. data/lib/gel/command/install_gem.rb +16 -0
  20. data/lib/gel/command/lock.rb +34 -0
  21. data/lib/gel/command/ruby.rb +10 -0
  22. data/lib/gel/command/shell_setup.rb +25 -0
  23. data/lib/gel/command/stub.rb +12 -0
  24. data/lib/gel/command/update.rb +11 -0
  25. data/lib/gel/compatibility.rb +4 -0
  26. data/lib/gel/compatibility/bundler.rb +54 -0
  27. data/lib/gel/compatibility/bundler/cli.rb +6 -0
  28. data/lib/gel/compatibility/bundler/friendly_errors.rb +3 -0
  29. data/lib/gel/compatibility/bundler/setup.rb +4 -0
  30. data/lib/gel/compatibility/rubygems.rb +192 -0
  31. data/lib/gel/compatibility/rubygems/command.rb +4 -0
  32. data/lib/gel/compatibility/rubygems/dependency_installer.rb +0 -0
  33. data/lib/gel/compatibility/rubygems/gem_runner.rb +6 -0
  34. data/lib/gel/config.rb +80 -0
  35. data/lib/gel/db.rb +294 -0
  36. data/lib/gel/direct_gem.rb +29 -0
  37. data/lib/gel/environment.rb +592 -0
  38. data/lib/gel/error.rb +104 -0
  39. data/lib/gel/gemfile_parser.rb +144 -0
  40. data/lib/gel/gemspec_parser.rb +95 -0
  41. data/lib/gel/git_catalog.rb +38 -0
  42. data/lib/gel/git_depot.rb +119 -0
  43. data/lib/gel/httpool.rb +148 -0
  44. data/lib/gel/installer.rb +251 -0
  45. data/lib/gel/lock_loader.rb +164 -0
  46. data/lib/gel/lock_parser.rb +64 -0
  47. data/lib/gel/locked_store.rb +126 -0
  48. data/lib/gel/multi_store.rb +96 -0
  49. data/lib/gel/package.rb +156 -0
  50. data/lib/gel/package/inspector.rb +23 -0
  51. data/lib/gel/package/installer.rb +267 -0
  52. data/lib/gel/path_catalog.rb +44 -0
  53. data/lib/gel/pinboard.rb +140 -0
  54. data/lib/gel/pub_grub/preference_strategy.rb +82 -0
  55. data/lib/gel/pub_grub/source.rb +153 -0
  56. data/lib/gel/runtime.rb +27 -0
  57. data/lib/gel/store.rb +205 -0
  58. data/lib/gel/store_catalog.rb +31 -0
  59. data/lib/gel/store_gem.rb +80 -0
  60. data/lib/gel/stub_set.rb +51 -0
  61. data/lib/gel/support/gem_platform.rb +225 -0
  62. data/lib/gel/support/gem_requirement.rb +264 -0
  63. data/lib/gel/support/gem_version.rb +398 -0
  64. data/lib/gel/support/tar.rb +13 -0
  65. data/lib/gel/support/tar/tar_header.rb +229 -0
  66. data/lib/gel/support/tar/tar_reader.rb +123 -0
  67. data/lib/gel/support/tar/tar_reader/entry.rb +154 -0
  68. data/lib/gel/support/tar/tar_writer.rb +339 -0
  69. data/lib/gel/tail_file.rb +205 -0
  70. data/lib/gel/version.rb +5 -0
  71. data/lib/gel/work_pool.rb +143 -0
  72. data/man/man1/gel-exec.1 +16 -0
  73. data/man/man1/gel-install.1 +16 -0
  74. data/man/man1/gel.1 +30 -0
  75. 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
@@ -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
@@ -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