gel 0.2.0

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