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,24 @@
1
+ require 'carat/shared_helpers'
2
+
3
+ if Carat::SharedHelpers.in_bundle?
4
+ require 'carat'
5
+
6
+ if STDOUT.tty? || ENV['CARAT_FORCE_TTY']
7
+ begin
8
+ Carat.setup
9
+ rescue Carat::CaratError => e
10
+ puts "\e[31m#{e.message}\e[0m"
11
+ puts e.backtrace.join("\n") if ENV["DEBUG"]
12
+ if e.is_a?(Carat::GemNotFound)
13
+ puts "\e[33mRun `carat install` to install missing gems.\e[0m"
14
+ end
15
+ exit e.status_code
16
+ end
17
+ else
18
+ Carat.setup
19
+ end
20
+
21
+ # Add carat to the load path after disabling system gems
22
+ carat_lib = File.expand_path("../..", __FILE__)
23
+ $LOAD_PATH.unshift(carat_lib) unless $LOAD_PATH.include?(carat_lib)
24
+ end
@@ -0,0 +1,149 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+
4
+ require 'carat/constants'
5
+ require 'carat/rubygems_integration'
6
+ require 'carat/current_ruby'
7
+
8
+ module Gem
9
+ class Dependency
10
+ if !instance_methods.map { |m| m.to_s }.include?("requirement")
11
+ def requirement
12
+ version_requirements
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ module Carat
19
+ module SharedHelpers
20
+ attr_accessor :gem_loaded
21
+
22
+ def default_gemfile
23
+ gemfile = find_gemfile
24
+ raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
25
+ Pathname.new(gemfile)
26
+ end
27
+
28
+ def default_lockfile
29
+ gemfile = default_gemfile
30
+
31
+ case gemfile.basename.to_s
32
+ when 'gems.rb' then Pathname.new(gemfile.sub(/.rb$/, '.locked'))
33
+ else Pathname.new("#{gemfile}.lock")
34
+ end
35
+ end
36
+
37
+ def default_bundle_dir
38
+ bundle_dir = find_directory(".carat")
39
+ return nil unless bundle_dir
40
+
41
+ global_bundle_dir = File.join(Carat.rubygems.user_home, ".carat")
42
+ return nil if bundle_dir == global_bundle_dir
43
+
44
+ Pathname.new(bundle_dir)
45
+ end
46
+
47
+ def in_bundle?
48
+ find_gemfile
49
+ end
50
+
51
+ def chdir(dir, &blk)
52
+ Carat.rubygems.ext_lock.synchronize do
53
+ Dir.chdir dir, &blk
54
+ end
55
+ end
56
+
57
+ def pwd
58
+ Carat.rubygems.ext_lock.synchronize do
59
+ Dir.pwd
60
+ end
61
+ end
62
+
63
+ def with_clean_git_env(&block)
64
+ keys = %w[GIT_DIR GIT_WORK_TREE]
65
+ old_env = keys.inject({}) do |h, k|
66
+ h.update(k => ENV[k])
67
+ end
68
+
69
+ keys.each {|key| ENV.delete(key) }
70
+
71
+ block.call
72
+ ensure
73
+ keys.each {|key| ENV[key] = old_env[key] }
74
+ end
75
+
76
+ def set_bundle_environment
77
+ # Set PATH
78
+ paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
79
+ paths.unshift "#{Carat.bundle_path}/bin"
80
+ ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR)
81
+
82
+ # Set RUBYOPT
83
+ rubyopt = [ENV["RUBYOPT"]].compact
84
+ if rubyopt.empty? || rubyopt.first !~ /-rcarat\/setup/
85
+ rubyopt.unshift %|-rcarat/setup|
86
+ ENV["RUBYOPT"] = rubyopt.join(' ')
87
+ end
88
+
89
+ # Set RUBYLIB
90
+ rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
91
+ rubylib.unshift File.expand_path('../..', __FILE__)
92
+ ENV["RUBYLIB"] = rubylib.uniq.join(File::PATH_SEPARATOR)
93
+ end
94
+
95
+ private
96
+
97
+ def find_gemfile
98
+ given = ENV['BUNDLE_GEMFILE']
99
+ return given if given && !given.empty?
100
+
101
+ find_file('Gemfile', 'gems.rb')
102
+ end
103
+
104
+ def find_file(*names)
105
+ search_up(*names) {|filename|
106
+ return filename if File.file?(filename)
107
+ }
108
+ end
109
+
110
+ def find_directory(*names)
111
+ search_up(*names) do |dirname|
112
+ return dirname if File.directory?(dirname)
113
+ end
114
+ end
115
+
116
+ def search_up(*names)
117
+ previous = nil
118
+ current = File.expand_path(SharedHelpers.pwd)
119
+
120
+ until !File.directory?(current) || current == previous
121
+ if ENV['BUNDLE_SPEC_RUN']
122
+ # avoid stepping above the tmp directory when testing
123
+ return nil if File.file?(File.join(current, 'carat.gemspec'))
124
+ end
125
+
126
+ names.each do |name|
127
+ filename = File.join(current, name)
128
+ yield filename
129
+ end
130
+ current, previous = File.expand_path("..", current), current
131
+ end
132
+ end
133
+
134
+ def clean_load_path
135
+ # handle 1.9 where system gems are always on the load path
136
+ if defined?(::Gem)
137
+ me = File.expand_path("../../", __FILE__)
138
+ $LOAD_PATH.reject! do |p|
139
+ next if File.expand_path(p) =~ /^#{Regexp.escape(me)}/
140
+ p != File.dirname(__FILE__) &&
141
+ Carat.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ }
142
+ end
143
+ $LOAD_PATH.uniq!
144
+ end
145
+ end
146
+
147
+ extend self
148
+ end
149
+ end
@@ -0,0 +1,63 @@
1
+ module Carat
2
+ class SimilarityDetector
3
+ SimilarityScore = Struct.new(:string, :distance)
4
+
5
+ # initialize with an array of words to be matched against
6
+ def initialize(corpus)
7
+ @corpus = corpus
8
+ end
9
+
10
+ # return an array of words similar to 'word' from the corpus
11
+ def similar_words(word, limit=3)
12
+ words_by_similarity = @corpus.map{|w| SimilarityScore.new(w, levenshtein_distance(word, w))}
13
+ words_by_similarity.select{|s| s.distance<=limit}.sort_by(&:distance).map(&:string)
14
+ end
15
+
16
+ # return the result of 'similar_words', concatenated into a list
17
+ # (eg "a, b, or c")
18
+ def similar_word_list(word, limit=3)
19
+ words = similar_words(word,limit)
20
+ if words.length==1
21
+ words[0]
22
+ elsif words.length>1
23
+ [words[0..-2].join(', '), words[-1]].join(' or ')
24
+ end
25
+ end
26
+
27
+
28
+ protected
29
+ # http://www.informit.com/articles/article.aspx?p=683059&seqNum=36
30
+ def levenshtein_distance(this, that, ins=2, del=2, sub=1)
31
+ # ins, del, sub are weighted costs
32
+ return nil if this.nil?
33
+ return nil if that.nil?
34
+ dm = [] # distance matrix
35
+
36
+ # Initialize first row values
37
+ dm[0] = (0..this.length).collect { |i| i * ins }
38
+ fill = [0] * (this.length - 1)
39
+
40
+ # Initialize first column values
41
+ for i in 1..that.length
42
+ dm[i] = [i * del, fill.flatten]
43
+ end
44
+
45
+ # populate matrix
46
+ for i in 1..that.length
47
+ for j in 1..this.length
48
+ # critical comparison
49
+ dm[i][j] = [
50
+ dm[i-1][j-1] +
51
+ (this[j-1] == that[i-1] ? 0 : sub),
52
+ dm[i][j-1] + ins,
53
+ dm[i-1][j] + del
54
+ ].min
55
+ end
56
+ end
57
+
58
+ # The last value in matrix is the Levenshtein distance between the strings
59
+ dm[that.length][this.length]
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,46 @@
1
+ module Carat
2
+ class Source
3
+ autoload :Rubygems, 'carat/source/rubygems'
4
+ autoload :Path, 'carat/source/path'
5
+ autoload :Git, 'carat/source/git'
6
+
7
+ def self.mirror_for(uri)
8
+ uri = URI(uri.to_s) unless uri.is_a?(URI)
9
+
10
+ # Settings keys are all downcased
11
+ mirrors = Carat.settings.gem_mirrors
12
+ normalized_key = URI(uri.to_s.downcase)
13
+
14
+ mirrors[normalized_key] || uri
15
+ end
16
+
17
+ attr_accessor :dependency_names
18
+
19
+ def unmet_deps
20
+ specs.unmet_dependency_names
21
+ end
22
+
23
+ def version_message(spec)
24
+ message = "#{spec.name} #{spec.version}"
25
+
26
+ if Carat.locked_gems
27
+ locked_spec = Carat.locked_gems.specs.find { |s| s.name == spec.name }
28
+ locked_spec_version = locked_spec.version if locked_spec
29
+ if locked_spec_version && spec.version != locked_spec_version
30
+ message << " (was #{locked_spec_version})"
31
+ end
32
+ end
33
+
34
+ message
35
+ end
36
+
37
+ def can_lock?(spec)
38
+ spec.source == self
39
+ end
40
+
41
+ def include?(other)
42
+ other == self
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,294 @@
1
+ require 'fileutils'
2
+ require 'uri'
3
+ require 'digest/sha1'
4
+
5
+ module Carat
6
+ class Source
7
+
8
+ class Git < Path
9
+ autoload :GitProxy, 'carat/source/git/git_proxy'
10
+
11
+ attr_reader :uri, :ref, :branch, :options, :submodules
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ @glob = options["glob"] || DEFAULT_GLOB
16
+
17
+ @allow_cached = false
18
+ @allow_remote = false
19
+
20
+ # Stringify options that could be set as symbols
21
+ %w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }
22
+
23
+ @uri = options["uri"] || ''
24
+ @branch = options["branch"]
25
+ @ref = options["ref"] || options["branch"] || options["tag"] || 'master'
26
+ @submodules = options["submodules"]
27
+ @name = options["name"]
28
+ @version = options["version"]
29
+
30
+ @copied = false
31
+ @local = false
32
+ end
33
+
34
+ def self.from_lock(options)
35
+ new(options.merge("uri" => options.delete("remote")))
36
+ end
37
+
38
+ def to_lock
39
+ out = "GIT\n"
40
+ out << " remote: #{@uri}\n"
41
+ out << " revision: #{revision}\n"
42
+ %w(ref branch tag submodules).each do |opt|
43
+ out << " #{opt}: #{options[opt]}\n" if options[opt]
44
+ end
45
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
46
+ out << " specs:\n"
47
+ end
48
+
49
+ def hash
50
+ [self.class, uri, ref, branch, name, version, submodules].hash
51
+ end
52
+
53
+ def eql?(o)
54
+ o.is_a?(Git) &&
55
+ uri == o.uri &&
56
+ ref == o.ref &&
57
+ branch == o.branch &&
58
+ name == o.name &&
59
+ version == o.version &&
60
+ submodules == o.submodules
61
+ end
62
+
63
+ alias == eql?
64
+
65
+ def to_s
66
+ at = if local?
67
+ path
68
+ elsif options["ref"]
69
+ shortref_for_display(options["ref"])
70
+ else
71
+ ref
72
+ end
73
+ "#{uri} (at #{at})"
74
+ end
75
+
76
+ def name
77
+ File.basename(@uri, '.git')
78
+ end
79
+
80
+ # This is the path which is going to contain a specific
81
+ # checkout of the git repository. When using local git
82
+ # repos, this is set to the local repo.
83
+ def install_path
84
+ @install_path ||= begin
85
+ git_scope = "#{base_name}-#{shortref_for_path(revision)}"
86
+ path = Carat.install_path.join(git_scope)
87
+
88
+ if !path.exist? && Carat.requires_sudo?
89
+ Carat.user_bundle_path.join(Carat.ruby_scope).join(git_scope)
90
+ else
91
+ path
92
+ end
93
+ end
94
+ end
95
+
96
+ alias :path :install_path
97
+
98
+ def extension_dir_name
99
+ "#{base_name}-#{shortref_for_path(revision)}"
100
+ end
101
+
102
+ def unlock!
103
+ git_proxy.revision = nil
104
+ @unlocked = true
105
+ end
106
+
107
+ def local_override!(path)
108
+ return false if local?
109
+
110
+ path = Pathname.new(path)
111
+ path = path.expand_path(Carat.root) unless path.relative?
112
+
113
+ unless options["branch"] || Carat.settings[:disable_local_branch_check]
114
+ raise GitError, "Cannot use local override for #{name} at #{path} because " \
115
+ ":branch is not specified in Gemfile. Specify a branch or use " \
116
+ "`carat config --delete` to remove the local override"
117
+ end
118
+
119
+ unless path.exist?
120
+ raise GitError, "Cannot use local override for #{name} because #{path} " \
121
+ "does not exist. Check `carat config --delete` to remove the local override"
122
+ end
123
+
124
+ set_local!(path)
125
+
126
+ # Create a new git proxy without the cached revision
127
+ # so the Gemfile.lock always picks up the new revision.
128
+ @git_proxy = GitProxy.new(path, uri, ref)
129
+
130
+ if git_proxy.branch != options["branch"] && !Carat.settings[:disable_local_branch_check]
131
+ raise GitError, "Local override for #{name} at #{path} is using branch " \
132
+ "#{git_proxy.branch} but Gemfile specifies #{options["branch"]}"
133
+ end
134
+
135
+ changed = cached_revision && cached_revision != git_proxy.revision
136
+
137
+ if changed && !@unlocked && !git_proxy.contains?(cached_revision)
138
+ raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
139
+ "but the current branch in your local override for #{name} does not contain such commit. " \
140
+ "Please make sure your branch is up to date."
141
+ end
142
+
143
+ changed
144
+ end
145
+
146
+ # TODO: actually cache git specs
147
+ def specs(*)
148
+ if has_app_cache? && !local?
149
+ set_local!(app_cache_path)
150
+ end
151
+
152
+ if requires_checkout? && !@copied
153
+ git_proxy.checkout
154
+ git_proxy.copy_to(install_path, submodules)
155
+ serialize_gemspecs_in(install_path)
156
+ @copied = true
157
+ end
158
+
159
+ local_specs
160
+ end
161
+
162
+ def install(spec)
163
+ debug = nil
164
+ if requires_checkout? && !@copied
165
+ debug = " * Checking out revision: #{ref}"
166
+ git_proxy.copy_to(install_path, submodules)
167
+ serialize_gemspecs_in(install_path)
168
+ @copied = true
169
+ end
170
+ generate_bin(spec)
171
+ if requires_checkout? && spec.post_install_message
172
+ Installer.post_install_messages[spec.name] = spec.post_install_message
173
+ end
174
+ ["Using #{version_message(spec)} from #{to_s}", nil, debug]
175
+ end
176
+
177
+ def cache(spec, custom_path = nil)
178
+ app_cache_path = app_cache_path(custom_path)
179
+ return unless Carat.settings[:cache_all]
180
+ return if path == app_cache_path
181
+ cached!
182
+ FileUtils.rm_rf(app_cache_path)
183
+ git_proxy.checkout if requires_checkout?
184
+ git_proxy.copy_to(app_cache_path, @submodules)
185
+ serialize_gemspecs_in(app_cache_path)
186
+ end
187
+
188
+ def load_spec_files
189
+ super
190
+ rescue PathError => e
191
+ Carat.ui.trace e
192
+ raise GitError, "#{to_s} is not yet checked out. Run `carat install` first."
193
+ end
194
+
195
+ # This is the path which is going to contain a cache
196
+ # of the git repository. When using the same git repository
197
+ # across different projects, this cache will be shared.
198
+ # When using local git repos, this is set to the local repo.
199
+ def cache_path
200
+ @cache_path ||= begin
201
+ git_scope = "#{base_name}-#{uri_hash}"
202
+
203
+ if Carat.requires_sudo?
204
+ Carat.user_bundle_path.join("cache/git", git_scope)
205
+ else
206
+ Carat.cache.join("git", git_scope)
207
+ end
208
+ end
209
+ end
210
+
211
+ def app_cache_dirname
212
+ "#{base_name}-#{shortref_for_path(cached_revision || revision)}"
213
+ end
214
+
215
+ def revision
216
+ git_proxy.revision
217
+ end
218
+
219
+ def allow_git_ops?
220
+ @allow_remote || @allow_cached
221
+ end
222
+
223
+ private
224
+
225
+ def serialize_gemspecs_in(destination)
226
+ expanded_path = destination.expand_path(Carat.root)
227
+ Dir["#{expanded_path}/#{@glob}"].each do |spec_path|
228
+ # Evaluate gemspecs and cache the result. Gemspecs
229
+ # in git might require git or other dependencies.
230
+ # The gemspecs we cache should already be evaluated.
231
+ spec = Carat.load_gemspec(spec_path)
232
+ next unless spec
233
+ File.open(spec_path, 'wb') {|file| file.write(spec.to_ruby) }
234
+ end
235
+ end
236
+
237
+ def set_local!(path)
238
+ @local = true
239
+ @local_specs = @git_proxy = nil
240
+ @cache_path = @install_path = path
241
+ end
242
+
243
+ def has_app_cache?
244
+ cached_revision && super
245
+ end
246
+
247
+ def local?
248
+ @local
249
+ end
250
+
251
+ def requires_checkout?
252
+ allow_git_ops? && !local?
253
+ end
254
+
255
+ def base_name
256
+ File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*},''),".git")
257
+ end
258
+
259
+ def shortref_for_display(ref)
260
+ ref[0..6]
261
+ end
262
+
263
+ def shortref_for_path(ref)
264
+ ref[0..11]
265
+ end
266
+
267
+ def uri_hash
268
+ if uri =~ %r{^\w+://(\w+@)?}
269
+ # Downcase the domain component of the URI
270
+ # and strip off a trailing slash, if one is present
271
+ input = URI.parse(uri).normalize.to_s.sub(%r{/$},'')
272
+ else
273
+ # If there is no URI scheme, assume it is an ssh/git URI
274
+ input = uri
275
+ end
276
+ Digest::SHA1.hexdigest(input)
277
+ end
278
+
279
+ def cached_revision
280
+ options["revision"]
281
+ end
282
+
283
+ def cached?
284
+ cache_path.exist?
285
+ end
286
+
287
+ def git_proxy
288
+ @git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision, self)
289
+ end
290
+
291
+ end
292
+
293
+ end
294
+ end