carat 1.9.9.pre1

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 (184) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +24 -0
  5. data/CHANGELOG.md +2006 -0
  6. data/CODE_OF_CONDUCT.md +40 -0
  7. data/CONTRIBUTING.md +23 -0
  8. data/DEVELOPMENT.md +119 -0
  9. data/ISSUES.md +96 -0
  10. data/LICENSE.md +23 -0
  11. data/README.md +32 -0
  12. data/Rakefile +308 -0
  13. data/bin/carat +21 -0
  14. data/bin/carat_ruby +56 -0
  15. data/carat.gemspec +32 -0
  16. data/lib/carat.rb +446 -0
  17. data/lib/carat/anonymizable_uri.rb +32 -0
  18. data/lib/carat/capistrano.rb +16 -0
  19. data/lib/carat/cli.rb +407 -0
  20. data/lib/carat/cli/binstubs.rb +38 -0
  21. data/lib/carat/cli/cache.rb +35 -0
  22. data/lib/carat/cli/check.rb +35 -0
  23. data/lib/carat/cli/clean.rb +26 -0
  24. data/lib/carat/cli/common.rb +56 -0
  25. data/lib/carat/cli/config.rb +84 -0
  26. data/lib/carat/cli/console.rb +38 -0
  27. data/lib/carat/cli/exec.rb +44 -0
  28. data/lib/carat/cli/gem.rb +195 -0
  29. data/lib/carat/cli/init.rb +33 -0
  30. data/lib/carat/cli/inject.rb +33 -0
  31. data/lib/carat/cli/install.rb +156 -0
  32. data/lib/carat/cli/open.rb +23 -0
  33. data/lib/carat/cli/outdated.rb +80 -0
  34. data/lib/carat/cli/package.rb +45 -0
  35. data/lib/carat/cli/platform.rb +43 -0
  36. data/lib/carat/cli/show.rb +74 -0
  37. data/lib/carat/cli/update.rb +73 -0
  38. data/lib/carat/cli/viz.rb +27 -0
  39. data/lib/carat/constants.rb +5 -0
  40. data/lib/carat/current_ruby.rb +183 -0
  41. data/lib/carat/definition.rb +628 -0
  42. data/lib/carat/dep_proxy.rb +43 -0
  43. data/lib/carat/dependency.rb +110 -0
  44. data/lib/carat/deployment.rb +59 -0
  45. data/lib/carat/deprecate.rb +15 -0
  46. data/lib/carat/dsl.rb +331 -0
  47. data/lib/carat/endpoint_specification.rb +76 -0
  48. data/lib/carat/env.rb +75 -0
  49. data/lib/carat/environment.rb +42 -0
  50. data/lib/carat/fetcher.rb +423 -0
  51. data/lib/carat/friendly_errors.rb +85 -0
  52. data/lib/carat/gem_helper.rb +180 -0
  53. data/lib/carat/gem_helpers.rb +26 -0
  54. data/lib/carat/gem_installer.rb +9 -0
  55. data/lib/carat/gem_path_manipulation.rb +8 -0
  56. data/lib/carat/gem_tasks.rb +2 -0
  57. data/lib/carat/graph.rb +169 -0
  58. data/lib/carat/index.rb +197 -0
  59. data/lib/carat/injector.rb +64 -0
  60. data/lib/carat/installer.rb +339 -0
  61. data/lib/carat/lazy_specification.rb +83 -0
  62. data/lib/carat/lockfile_parser.rb +167 -0
  63. data/lib/carat/match_platform.rb +13 -0
  64. data/lib/carat/psyched_yaml.rb +26 -0
  65. data/lib/carat/remote_specification.rb +57 -0
  66. data/lib/carat/resolver.rb +334 -0
  67. data/lib/carat/retry.rb +60 -0
  68. data/lib/carat/ruby_dsl.rb +11 -0
  69. data/lib/carat/ruby_version.rb +117 -0
  70. data/lib/carat/rubygems_ext.rb +170 -0
  71. data/lib/carat/rubygems_integration.rb +619 -0
  72. data/lib/carat/runtime.rb +289 -0
  73. data/lib/carat/settings.rb +208 -0
  74. data/lib/carat/setup.rb +24 -0
  75. data/lib/carat/shared_helpers.rb +149 -0
  76. data/lib/carat/similarity_detector.rb +63 -0
  77. data/lib/carat/source.rb +46 -0
  78. data/lib/carat/source/git.rb +294 -0
  79. data/lib/carat/source/git/git_proxy.rb +162 -0
  80. data/lib/carat/source/path.rb +226 -0
  81. data/lib/carat/source/path/installer.rb +43 -0
  82. data/lib/carat/source/rubygems.rb +381 -0
  83. data/lib/carat/source_list.rb +101 -0
  84. data/lib/carat/spec_set.rb +154 -0
  85. data/lib/carat/ssl_certs/.document +1 -0
  86. data/lib/carat/ssl_certs/AddTrustExternalCARoot-2048.pem +25 -0
  87. data/lib/carat/ssl_certs/AddTrustExternalCARoot.pem +32 -0
  88. data/lib/carat/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +14 -0
  89. data/lib/carat/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +23 -0
  90. data/lib/carat/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +28 -0
  91. data/lib/carat/ssl_certs/GeoTrustGlobalCA.pem +20 -0
  92. data/lib/carat/ssl_certs/certificate_manager.rb +66 -0
  93. data/lib/carat/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
  94. data/lib/carat/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +23 -0
  95. data/lib/carat/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +25 -0
  96. data/lib/carat/templates/Executable +16 -0
  97. data/lib/carat/templates/Executable.standalone +12 -0
  98. data/lib/carat/templates/Gemfile +4 -0
  99. data/lib/carat/templates/newgem/.travis.yml.tt +3 -0
  100. data/lib/carat/templates/newgem/CODE_OF_CONDUCT.md.tt +13 -0
  101. data/lib/carat/templates/newgem/Gemfile.tt +4 -0
  102. data/lib/carat/templates/newgem/LICENSE.txt.tt +21 -0
  103. data/lib/carat/templates/newgem/README.md.tt +39 -0
  104. data/lib/carat/templates/newgem/Rakefile.tt +25 -0
  105. data/lib/carat/templates/newgem/bin/console.tt +14 -0
  106. data/lib/carat/templates/newgem/bin/setup.tt +7 -0
  107. data/lib/carat/templates/newgem/exe/newgem.tt +3 -0
  108. data/lib/carat/templates/newgem/ext/newgem/extconf.rb.tt +3 -0
  109. data/lib/carat/templates/newgem/ext/newgem/newgem.c.tt +9 -0
  110. data/lib/carat/templates/newgem/ext/newgem/newgem.h.tt +6 -0
  111. data/lib/carat/templates/newgem/gitignore.tt +16 -0
  112. data/lib/carat/templates/newgem/lib/newgem.rb.tt +12 -0
  113. data/lib/carat/templates/newgem/lib/newgem/version.rb.tt +7 -0
  114. data/lib/carat/templates/newgem/newgem.gemspec.tt +43 -0
  115. data/lib/carat/templates/newgem/rspec.tt +2 -0
  116. data/lib/carat/templates/newgem/spec/newgem_spec.rb.tt +11 -0
  117. data/lib/carat/templates/newgem/spec/spec_helper.rb.tt +2 -0
  118. data/lib/carat/templates/newgem/test/minitest_helper.rb.tt +4 -0
  119. data/lib/carat/templates/newgem/test/test_newgem.rb.tt +11 -0
  120. data/lib/carat/ui.rb +7 -0
  121. data/lib/carat/ui/rg_proxy.rb +21 -0
  122. data/lib/carat/ui/shell.rb +103 -0
  123. data/lib/carat/ui/silent.rb +44 -0
  124. data/lib/carat/vendor/molinillo/lib/molinillo.rb +5 -0
  125. data/lib/carat/vendor/molinillo/lib/molinillo/dependency_graph.rb +266 -0
  126. data/lib/carat/vendor/molinillo/lib/molinillo/errors.rb +69 -0
  127. data/lib/carat/vendor/molinillo/lib/molinillo/gem_metadata.rb +3 -0
  128. data/lib/carat/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +90 -0
  129. data/lib/carat/vendor/molinillo/lib/molinillo/modules/ui.rb +63 -0
  130. data/lib/carat/vendor/molinillo/lib/molinillo/resolution.rb +415 -0
  131. data/lib/carat/vendor/molinillo/lib/molinillo/resolver.rb +43 -0
  132. data/lib/carat/vendor/molinillo/lib/molinillo/state.rb +43 -0
  133. data/lib/carat/vendor/net/http/faster.rb +26 -0
  134. data/lib/carat/vendor/net/http/persistent.rb +1230 -0
  135. data/lib/carat/vendor/net/http/persistent/ssl_reuse.rb +128 -0
  136. data/lib/carat/vendor/thor/lib/thor.rb +484 -0
  137. data/lib/carat/vendor/thor/lib/thor/actions.rb +319 -0
  138. data/lib/carat/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  139. data/lib/carat/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  140. data/lib/carat/vendor/thor/lib/thor/actions/directory.rb +118 -0
  141. data/lib/carat/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  142. data/lib/carat/vendor/thor/lib/thor/actions/file_manipulation.rb +316 -0
  143. data/lib/carat/vendor/thor/lib/thor/actions/inject_into_file.rb +107 -0
  144. data/lib/carat/vendor/thor/lib/thor/base.rb +656 -0
  145. data/lib/carat/vendor/thor/lib/thor/command.rb +133 -0
  146. data/lib/carat/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +77 -0
  147. data/lib/carat/vendor/thor/lib/thor/core_ext/io_binary_read.rb +10 -0
  148. data/lib/carat/vendor/thor/lib/thor/core_ext/ordered_hash.rb +98 -0
  149. data/lib/carat/vendor/thor/lib/thor/error.rb +32 -0
  150. data/lib/carat/vendor/thor/lib/thor/group.rb +281 -0
  151. data/lib/carat/vendor/thor/lib/thor/invocation.rb +178 -0
  152. data/lib/carat/vendor/thor/lib/thor/line_editor.rb +17 -0
  153. data/lib/carat/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  154. data/lib/carat/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  155. data/lib/carat/vendor/thor/lib/thor/parser.rb +4 -0
  156. data/lib/carat/vendor/thor/lib/thor/parser/argument.rb +73 -0
  157. data/lib/carat/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  158. data/lib/carat/vendor/thor/lib/thor/parser/option.rb +125 -0
  159. data/lib/carat/vendor/thor/lib/thor/parser/options.rb +218 -0
  160. data/lib/carat/vendor/thor/lib/thor/rake_compat.rb +71 -0
  161. data/lib/carat/vendor/thor/lib/thor/runner.rb +322 -0
  162. data/lib/carat/vendor/thor/lib/thor/shell.rb +81 -0
  163. data/lib/carat/vendor/thor/lib/thor/shell/basic.rb +421 -0
  164. data/lib/carat/vendor/thor/lib/thor/shell/color.rb +149 -0
  165. data/lib/carat/vendor/thor/lib/thor/shell/html.rb +126 -0
  166. data/lib/carat/vendor/thor/lib/thor/util.rb +267 -0
  167. data/lib/carat/vendor/thor/lib/thor/version.rb +3 -0
  168. data/lib/carat/vendored_fileutils.rb +9 -0
  169. data/lib/carat/vendored_molinillo.rb +2 -0
  170. data/lib/carat/vendored_persistent.rb +11 -0
  171. data/lib/carat/vendored_thor.rb +3 -0
  172. data/lib/carat/version.rb +6 -0
  173. data/lib/carat/vlad.rb +11 -0
  174. data/lib/carat/worker.rb +73 -0
  175. data/man/carat-config.ronn +178 -0
  176. data/man/carat-exec.ronn +136 -0
  177. data/man/carat-install.ronn +383 -0
  178. data/man/carat-package.ronn +66 -0
  179. data/man/carat-platform.ronn +42 -0
  180. data/man/carat-update.ronn +188 -0
  181. data/man/carat.ronn +98 -0
  182. data/man/gemfile.5.ronn +473 -0
  183. data/man/index.txt +7 -0
  184. metadata +321 -0
@@ -0,0 +1,289 @@
1
+ require "digest/sha1"
2
+
3
+ module Carat
4
+ class Runtime < Environment
5
+ include SharedHelpers
6
+
7
+ def setup(*groups)
8
+ groups.map! { |g| g.to_sym }
9
+
10
+ # Has to happen first
11
+ clean_load_path
12
+
13
+ specs = groups.any? ? @definition.specs_for(groups) : requested_specs
14
+
15
+ setup_environment
16
+ Carat.rubygems.replace_entrypoints(specs)
17
+
18
+ # Activate the specs
19
+ specs.each do |spec|
20
+ unless spec.loaded_from
21
+ raise GemNotFound, "#{spec.full_name} is missing. Run `carat` to get it."
22
+ end
23
+
24
+ if activated_spec = Carat.rubygems.loaded_specs(spec.name) and activated_spec.version != spec.version
25
+ e = Gem::LoadError.new "You have already activated #{activated_spec.name} #{activated_spec.version}, " \
26
+ "but your Gemfile requires #{spec.name} #{spec.version}. Prepending " \
27
+ "`carat exec` to your command may solve this."
28
+ e.name = spec.name
29
+ if e.respond_to?(:requirement=)
30
+ e.requirement = Gem::Requirement.new(spec.version.to_s)
31
+ else
32
+ e.version_requirement = Gem::Requirement.new(spec.version.to_s)
33
+ end
34
+ raise e
35
+ end
36
+
37
+ Carat.rubygems.mark_loaded(spec)
38
+ load_paths = spec.load_paths.reject {|path| $LOAD_PATH.include?(path)}
39
+ $LOAD_PATH.unshift(*load_paths)
40
+ end
41
+
42
+ setup_manpath
43
+
44
+ lock
45
+
46
+ self
47
+ end
48
+
49
+ REQUIRE_ERRORS = [
50
+ /^no such file to load -- (.+)$/i,
51
+ /^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
52
+ /^Missing API definition file in (.+)$/i,
53
+ /^cannot load such file -- (.+)$/i,
54
+ /^dlopen\([^)]*\): Library not loaded: (.+)$/i,
55
+ ]
56
+
57
+ def require(*groups)
58
+ groups.map! { |g| g.to_sym }
59
+ groups = [:default] if groups.empty?
60
+
61
+ @definition.dependencies.each do |dep|
62
+ # Skip the dependency if it is not in any of the requested
63
+ # groups
64
+ next unless ((dep.groups & groups).any? && dep.current_platform?)
65
+
66
+ required_file = nil
67
+
68
+ begin
69
+ # Loop through all the specified autorequires for the
70
+ # dependency. If there are none, use the dependency's name
71
+ # as the autorequire.
72
+ Array(dep.autorequire || dep.name).each do |file|
73
+ # Allow `require: true` as an alias for `require: <name>`
74
+ file = dep.name if file == true
75
+ required_file = file
76
+ Kernel.require file
77
+ end
78
+ rescue LoadError => e
79
+ REQUIRE_ERRORS.find { |r| r =~ e.message }
80
+ raise if dep.autorequire || $1 != required_file
81
+
82
+ if dep.autorequire.nil? && dep.name.include?('-')
83
+ begin
84
+ namespaced_file = dep.name.gsub('-', '/')
85
+ Kernel.require namespaced_file
86
+ rescue LoadError => e
87
+ REQUIRE_ERRORS.find { |r| r =~ e.message }
88
+ raise if $1 != namespaced_file
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def dependencies_for(*groups)
96
+ if groups.empty?
97
+ dependencies
98
+ else
99
+ dependencies.select { |d| (groups & d.groups).any? }
100
+ end
101
+ end
102
+
103
+ alias gems specs
104
+
105
+ def cache(custom_path = nil)
106
+ cache_path = Carat.app_cache(custom_path)
107
+ FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path)
108
+
109
+ Carat.ui.info "Updating files in #{Carat.settings.app_cache_path}"
110
+ specs.each do |spec|
111
+ next if spec.name == 'carat'
112
+ spec.source.send(:fetch_gem, spec) if Carat.settings[:cache_all_platforms] && spec.source.respond_to?(:fetch_gem, true)
113
+ spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache)
114
+ end
115
+
116
+ Dir[cache_path.join("*/.git")].each do |git_dir|
117
+ FileUtils.rm_rf(git_dir)
118
+ FileUtils.touch(File.expand_path("../.caratcache", git_dir))
119
+ end
120
+
121
+ prune_cache(cache_path) unless Carat.settings[:no_prune]
122
+ end
123
+
124
+ def prune_cache(cache_path)
125
+ FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path)
126
+ resolve = @definition.resolve
127
+ prune_gem_cache(resolve, cache_path)
128
+ prune_git_and_path_cache(resolve, cache_path)
129
+ end
130
+
131
+ def clean(dry_run = false)
132
+ gem_bins = Dir["#{Gem.dir}/bin/*"]
133
+ git_dirs = Dir["#{Gem.dir}/carat/gems/*"]
134
+ git_cache_dirs = Dir["#{Gem.dir}/cache/carat/git/*"]
135
+ gem_dirs = Dir["#{Gem.dir}/gems/*"]
136
+ gem_files = Dir["#{Gem.dir}/cache/*.gem"]
137
+ gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"]
138
+ spec_gem_paths = []
139
+ # need to keep git sources around
140
+ spec_git_paths = @definition.spec_git_paths
141
+ spec_git_cache_dirs = []
142
+ spec_gem_executables = []
143
+ spec_cache_paths = []
144
+ spec_gemspec_paths = []
145
+ specs.each do |spec|
146
+ spec_gem_paths << spec.full_gem_path
147
+ # need to check here in case gems are nested like for the rails git repo
148
+ md = %r{(.+carat/gems/.+-[a-f0-9]{7,12})}.match(spec.full_gem_path)
149
+ spec_git_paths << md[1] if md
150
+ spec_gem_executables << spec.executables.collect do |executable|
151
+ e = "#{Carat.rubygems.gem_bindir}/#{executable}"
152
+ [e, "#{e}.bat"]
153
+ end
154
+ spec_cache_paths << spec.cache_file
155
+ spec_gemspec_paths << spec.spec_file
156
+ spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Carat::Source::Git)
157
+ end
158
+ spec_gem_paths.uniq!
159
+ spec_gem_executables.flatten!
160
+
161
+ stale_gem_bins = gem_bins - spec_gem_executables
162
+ stale_git_dirs = git_dirs - spec_git_paths
163
+ stale_git_cache_dirs = git_cache_dirs - spec_git_cache_dirs
164
+ stale_gem_dirs = gem_dirs - spec_gem_paths
165
+ stale_gem_files = gem_files - spec_cache_paths
166
+ stale_gemspec_files = gemspec_files - spec_gemspec_paths
167
+
168
+ output = stale_gem_dirs.collect do |gem_dir|
169
+ full_name = Pathname.new(gem_dir).basename.to_s
170
+
171
+ parts = full_name.split('-')
172
+ name = parts[0..-2].join('-')
173
+ version = parts.last
174
+ output = "#{name} (#{version})"
175
+
176
+ if dry_run
177
+ Carat.ui.info "Would have removed #{output}"
178
+ else
179
+ Carat.ui.info "Removing #{output}"
180
+ FileUtils.rm_rf(gem_dir)
181
+ end
182
+
183
+ output
184
+ end + stale_git_dirs.collect do |gem_dir|
185
+ full_name = Pathname.new(gem_dir).basename.to_s
186
+
187
+ parts = full_name.split('-')
188
+ name = parts[0..-2].join('-')
189
+ revision = parts[-1]
190
+ output = "#{name} (#{revision})"
191
+
192
+ if dry_run
193
+ Carat.ui.info "Would have removed #{output}"
194
+ else
195
+ Carat.ui.info "Removing #{output}"
196
+ FileUtils.rm_rf(gem_dir)
197
+ end
198
+
199
+ output
200
+ end
201
+
202
+ unless dry_run
203
+ stale_gem_bins.each { |bin| FileUtils.rm(bin) if File.exist?(bin) }
204
+ stale_gem_files.each { |file| FileUtils.rm(file) if File.exist?(file) }
205
+ stale_gemspec_files.each { |file| FileUtils.rm(file) if File.exist?(file) }
206
+ stale_git_cache_dirs.each { |dir| FileUtils.rm_rf(dir) if File.exist?(dir) }
207
+ end
208
+
209
+ output
210
+ end
211
+
212
+ def setup_environment
213
+ begin
214
+ ENV["BUNDLE_BIN_PATH"] = Carat.rubygems.bin_path("carat", "carat", VERSION)
215
+ rescue Gem::GemNotFoundException
216
+ ENV["BUNDLE_BIN_PATH"] = File.expand_path("../../../bin/carat", __FILE__)
217
+ end
218
+
219
+ # Set BUNDLE_GEMFILE
220
+ ENV["BUNDLE_GEMFILE"] = default_gemfile.to_s
221
+
222
+ SharedHelpers.set_bundle_environment
223
+ end
224
+
225
+ private
226
+
227
+ def prune_gem_cache(resolve, cache_path)
228
+ cached = Dir["#{cache_path}/*.gem"]
229
+
230
+ cached = cached.delete_if do |path|
231
+ spec = Carat.rubygems.spec_from_gem path
232
+
233
+ resolve.any? do |s|
234
+ s.name == spec.name && s.version == spec.version && !s.source.is_a?(Carat::Source::Git)
235
+ end
236
+ end
237
+
238
+ if cached.any?
239
+ Carat.ui.info "Removing outdated .gem files from #{Carat.settings.app_cache_path}"
240
+
241
+ cached.each do |path|
242
+ Carat.ui.info " * #{File.basename(path)}"
243
+ File.delete(path)
244
+ end
245
+ end
246
+ end
247
+
248
+ def prune_git_and_path_cache(resolve, cache_path)
249
+ cached = Dir["#{cache_path}/*/.caratcache"]
250
+
251
+ cached = cached.delete_if do |path|
252
+ name = File.basename(File.dirname(path))
253
+
254
+ resolve.any? do |s|
255
+ source = s.source
256
+ source.respond_to?(:app_cache_dirname) && source.app_cache_dirname == name
257
+ end
258
+ end
259
+
260
+ if cached.any?
261
+ Carat.ui.info "Removing outdated git and path gems from #{Carat.settings.app_cache_path}"
262
+
263
+ cached.each do |path|
264
+ path = File.dirname(path)
265
+ Carat.ui.info " * #{File.basename(path)}"
266
+ FileUtils.rm_rf(path)
267
+ end
268
+ end
269
+ end
270
+
271
+ def setup_manpath
272
+ # Store original MANPATH for restoration later in with_clean_env()
273
+ ENV['BUNDLE_ORIG_MANPATH'] = ENV['MANPATH']
274
+
275
+ # Add man/ subdirectories from activated bundles to MANPATH for man(1)
276
+ manuals = $LOAD_PATH.map do |path|
277
+ man_subdir = path.sub(/lib$/, 'man')
278
+ man_subdir unless Dir[man_subdir + '/man?/'].empty?
279
+ end.compact
280
+
281
+ unless manuals.empty?
282
+ ENV['MANPATH'] = manuals.concat(
283
+ ENV['MANPATH'].to_s.split(File::PATH_SEPARATOR)
284
+ ).uniq.join(File::PATH_SEPARATOR)
285
+ end
286
+ end
287
+
288
+ end
289
+ end
@@ -0,0 +1,208 @@
1
+ require 'uri'
2
+
3
+ module Carat
4
+ class Settings
5
+ BOOL_KEYS = %w(frozen cache_all no_prune disable_local_branch_check gem.mit gem.coc).freeze
6
+
7
+ def initialize(root = nil)
8
+ @root = root
9
+ @local_config = load_config(local_config_file)
10
+ @global_config = load_config(global_config_file)
11
+ end
12
+
13
+ def [](name)
14
+ key = key_for(name)
15
+ value = (@local_config[key] || ENV[key] || @global_config[key])
16
+
17
+ if !value.nil? && is_bool(name)
18
+ to_bool(value)
19
+ else
20
+ value
21
+ end
22
+ end
23
+
24
+ def []=(key, value)
25
+ local_config_file or raise GemfileNotFound, "Could not locate Gemfile"
26
+ set_key(key, value, @local_config, local_config_file)
27
+ end
28
+
29
+ alias :set_local :[]=
30
+
31
+ def delete(key)
32
+ @local_config.delete(key_for(key))
33
+ end
34
+
35
+ def set_global(key, value)
36
+ set_key(key, value, @global_config, global_config_file)
37
+ end
38
+
39
+ def all
40
+ env_keys = ENV.keys.select { |k| k =~ /BUNDLE_.*/ }
41
+
42
+ keys = @global_config.keys | @local_config.keys | env_keys
43
+
44
+ keys.map do |key|
45
+ key.sub(/^BUNDLE_/, '').gsub(/__/, ".").downcase
46
+ end
47
+ end
48
+
49
+ def local_overrides
50
+ repos = {}
51
+ all.each do |k|
52
+ if k =~ /^local\./
53
+ repos[$'] = self[k]
54
+ end
55
+ end
56
+ repos
57
+ end
58
+
59
+ def gem_mirrors
60
+ all.inject({}) do |h, k|
61
+ if k =~ /^mirror\./
62
+ uri = normalize_uri($')
63
+ h[uri] = normalize_uri(self[k])
64
+ end
65
+ h
66
+ end
67
+ end
68
+
69
+ def locations(key)
70
+ key = key_for(key)
71
+ locations = {}
72
+ locations[:local] = @local_config[key] if @local_config.key?(key)
73
+ locations[:env] = ENV[key] if ENV[key]
74
+ locations[:global] = @global_config[key] if @global_config.key?(key)
75
+ locations
76
+ end
77
+
78
+ def pretty_values_for(exposed_key)
79
+ key = key_for(exposed_key)
80
+
81
+ locations = []
82
+ if @local_config.key?(key)
83
+ locations << "Set for your local app (#{local_config_file}): #{@local_config[key].inspect}"
84
+ end
85
+
86
+ if value = ENV[key]
87
+ locations << "Set via #{key}: #{value.inspect}"
88
+ end
89
+
90
+ if @global_config.key?(key)
91
+ locations << "Set for the current user (#{global_config_file}): #{@global_config[key].inspect}"
92
+ end
93
+
94
+ return ["You have not configured a value for `#{exposed_key}`"] if locations.empty?
95
+ locations
96
+ end
97
+
98
+ def without=(array)
99
+ self[:without] = (array.empty? ? nil : array.join(":")) if array
100
+ end
101
+
102
+ def without
103
+ self[:without] ? self[:without].split(":").map { |w| w.to_sym } : []
104
+ end
105
+
106
+ # @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"]
107
+ def path
108
+ key = key_for(:path)
109
+ path = ENV[key] || @global_config[key]
110
+ return path if path && !@local_config.key?(key)
111
+
112
+ if path = self[:path]
113
+ "#{path}/#{Carat.ruby_scope}"
114
+ else
115
+ Carat.rubygems.gem_dir
116
+ end
117
+ end
118
+
119
+ def allow_sudo?
120
+ !@local_config.key?(key_for(:path))
121
+ end
122
+
123
+ def ignore_config?
124
+ ENV['BUNDLE_IGNORE_CONFIG']
125
+ end
126
+
127
+ def app_cache_path
128
+ @app_cache_path ||= begin
129
+ path = self[:cache_path] || "vendor/cache"
130
+ raise InvalidOption, "Cache path must be relative to the bundle path" if path.start_with?("/")
131
+ path
132
+ end
133
+ end
134
+
135
+ private
136
+ def key_for(key)
137
+ if key.is_a?(String) && /https?:/ =~ key
138
+ key = normalize_uri(key).to_s
139
+ end
140
+ key = key.to_s.gsub(".", "__").upcase
141
+ "BUNDLE_#{key}"
142
+ end
143
+
144
+ def is_bool(key)
145
+ BOOL_KEYS.include?(key.to_s)
146
+ end
147
+
148
+ def to_bool(value)
149
+ !(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i || value == false)
150
+ end
151
+
152
+ def set_key(key, value, hash, file)
153
+ key = key_for(key)
154
+
155
+ unless hash[key] == value
156
+ hash[key] = value
157
+ hash.delete(key) if value.nil?
158
+ FileUtils.mkdir_p(file.dirname)
159
+ require 'carat/psyched_yaml'
160
+ File.open(file, "w") { |f| f.puts YAML.dump(hash) }
161
+ end
162
+
163
+ value
164
+ end
165
+
166
+ def global_config_file
167
+ file = ENV["BUNDLE_CONFIG"] || File.join(Carat.rubygems.user_home, ".carat/config")
168
+ Pathname.new(file)
169
+ end
170
+
171
+ def local_config_file
172
+ Pathname.new(@root).join("config") if @root
173
+ end
174
+
175
+ def load_config(config_file)
176
+ valid_file = config_file && config_file.exist? && !config_file.size.zero?
177
+ if !ignore_config? && valid_file
178
+ config_regex = /^(BUNDLE_.+): (['"]?)(.*(?:\n(?!BUNDLE).+)?)\2$/
179
+ config_pairs = config_file.read.scan(config_regex).map do |m|
180
+ key, _, value = m
181
+ [convert_to_backward_compatible_key(key), value.gsub(/\s+/, " ").tr('"', "'")]
182
+ end
183
+ Hash[config_pairs]
184
+ else
185
+ {}
186
+ end
187
+ end
188
+
189
+ def convert_to_backward_compatible_key(key)
190
+ key = "#{key}/" if key =~ /https?:/i && key !~ %r[/\Z]
191
+ key = key.gsub(".", "__") if key.include?(".")
192
+ key
193
+ end
194
+
195
+ # TODO: duplicates Rubygems#normalize_uri
196
+ # TODO: is this the correct place to validate mirror URIs?
197
+ def normalize_uri(uri)
198
+ uri = uri.to_s
199
+ uri = "#{uri}/" unless uri =~ %r[/\Z]
200
+ uri = URI(uri)
201
+ unless uri.absolute?
202
+ raise ArgumentError, "Gem sources must be absolute. You provided '#{uri}'."
203
+ end
204
+ uri
205
+ end
206
+
207
+ end
208
+ end